一、靜態庫與動態庫
- 在專案中使用 pod 實作模塊化,對于子模塊和第三類別庫的匯入方式存在兩種:靜態庫、動態庫,
- 當在 podfile 中指定 use_frameworks! 時,子模塊和第三方類別庫將被打包成 .framework 動態庫,模塊之間的代碼不能直接參考,需要添加依賴;

- 反之(默認情況)將打包成 .a 靜態庫:

- 動態庫和靜態庫的區別:
-
- 資源加載方式;
-
- 包的大小;
-
- 編譯速度,
二、資源加載方式
- s.dependency 'xx’:
-
- 靜態方式中各模塊的 podspec 檔案不用設定依賴,就可以直接 #import 其他模塊的類頭檔案:

-
- 而動態方式則會報錯:

- s.resources:
s.resources = ['Classes/**/*.{xib,storyboard,Bundle,png,gif,jpg,jpeg,txt}', 'Resource/**/*']
- 圖片等資源是都放入 mainbundle,直接用 imageNamed: 訪問,不用增加很多獲取 bundle 的代碼:
s.resource = 'xx/xxx.bundle'
s.resource_bundles = { 'xxx' => ['/Classes/**/*.{xib,storyboard,Bundle,png,gif,jpg,jpeg,txt}', 'Resource/**/*'] }
- 這兩種寫法,資源都在模塊自己的 bundle 里面,檔案名為 xxx.bundle,工程中需要通過 bundleForClass 等獲取資源路徑,
三、包的大小
- 使用 use_frameworks! 的動態包:

- 默認(或使用 use_modular_headers!)的靜態包:

- 多次驗證,結果都是動態的更小,
- 需要注意,不同的證書 ipa 的大小不同:
動態 pod:adhoc 58.9M,pub 130.6M
靜態 pod:adhoc 58.3M,pub 131.6M
- 差別這么大的原因是:工程使用 swift 的代碼:

四、編譯速度
- 動態庫 Pod 方式:
Total pre-main time: 819.46 milliseconds (100.0%)
dylib loading time: 542.43 milliseconds (66.1%)
rebase/binding time: 35.86 milliseconds (4.3%)
ObjC setup time: 57.79 milliseconds (7.0%)
initializer time: 183.27 milliseconds (22.3%)
slowest intializers :
libSystem.B.dylib : 10.33 milliseconds (1.2%)
libMainThreadChecker.dylib : 28.76 milliseconds (3.5%)
AFNetworking : 83.46 milliseconds (10.1%)
Realm : 27.37 milliseconds (3.3%)
CYKJBasicModule : 17.54 milliseconds (2.1%)
- 靜態庫 Pod 方式:
Total pre-main time: 591.00 milliseconds (100.0%)
dylib loading time: 447.06 milliseconds (75.6%)
rebase/binding time: 30.51 milliseconds (5.1%)
ObjC setup time: 20.26 milliseconds (3.4%)
initializer time: 93.04 milliseconds (15.7%)
slowest intializers :
libSystem.B.dylib : 7.67 milliseconds (1.2%)
libMainThreadChecker.dylib : 25.41 milliseconds (4.3%)
Realm : 21.13 milliseconds (3.5%)
CYKJMain : 57.87 milliseconds (9.7%)
五、工程實體
- 在專案開發中的場景是一個第三方類別庫 bongSDK.framework 引入了 Realm.framework 和 RealmSwift.framework,RealmSwift.framework 是通過 swift 語言寫的,它的內部呼叫 Realm,
- 最初靜態方式的 pod 遇到了難以理解的報錯,因為知識的欠缺和時間的緊迫,放棄了靜態這條路,使用 use_frameworks! 動態 pod 的方式,
- 動態方式在 pod install 階段沒有報錯,但子模塊需要添加依賴,更困難的是圖片、xib、storyboard 等資源需要獲取到模塊的 bundle 才能加載,導致工程大面積的圖片加載錯誤,頁面跳轉崩潰,因此不得不增加很多獲取 bundle 路徑的代碼,修改的位置幾百上千處,
+ (NSBundle *)bundleWithClassName:(Class)cls moduleName:(NSString*)module {
if (module == nil) {
return [NSBundle mainBundle];
}
NSBundle * bundle = [NSBundle bundleForClass:cls];
NSURL * bundleURL = [bundle URLForResource:module withExtension:@"bundle"];
if (bundleURL == nil) {
__block UINavigationController* nav;
[[UIApplication sharedApplication].windows enumerateObjectsUsingBlock:^(__kindof UIWindow * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
UIViewController * windowVC = obj.rootViewController;
if ([windowVC isKindOfClass:[UINavigationController class]]) {
nav = (UINavigationController *)windowVC;
*stop = YES;
}
}];
if (nav != nil) {
Class callerCls = [nav.viewControllers.firstObject class];
bundle = [NSBundle bundleForClass:callerCls];
bundleURL = [bundle URLForResource:module withExtension:@"bundle"];
}
if (bundleURL == nil) {
return [NSBundle mainBundle];
}
}
return [NSBundle bundleWithURL:bundleURL];
}
- 圖片加載則更加困難,因為很多圖片是在 xib 中寫的,通過斷點發現,系統并沒有呼叫 imageNamed: 方法,導致使用 runtime 替換方法實作圖片位置修改的方式失敗,通過查找資料,發現 xib 中的 UIButton、UIImageView 會呼叫 - initWithCoder: 方法,底層會呼叫 UINibDecoder 類的 decodeObjectForKey,
- runtime 替換 decodeObjectForKey 方法后,列印輸出發現,UIButton、UIImageView 控制元件加載的圖片名稱在 UIResourceName 欄位,由此就有了如下的處理方式:
+ (void)load {
_imageViewImageArray = [NSMutableArray arrayWithCapacity:2];
propKey = [CYKJXUtil stringByReversed:@"emaNecruoseRIU"];
btnKey = [CYKJXUtil stringByReversed:@"tnetnoClufetatSnottuBIU"];
// hook UINibDecoder - decodeObjectForKey:
NSString* clsName = [NSString stringWithFormat:@"redoce%@biNIU", @"D"];
clsName = [CYKJXUtil stringByReversed:clsName];
[HookTool exchangeInstanceMethod:NSClassFromString(clsName)
originalSEL:@selector(decodeObjectForKey:)
swizzledSEL:@selector(swizzle_decodeObjectForKey:)];
// hook UIImageView - initWithCoder:
[HookTool exchangeInstanceMethod:UIImageView.class
originalSEL:@selector(initWithCoder:)
swizzledSEL:@selector(swizzle_imageView_initWithCoder:)];
// hook UIButton - initWithCoder:
[HookTool exchangeInstanceMethod:UIButton.class
originalSEL:@selector(initWithCoder:)
swizzledSEL:@selector(swizzle_button_initWithCoder:)];
}
- (id)swizzle_decodeObjectForKey:(NSString *)key {
Method originalMethod = class_getInstanceMethod([HookTool class], @selector(swizzle_decodeObjectForKey:));
IMP function = method_getImplementation(originalMethod);
id (*functionPoint)(id, SEL, id) = (id (*)(id, SEL, id)) function;
id value = functionPoint(self, _cmd, key);
// 保存圖片名稱
if ([key isEqualToString:propKey]) {
[_imageViewImageArray addObject:value];
}
// 保存 button 狀態資料
if ([key isEqualToString:btnKey]) {
if ([value isKindOfClass:[NSDictionary class]]) {
_buttonImageDictionary = value;
}
}
return value;
}
#pragma mark - UIImageView
- (id)swizzle_imageView_initWithCoder:(NSCoder *)aDecoder {
// 執行順序:initWithCoder -》DecoderWithKey -》setImage:,所以每次給 imageView 設定圖片時,需要將之前的置空,
// tabbarItem 的圖片設定不會執行 initWithCoder,如果不置空,會導致 imageView 設定成和 tabbarItem 一樣的圖片,
[_imageViewImageArray removeAllObjects];
UIImageView * instance = (UIImageView *)[self swizzle_imageView_initWithCoder:aDecoder];
// 設定 image
if (_imageViewImageArray.count > 0) {
UIImage * normalImage = [HookTool imageAfterSearch:_imageViewImageArray[0]];
if (normalImage) {
instance.image = normalImage;
}
}
// 設定 highlightedImage
if (_imageViewImageArray.count > 1) {
UIImage * highlightedImage = [HookTool imageAfterSearch:_imageViewImageArray[1]];
if (highlightedImage) {
instance.highlightedImage = highlightedImage;
}
}
return instance;
}
#pragma mark - UIButton
- (id)swizzle_button_initWithCoder:(NSCoder *)aDecoder {
// 執行順序:initWithCoder -》DecoderWithKey -》setImage:,所以每次給 button 設定圖片時,需要將之前的置空,
// tabbarItem 的圖片設定不會執行 initWithCoder,如果不置空,會導致 button 設定成和 tabbarItem 一樣的圖片,
[_imageViewImageArray removeAllObjects];
_buttonImageDictionary = nil;
UIButton * instance = (UIButton *)[self swizzle_button_initWithCoder:aDecoder];
@autoreleasepool {
[_buttonImageDictionary enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key,
id _Nonnull obj,
BOOL * _Nonnull stop) {
if (_imageViewImageArray.count == 0) {
*stop = YES;
}
else {
switch ([key integerValue]) {
case ButtonImageOrder_Normal:
[HookTool setImageForButton:instance object:obj state:UIControlStateNormal];
break;
case ButtonImageOrder_Highlighted:
[HookTool setImageForButton:instance object:obj state:UIControlStateHighlighted];
break;
case ButtonImageOrder_Selected:
[HookTool setImageForButton:instance object:obj state:UIControlStateSelected];
break;
case ButtonImageOrder_Disabled:
[HookTool setImageForButton:instance object:obj state:UIControlStateDisabled];
break;
}
}
}];
}
return instance;
}
- 如上可見,這種動態方式對于編碼并不友好,資源必須要特定的 bundle,一旦資源路徑出錯,輕則圖片未加載,重則程式崩潰,因此,需要研究下如果使用靜態方式 pod 子模塊代碼,
- 首先將 use_frameworks! 洗掉,重新執行 pod install,等 Pod installation complete! 之后,運行工程,報錯,一個一個的解決:
-
- dyld: Library not loaded: @rpath/Realm.framework/Realm,現在不用 pod 匯入 realm,而是將 realm.framework 拖入 basicModule 工程,官方最新的 realm.framework,它分為靜態版和動態版,添加到工程的 Embedded Binaries,編譯時報錯 Unknown type name namespace,不管通過修改 .h 為 .hpp,還是修改 build settings -> Compile Sources As -> Objectoive-C++ 都沒有效果,無計可施之時,可以將 use_frameworks! 時,cocoapods 生成的 Realm.framework 拷貝一份,拖入工程,編譯運行,這個問題得到解決,
-
- Library not loaded: @rpath/libswiftCore.dylib:Build Settings 中 Aways Embed Swift Standard Libraries 修改成 YES,
-
- Argument list too long: recursive header expansion failed:Search Paths -> Header Search Paths,去掉 $(PODS_ROOT)/**,去掉不必要的 recursive search,
- 其余的就是解決一些資源加載問題,資源重名問題,動態庫的參考問題 #import “” 改為 #import <>:

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/325626.html
標籤:其他
