Xcode 6制作動態及靜態Framework和各種坑


Xcode 6制作動態及靜態Framework

http://www.cocoachina.com/ios/20141126/10322.html

有沒有寫SDK或者要將一些常用的工具類做成Framework的經歷? 你或許自己寫腳本完成了這項工作,相信也有很多的人使用 iOS-Universal-Framework ,隨着Xcode 6的發布,相信小伙伴們已經都知道了,Xcode 6支持做Framework了. 同時iOS-Universal-Framework開發者也宣布不在繼續維持此項目的開發,建議開發者使用Xcode 6制作,目前網上也有很多制作iOS Framework的資料,但大多都不夠詳細,接下來本文會詳情介紹一下在Xcode 6下制作iOS Framework.

關於靜態庫和動態庫的概念,網上資料很多,這里不做敘述,只講解制作過程。

創建iOS動態庫

新建工程並選擇默認Target為Cocoa Touch Framework, 如圖:

11.png

做編碼工作,在這里我簡單的寫了一個Utils的類,並寫了一個log方法

12.png

設置開放的頭文件:Framework中有些類可能是一些私有的輔助工具,不需要使用者看到,在這里只需要把開放出去的類放到Public下, 如圖

13.png

這樣生成的Framework的Headers目錄下也只能看到Public的頭文件

14.png

編碼完成之后,直接Run就能成功生成Framework文件了,選擇 xCode->Window->Organizer->Projects->Your Project, 打開工程的Derived Data目錄,這樣就能找到生成的Framework文件了,如圖

15.png

16.png

新建測試工程,使用生成的Framework

將Framework文件導入到測試工程,調用Framework中的代碼

1
2
MyUtils *utils = [MyUtils  new ]; 
[utils log:@ "didFinishLaunchingWithOptions" ];

運行報錯(Reason: Image Not Found)

18.png

為什么會這樣的?因為我們做的是動態庫,在使用的時候需要額外加一個步驟,要把Framework同時添加到‘Embedded Binaries’中

19.png

注意: 在XCode 6之前是沒有這個選項的(我沒發現),所以理論上XCode 5及之前的版本無法使用Xcode 6下生成的Framework動態庫。

到這里,假定你整個過程都是使用的模擬器做的,那看上去會很順利。這時候嘗試將測試工程部署到真機上,問題來了

ld: warning: ignoring file /work/ios/MyFrameworkTest/MyFrameworkTest/MyFramework.framework/MyFramework, file was built for x86_64 which is not the architecture being linked (armv7): /work/ios/MyFrameworkTest/MyFrameworkTest/MyFramework.framework/MyFramework

Undefined symbols for architecture armv7:

  "_OBJC_CLASS_$_MyUtils", referenced from:

      objc-class-ref in AppDelegate.o

ld: symbol(s) not found for architecture armv7

clang: error: linker command failed with exit code 1 (use -v to see invocation)

為什么會這樣?錯誤提示已經很明顯了,因為我們制作動態庫的時候,選的設備是模擬器,如果選真機的話,那生成的庫也只能在真機上使用,那我們該怎樣制作一個通用的動態庫呢? 簡單的方法是分別生成模擬器和真機上運行的庫,然后在合並,這個方法,在每次生成動態庫的時候,過程都會很繁瑣,下面我們用一個腳本來自動完成它。

制作通用動態庫

新建Aggregate Target

20.png

添加script到新建的Target

21.png

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# Sets the target folders and the final framework product.
# 如果工程名稱和Framework的Target名稱不一樣的話,要自定義FMKNAME
# 例如: FMK_NAME = "MyFramework"
FMK_NAME=${PROJECT_NAME}
# Install dir will be the final output to the framework.
# The following line create it in the root folder of the current project.
INSTALL_DIR=${SRCROOT}/Products/${FMK_NAME}.framework
# Working dir will be deleted after the framework creation.
WRK_DIR=build
DEVICE_DIR=${WRK_DIR}/Release-iphoneos/${FMK_NAME}.framework
SIMULATOR_DIR=${WRK_DIR}/Release-iphonesimulator/${FMK_NAME}.framework
# -configuration ${CONFIGURATION}
# Clean and Building both architectures.
xcodebuild -configuration  "Release"  -target  "${FMK_NAME}"  -sdk iphoneos clean build
xcodebuild -configuration  "Release"  -target  "${FMK_NAME}"  -sdk iphonesimulator clean build
# Cleaning the oldest.
if  [ -d  "${INSTALL_DIR}"  ]
then
rm -rf  "${INSTALL_DIR}"
fi
mkdir -p  "${INSTALL_DIR}"
cp -R  "${DEVICE_DIR}/"  "${INSTALL_DIR}/"
# Uses the Lipo Tool to merge both binary files (i386 + armv6/armv7) into one Universal final product.
lipo -create  "${DEVICE_DIR}/${FMK_NAME}"  "${SIMULATOR_DIR}/${FMK_NAME}"  -output  "${INSTALL_DIR}/${FMK_NAME}"
rm -r  "${WRK_DIR}"
open  "${INSTALL_DIR}"

選中新建的Target,Run, 如果沒有異常的話,會自動彈出生成的Framework文件

22.png

這樣生成的動態庫就能同時支持模擬器和真機了。

Xcode 6下制作通用靜態庫

上面我們也提到了,這樣生成的動態庫恐怕很難在Xcode 5上使用,那我們為什么非要用動態庫呢,一般情況下不是用靜態庫就好了嗎? So Easy!只需要修改一個參數即可生成靜態庫了。

23.png

使用靜態庫的話,就可以把Framework從‘Embedded Binaries’中刪除了. 親測在Xcode 5下可用。把新生成的庫導入到測試工程,試試在模擬器和真機上運行,一切OK.

不巧,如果你用的真機是iPhone5 C, 那悲劇又要發成了,生成的Framework竟然不支持armv7s,不知是Xcode 6的bug,還是因為蘋果認為使用armv7s的設備太少,可以不支持了.Xcode 新建工程,默認的Architectures竟然不包含armv7s.

24.png

想要生成的庫支持armv7s,把armv7s添加到Architectures中,重新生成Framework即可

25.png

判斷一個Framework支持哪些架構

我們該怎么驗證生成的Framework支持哪些平台呢,總不能一個個測試吧?當然不用.下面的命令是加上armv7s前后生成的framework的對比

1
2
3
4
Yearsdembp:Products Years$ lipo -info ./MyFramework.framework/MyFramework 
Architectures  in  the fat file: ./MyFramework.framework/MyFramework are: i386 x86_64 armv7 arm64 
Yearsdembp:Products Years$ lipo -info ./MyFramework.framework/MyFramework 
Architectures  in  the fat file: ./MyFramework.framework/MyFramework are: armv7 armv7s i386 x86_64 arm64
 

4、建立一個真機和模擬器通用的framework

首先用finder找到framework所在的位置

然后找到framework中的文件,例如這里的  Kalagame-library,並且紀錄其路徑  os_frame_path

同樣方法打開另一個文件夾,紀錄其中庫的路徑,simulator_frame_path

 

然后打開控制台,輸入 lipo -create os_frame_path  simulator_frame_path  -output  newframe

這樣就完成了模擬器和真機版本framework的合並,用finder找到這個newframe,然后把newframe改名字(例如這里的Kalagame-library),並放回到framework文件夾中,替換原來的文件。

 

例如: 

1. lipo -create /Users/chengdeluo/Library/Developer/Xcode/DerivedData/MyFramework-dkfgbkcpzmnceoenwrpehqyoopof/Build/Products/Debug-iphoneos/MyFramework.framework/MyFramework  /Users/chengdeluo/Library/Developer/Xcode/DerivedData/MyFramework-dkfgbkcpzmnceoenwrpehqyoopof/Build/Products/Debug-iphonesimulator/MyFramework.framework/MyFramework  -output /Users/chengdeluo/Desktop/newFramework

 

2. 接下來用finder找到這個newFramework,然后把newframe改名字為:MyFramework ,並放回到framework文件夾中,替換原來的(MyFramework)文件。

 

使用框架時的各種坑 

1. 如果框架本身要引用到.dylib(動態鏈接庫), (通常會報錯: include of non=modular header inside framework 'Hbb_FileFramework.unzip')那么你需要做如下配置:

應用工程需要配置 allows non-modular includes in framework modules 為YES 

 

2. 如果你的靜態frameowrk/靜態庫(.a)中包含有category文件(如:NSString+StackSymbol.h)

那么你恭喜你要繼續配置,就是這么麻煩, 但是沒辦法

應用工程 project -> target -> build settings -> linker -> other linker flags 

中添加 -force_load $(SOURCE_ROOT)/UseOfHbb_LogDemo/Hbb_Log.framework/Hbb_Log

注意: 這里是加載框架Hbb_Log.framework里的名為Hbb_Log的文件, 你可以看成框架源代碼的打包文件

 

3. 如果框架工程中警告: 

Error: “File was built for archive which is not the architecture being linked (armv7s)”

那么只是叫你把真機和模擬器生成的工程合並起來

 

4. 如果框架工程中出現declaration of must be imported from module before it is required

或者 duplicate file.. 等編譯錯誤的話, 很可能框架工程本身引用到框架本身, 照成定義重復.

我們這種情況常發生在 在一個工程里, 有2個target, 一個target用來生成靜態framework, 另一個則是用來使用這個靜態框架, 這個時候如果是使用不當就會出現上述情況. 

解決方法: 將framework所屬的類全部拖入framework工程文件夾中, 並在.h和.m選中 framework target, 注意千萬不要選擇demo target, 一個都不要

在demo中這樣使用框架

project -> demo target -> general -> linked frameworks and libraries 加入該靜態framework 

 

2. 編譯靜態framework, 模擬器和真機都要

 

3. 在代碼中導入要使用的頭文件如:

#import <HbbLogFramework/Hbb_Logger.h>

然后就可以盡情使用了

 

5. 真機運行前警告: App installation failed

The application does not have a valid signature.

解決辦法: 

處理辦法是: project -> target -> Hbb_LogDemo(框架工程)  -> general -> embed binary 刪除框架引用

6. 所有操作必須使用clean才能使用, 否則將不准確

7. static 的 framework 不能在訪問框架內部的資源文件(也就是bundle文件), 所以如果framework中如果需要使用到資源文件,

那么需要同時創建一個bundle文件, 將資源文件放入其中, 使用時: framework和bundle文件一同拉入工程, 才可以使用.

8. framework的代碼本身含有category文件, 並且demo外部也引用了含有category文件的庫

連坑不斷, 唉, 沒辦法, 誰叫我是coder呢

解決方法:

project -> target -> Hbb_LogDemo(框架工程) -> build settings -> linking -> other linker flags 

添加:

-force_load

$(BUILT_PRODUCTS_DIR)/Hbb_ShareFramework.framework/Hbb_ShareFramework

Hbb_ShareDemo/libWeiboSDK/libWeiboSDK.a

注意: 框架應該排在前面, 順序不對的話, 在真機上同樣沒效果的,切記!!

 $(BUILT_PRODUCTS_DIR)是產品框架生成根目錄

8.2 如果同時有多個框架需要配置-force_load

那么應該這么寫: 

-force_load

Hbb_ShareFramework.framework/Hbb_ShareFramework

ManyFrameworkDemo/libWeiboSDK/libWeiboSDK.a

-force_load

Hbb_LogFramework.framework/Hbb_LogFramework

 

9.Missing sumodule 'Hbb_LocalDataBaseFramework.HP_FMDBOperator'

解決方案: 所有公開的類 (標識為public), 必須在Hbb_LocalDataBaseFramework.h中導入, 這是這個框架的頭文件, 否則就會有這樣的警告

 

10. dyld: Library not loaded:   Reason: image not found

dyld: Library not loaded: @rpath/Hbb_LocationFramework.framework/Hbb_LocationFramework

  Referenced from: /private/var/mobile/Containers/Bundle/Application/44478571-ECDB-4BC7-B440-AECCF28B20C7/Hbb_LocationDemo.app/Hbb_LocationDemo

  Reason: image not found

證明你創建的是動態的framework, 我們要改成靜態的

解決方案: project -> framework target -> build settings ->linking -> Mach-O Type 改為 static

默認是dynamic

 

11. 引用到libxml2.dylib動態庫

框架文件本身要加入libxml2.dylib, 並且還要配置search paths ->header search paths 中加入 /usr/include/libxml2

 

12. 框架的.h文件中聲明多個類interface, 而沒有@implementation

這個時候需要為這多個interface聲明  @implementation, 不能直接寫.h文件里, 而是需要創建.m文件實現這多個@interface

如: Hbb_IMComm.h

/**

 *  登陸信息

 */

 

@interface Hbb_IMLoginParam :TIMLoginParam

@end

那么就需要創建一個Hbb_IMComm.m文件, 並且在其中編寫如下代碼:

@implementation Hbb_IMLoginParam

@end

13.參考自: 真機運行出現 file was built for archive which is not the architecture being linked (armv7s) 報錯

真機file was built for archive which is not the architecture being linked (armv7s)     

原因是在在真機上生成真機自身architecture, 沒有生成別的型號的architecture, 比如我在iphone5s上生成了framework, 放在ipad mini上就運行不了

解決方法:

找到項目的Build Settings- > Build Active Architecture Only,將其從NO 設為 YES

 

14. 聲明為public的頭文件, 不需要全部導入到xxx_framework.h文件中

 

15.

方法一. xib文件必須放在[NSBundle mainBundle](主套裝)里,才能被編譯運行

方法二. 在framework中打包xib target->build phases->copy bundle resources + (把自己的framework加上去, 那么framework里的的資源都會順帶被存儲在main bundle里)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM