iOS高級強(qiáng)化--010:動態(tài)庫與靜態(tài)庫實戰(zhàn)

弱引用動態(tài)庫

弱引用動態(tài)庫:標(biāo)記-weak_framework參數(shù)供炎,允許在運(yùn)行時不鏈接該動態(tài)庫

正常情況下辨图,運(yùn)行時找不到動態(tài)庫所在位置,程序崩潰并提示image not found

搭建LGApp項目,將動態(tài)庫SYTimer.framework拷貝到項目根目錄

打開LGApp項目崩瓤,創(chuàng)建xcconfig文件,并配置到Tatget上踩官,寫入以下代碼:

HEADER_SEARCH_PATHS = $(inherited) ${SRCROOT}/SYTimer.framework/Headers
FRAMEWORK_SEARCH_PATHS = $(inherited) ${SRCROOT}
OTHER_LDFLAGS = $(inherited) -framework "SYTimer"
  • 指定頭文件路徑Header Search Paths
  • 指定Framework所在目錄Framework Search Paths
  • 指定鏈接Framework的名稱Other Linker Flags -framework SYTimer

配置鏈接Framework三要素却桶,編譯可以成功。但運(yùn)行時蔗牡,程序崩潰

dyld: Library not loaded: @rpath/SYTimer.framework/SYTimer
 Referenced from: /Users/zang/Library/Developer/CoreSimulator/Devices/BC871859-6A76-4967-A245-287615D883E6/data/Containers/Bundle/Application/BF848C5E-4E7E-440C-B266-6FEAD05BB6B2/LGApp.app/LGApp
 Reason: image not found

使用-weak-l<library name>-weak_framework <framework name>指定動態(tài)庫為weak imports颖系。如果在運(yùn)行時找不到該庫,會自動將該庫的地址及內(nèi)容返回NULL

使用man ld查看ld命令辩越,找到-weak_framework參數(shù)

  • -weak_framework:標(biāo)記為弱引用動態(tài)庫嘁扼。運(yùn)行時找不到地址不會報錯,而是返回一個NULL

打開xcconfig文件区匣,修改OTHER_LDFLAGS配置項:

OTHER_LDFLAGS = $(inherited) -Xlinker -weak_framework -Xlinker "SYTimer"

項目運(yùn)行成功偷拔,輸出內(nèi)容如下:

2021-03-08 17:46:30.286532+0800 LGApp[3397:9187180] (null)

使用-weak_framework標(biāo)記為弱引用動態(tài)庫蒋院,Mach-O中記錄的Load Command名稱不再是LC_LOAD_DYLIB,變?yōu)?code>LC_LOAD_WEAK_DYLIB

使用otool -l test | grep 'DYLIB' -A 2命令莲绰,查看Mach-O中動態(tài)庫的路徑

         cmd LC_LOAD_WEAK_DYLIB
     cmdsize 64
        name @rpath/SYTimer.framework/SYTimer (offset 24)
--
         cmd LC_LOAD_DYLIB
     cmdsize 88
        name /System/Library/Frameworks/Foundation.framework/Foundation (offset 24)
靜態(tài)庫代碼沖突

搭建LGApp項目欺旧,將靜態(tài)庫AFNetworking代碼,在項目根目錄下拷貝兩份蛤签。將靜態(tài)庫目錄辞友,分別命名為AFNetworkingAFNetworking2

打開AFNetworking2目錄,將libAFNetworking.a文件震肮,重命名為libAFNetworking2.a

打開LGApp項目称龙,創(chuàng)建xcconfig文件,并配置到Tatget上戳晌,寫入以下代碼:

HEADER_SEARCH_PATHS = $(inherited) "${SRCROOT}/AFNetworking" "${SRCROOT}/AFNetworking2"
LIBRARY_SEARCH_PATHS = $(inherited) "${SRCROOT}/AFNetworking" "${SRCROOT}/AFNetworking2"
OTHER_LDFLAGS = $(inherited) -l"AFNetworking" -l"AFNetworking2"

此時編譯可以通過鲫尊,因為鏈接靜態(tài)庫默認(rèn)使用-noall_load參數(shù)。當(dāng)鏈接靜態(tài)庫AFNetworking找到代碼后沦偎,就不會鏈接靜態(tài)庫AFNetworking2

但真實項目中疫向,我們使用Pods導(dǎo)入的三方庫,大部分使用-all_load-ObjC參數(shù)豪嚎,此時問題出現(xiàn)了

打開xcconfig文件搔驼,修改OTHER_LDFLAGS配置項:

OTHER_LDFLAGS = $(inherited) -ObjC -l"AFNetworking" -l"AFNetworking2"

編譯報錯,出現(xiàn)大量的重復(fù)符號

ld: 223 duplicate symbols for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

嘗試解決:

打開xcconfig文件侈询,修改OTHER_LDFLAGS配置項:

OTHER_LDFLAGS = $(inherited) -ObjC -l"AFNetworking" -l"AFNetworking2" -Xlinker -force_load -Xlinker "${SRCROOT}/AFNetworking/libAFNetworking.a" -Xlinker -load_hidden -Xlinker "${SRCROOT}/AFNetworking2/libAFNetworking2.a"
  • 使用-force_load參數(shù)舌涨,強(qiáng)制鏈接的靜態(tài)庫AFNetworking
  • 使用-load_hidden參數(shù),將靜態(tài)庫AFNetworking2的所有符號設(shè)置為隱藏

編譯報錯扔字,依然提示重復(fù)符號

ld: 223 duplicate symbols for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

結(jié)論:如果導(dǎo)入相同靜態(tài)庫囊嘉,又指定了-all_load-ObjC參數(shù),建議對靜態(tài)庫重新打包啦租,刪除重復(fù)的Symbol

動態(tài)庫鏈接動態(tài)庫

搭建LGFramework項目

LGFramework是一個動態(tài)庫項目哗伯,項目中使用Pods導(dǎo)入動態(tài)庫AFNetworking

使用Pods向一個Framework中導(dǎo)入三方庫荒揣,Pods只會配置鏈接參數(shù)篷角,不會將三方庫拷貝到指定位置,也不會生成.sh腳本

打開LGOneObject.h文件系任,寫入以下代碼:

#import <Foundation/Foundation.h>

@interface LGOneObject : NSObject

- (void)testOneObject;

@end

打開LGOneObject.m文件恳蹲,寫入以下代碼:

#ifndef AFN_HEADER_PATH
 #define AFN_HEADER_PATH <AFNetworking/AFNetworking.h>
#endif

#ifndef AFN
 #define AFN __has_include(AFN_HEADER_PATH)
#endif

#if AFN
#import AFN_HEADER_PATH
#endif

#import "LGOneObject.h"

@implementation LGOneObject

- (void)testOneObject {
#if AFN
   AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager manager];
   NSLog(@"LGFramework-----LGOneObject-----AFN: %@", manager);
#else
   NSLog(@"LGFramework-----LGOneObject-----NO AFN");
#endif
}

@end
  • AFN_HEADER_PATH:表示<AFNetworking/AFNetworking.h>頭文件
  • AFN:表示是否存在頭文件
  • 導(dǎo)入頭文件:判斷AFN,如果存在俩滥,執(zhí)行#import AFN_HEADER_PATH代碼
  • testOneObject方法:判斷AFN嘉蕾,如果存在,初始化AFNetworkReachabilityManager實例對象霜旧,并打印manager對象错忱。否則打印NO AFN

打開LGFramework.h文件,寫入以下代碼:

#import <LGFramework/LGOneObject.h>

搭建LGApp項目

LGApp是一個App項目

創(chuàng)建MulitProject.xcworkspace,加入LGFramework動態(tài)庫和Pods項目以清。LGApp鏈接LGFramework動態(tài)庫

  • 項目結(jié)構(gòu):LGApp(App) -> LGFramework(動態(tài)庫A) -> AFNetworking(動態(tài)庫B)

LGApp項目中儿普,打開ViewController文件,寫入以下代碼:

#import "ViewController.h"
#import <LGFramework/LGFramework.h>

@implementation ViewController

- (void)viewDidLoad {
   [super viewDidLoad];
   LGOneObject *object = [LGOneObject new];
   [object testOneObject];
}

@end

運(yùn)行項目后掷倔,程序直接崩潰

dyld: Library not loaded: @rpath/AFNetworking.framework/AFNetworking
 Referenced from: /Users/zang/Library/Developer/Xcode/DerivedData/MulitProject-dfauhknlvaapxgbchtdxxhwuixsn/Build/Products/Debug-iphonesimulator/LGFramework.framework/LGFramework
 Reason: image not found

LGFramework項目中眉孩,打開Pods-LGFramework.debug.xcconfig文件,查看LD_RUNPATH_SEARCH_PATHS配置項

LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' '@executable_path/../../Frameworks'

來到項目編譯后的目錄勒葱,AFNetworking.framework存放在AFNetworking目錄下浪汪,而AFNetworkingLGApp.app平級。所以按照LD_RUNPATH_SEARCH_PATHS配置項凛虽,無法找到AFNetworking.framework

介紹以下三種解決辦法:

【方式一】

修改LGFramework提供的@rpath路徑(僅適用于模擬器

LGFramework項目中死遭,創(chuàng)建xcconfig文件,并配置到Tatget上凯旋,寫入以下代碼:

#include "Pods/Target Support Files/Pods-LGFramework/Pods-LGFramework.debug.xcconfig"
LD_RUNPATH_SEARCH_PATHS = $(inherited) ${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking
  • 導(dǎo)入Pods生成的xcconfig
  • 按照AFNetworking.framework所在路徑殃姓,修改@rpath路徑

選擇模擬器,運(yùn)行項目瓦阐,輸出內(nèi)容如下:

2021-03-09 16:05:33.972325+0800 LGApp[10862:9536454] LGFramework-----LGOneObject-----AFN: <AFNetworkReachabilityManager: 0x600000856920>

選擇真機(jī)蜗侈,運(yùn)行項目,程序崩潰:

dyld: Library not loaded: @rpath/AFNetworking.framework/AFNetworking
 Referenced from: /private/var/containers/Bundle/Application/BCCBC8EC-0333-4C46-B2E1-E024C4030916/LGApp.app/Frameworks/LGFramework.framework/LGFramework
 Reason: image not found
  • 在設(shè)備上運(yùn)行App睡蟋,需要將鏈接的動態(tài)庫拷貝到App包內(nèi)
【方式二】

使用PodsLGApp項目中導(dǎo)入動態(tài)庫AFNetworking

Pods項目中踏幻,打開Podfile文件,寫入以下代碼:

platform :ios, '9.0'

workspace '../MulitProject.xcworkspace'

target 'LGFramework' do
 use_frameworks!
 pod 'AFNetworking'
end

target 'LGApp' do
 project '../LGApp/LGApp.xcodeproj'
 use_frameworks!
 pod 'AFNetworking'
end

選擇真機(jī)戳杀,運(yùn)行項目该面,輸出內(nèi)容如下:

2021-03-09 16:18:57.810101+0800 LGApp[1471:289515] LGFramework-----LGOneObject-----AFN: <AFNetworkReachabilityManager: 0x280d231c0>

使用PodsApp中導(dǎo)入三方庫,會生成一個.sh腳本信卡。它的作用就是將.framework拷貝到App中的Frameworks目錄

【方式三】

通過腳本隔缀,將AFNetworking.framework拷貝到App中的Frameworks目錄

方式二中,Pods生成的Pods-LGApp-frameworks.sh腳本傍菇,拷貝到LGApp項目的根目錄

Pods項目中猾瘸,打開Podfile文件,恢復(fù)初始代碼:

platform :ios, '9.0'

target 'LGFramework' do
 use_frameworks!
 pod 'AFNetworking'
end

LGApp項目中丢习,創(chuàng)建xcconfig文件牵触,并配置到Tatget上,寫入以下代碼:

SCRIPT_DIR = ${SRCROOT}/Pods-LGApp-frameworks.sh
  • 定義SCRIPT_DIR變量咐低,存儲.sh腳本路徑

LGApp項目中揽思,選擇Build Phases,在Run Script中寫入腳本:"${SCRIPT_DIR}"

選擇真機(jī)见擦,運(yùn)行項目钉汗,輸出內(nèi)容如下:

2021-03-09 17:51:16.113484+0800 LGApp[1556:311935] LGFramework-----LGOneObject-----AFN: <AFNetworkReachabilityManager: 0x2831fa2c0>

此時LGApp項目不再使用Pods導(dǎo)入AFNetworking動態(tài)庫羹令,而是通過腳本,將AFNetworking.framework拷貝到App中的Frameworks目錄

App想使用動態(tài)庫B的方法

基于上述案例:LGApp(App) -> LGFramework(動態(tài)庫A) -> AFNetworking(動態(tài)庫B)

如果App想使用動態(tài)庫B的方法损痰,也就是讓LGApp直接調(diào)用AFNetworking的方法

LGApp項目中特恬,打開ViewController.m文件,寫入以下代碼:

#import "ViewController.h"
#import <LGFramework/LGFramework.h>
#import <AFNetworking/AFNetworking.h>

@implementation ViewController

- (void)viewDidLoad {
   [super viewDidLoad];
   LGOneObject *object = [LGOneObject new];
   [object testOneObject];
   
   AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager manager];
   NSLog(@"App使用動態(tài)庫B的方法:%@", manager);
}

@end

編譯失敗徐钠,提示錯誤信息:找不到頭文件

'AFNetworking/AFNetworking.h' file not found

介紹以下兩種解決辦法:

【方式一】

使用PodsLGApp項目中導(dǎo)入動態(tài)庫AFNetworking

Pods項目中癌刽,打開Podfile文件,寫入以下代碼:

platform :ios, '9.0'

workspace '../MulitProject.xcworkspace'

target 'LGFramework' do
 use_frameworks!
 pod 'AFNetworking'
end

target 'LGApp' do
 project '../LGApp/LGApp.xcodeproj'
 use_frameworks!
 pod 'AFNetworking'
end

LGApp項目中尝丐,打開xcconfig文件显拜,寫入以下代碼:

#include "Pods/Target Support Files/Pods-LGFramework/Pods-LGFramework.debug.xcconfig"
  • 導(dǎo)入Pods生成的xcconfig

選擇真機(jī),運(yùn)行項目爹袁,輸出內(nèi)容如下:

2021-03-09 18:32:15.142101+0800 LGApp[1583:320382] LGFramework-----LGOneObject-----AFN: <AFNetworkReachabilityManager: 0x283afa7a0>
2021-03-09 18:32:15.142263+0800 LGApp[1583:320382] App使用動態(tài)庫B的方法:<AFNetworkReachabilityManager: 0x283afac60>
【方式二】

標(biāo)記-reexport_framework-reexport_l參數(shù)远荠,重新將AFNetworking通過動態(tài)庫LGFramework導(dǎo)出給LGApp

Pods項目中,打開Podfile文件失息,恢復(fù)初始代碼:

platform :ios, '9.0'

target 'LGFramework' do
 use_frameworks!
 pod 'AFNetworking'
end

LGFramework項目中譬淳,打開xcconfig文件,寫入以下代碼:

#include "Pods/Target Support Files/Pods-LGFramework/Pods-LGFramework.debug.xcconfig"
OTHER_LDFLAGS = -Xlinker -reexport_framework -Xlinker AFNetworking $(inherited)
  • 因為Cocoapods生成的xcconfig文件包含了-framework AFNetworking參數(shù)盹兢,想要將AFNetworking指定為-reexport_framework邻梆,需將其放在$(inherited)前面

LGApp項目中,打開xcconfig文件绎秒,寫入以下代碼:

SCRIPT_DIR = ${SRCROOT}/Pods-LGApp-frameworks.sh
HEADER_SEARCH_PATHS = $(inherited) "${BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/AFNetworking/AFNetworking.framework/Headers"
FRAMEWORK_SEARCH_PATHS = $(inherited) "${BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/AFNetworking"

選擇真機(jī)浦妄,運(yùn)行項目,輸出內(nèi)容如下:

2021-03-09 18:32:15.142101+0800 LGApp[1583:320382] LGFramework-----LGOneObject-----AFN: <AFNetworkReachabilityManager: 0x283afa7a0>
2021-03-09 18:32:15.142263+0800 LGApp[1583:320382] App使用動態(tài)庫B的方法:<AFNetworkReachabilityManager: 0x283afac60>
動態(tài)庫的反向依賴

動態(tài)庫的反向依賴见芹,由于符合的作用空間剂娄,在運(yùn)行時,動態(tài)庫可以動態(tài)找到App的符號玄呛。所以只要在編譯期間阅懦,繞過符號未定義的錯誤即可

LGApp項目中,打開LGAppObject.h文件徘铝,寫入以下代碼:

#import <Foundation/Foundation.h>

@interface LGAppObject : NSObject

- (void)test_app;

@end

LGApp項目中耳胎,打開LGAppObject.m文件,寫入以下代碼:

#import "LGAppObject.h"

@implementation LGAppObject

- (void)test_app {
   NSLog(@"test_app");
}

@end

LGFramework項目中庭砍,打開LGOneObject文件场晶,寫入以下代碼:

#ifndef AFN_HEADER_PATH
 #define AFN_HEADER_PATH <AFNetworking/AFNetworking.h>
#endif

#ifndef AFN
 #define AFN __has_include(AFN_HEADER_PATH)
#endif

#if AFN
#import AFN_HEADER_PATH
#endif
#import "LGOneObject.h"
#import "LGAppObject.h"

@implementation LGOneObject

- (void)testOneObject {
   
   [[LGAppObject new] test_app];
   
#if AFN
   AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager manager];
   NSLog(@"LGFramework-----LGOneObject-----AFN: %@", manager);
#else
   NSLog(@"LGFramework-----LGOneObject-----NO AFN");
#endif
}

@end
  • 導(dǎo)入LGApp項目中的LGAppObject.h頭文件
  • 調(diào)用了LGAppObjecttest_app方法

LGFramework項目中,打開xcconfig文件怠缸,寫入以下代碼:

#include "Pods/Target Support Files/Pods-LGFramework/Pods-LGFramework.debug.xcconfig"
HEADER_SEARCH_PATHS = $(inherited) "${SRCROOT}/../LGApp/LGApp"
  • 導(dǎo)入LGApp項目中的頭文件

編譯報錯,提示錯誤信息:_OBJC_CLASS_$_LGAppObject符號未定義

Undefined symbols for architecture arm64:
 "_OBJC_CLASS_$_LGAppObject", referenced from:
     objc-class-ref in LGOneObject.o

介紹以下兩種解決辦法:

【方式一】

使用-U參數(shù)钳宪,標(biāo)記某個符號為動態(tài)查找符號

LGFramework項目中揭北,打開xcconfig文件扳炬,寫入以下代碼:

#include "Pods/Target Support Files/Pods-LGFramework/Pods-LGFramework.debug.xcconfig"
HEADER_SEARCH_PATHS = $(inherited) "${SRCROOT}/../LGApp/LGApp"
OTHER_LDFLAGS = -Xlinker -U -Xlinker _OBJC_CLASS_$_LGAppObject
  • 使用-U參數(shù),標(biāo)記符號_OBJC_CLASS_$_LGAppObject為動態(tài)查找符號

選擇真機(jī)搔体,運(yùn)行項目恨樟,輸出內(nèi)容如下:

2021-03-09 19:29:53.986715+0800 LGApp[1611:332497] test_app
2021-03-09 19:29:53.987340+0800 LGApp[1611:332497] LGFramework-----LGOneObject-----AFN: <AFNetworkReachabilityManager: 0x282785a60>
【方式二】

使用-undefined參數(shù),將動態(tài)庫中的符號全部標(biāo)記為動態(tài)查找符號疚俱。配置后動態(tài)庫中可以使用任意符號劝术,這樣風(fēng)險較高,不建議使用

LGFramework項目中呆奕,打開xcconfig文件养晋,寫入以下代碼:

#include "Pods/Target Support Files/Pods-LGFramework/Pods-LGFramework.debug.xcconfig"
HEADER_SEARCH_PATHS = $(inherited) "${SRCROOT}/../LGApp/LGApp"
OTHER_LDFLAGS = -Xlinker -undefined -Xlinker dynamic_lookup
  • 使用-undefined參數(shù),將整個LGFramework動態(tài)庫中的符號梁钾,都標(biāo)記為動態(tài)查找符號

選擇真機(jī)绳泉,運(yùn)行項目,輸出內(nèi)容如下:

2021-03-10 09:34:26.692354+0800 LGApp[2143:427804] test_app
2021-03-10 09:34:26.693179+0800 LGApp[2143:427804] LGFramework-----LGOneObject-----AFN: <AFNetworkReachabilityManager: 0x2828047a0>
動態(tài)庫鏈接靜態(tài)庫

搭建LGFramework項目

LGFramework是一個動態(tài)庫項目姆泻,項目中使用Pods導(dǎo)入靜態(tài)庫AFNetworking

打開LGOneObject.h文件零酪,寫入以下代碼:

#import <Foundation/Foundation.h>

@interface LGOneObject : NSObject

- (void)testOneObject;

@end

打開LGOneObject.m文件,寫入以下代碼:

#ifndef AFN_HEADER_PATH
 #define AFN_HEADER_PATH <AFNetworking/AFNetworking.h>
#endif

#ifndef AFN
 #define AFN __has_include(AFN_HEADER_PATH)
#endif

#if AFN
#import AFN_HEADER_PATH
#endif

#import "LGOneObject.h"

@implementation LGOneObject

- (void)testOneObject {
#if AFN
   AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager manager];
   NSLog(@"LGFramework-----LGOneObject-----AFN: %@", manager);
#else
   NSLog(@"LGFramework-----LGOneObject-----NO AFN");
#endif
}

@end

打開LGFramework.h文件拇勃,寫入以下代碼:

#import <LGFramework/LGOneObject.h>

搭建LGApp項目

LGApp是一個App項目

創(chuàng)建MulitProject.xcworkspace四苇,加入LGFramework動態(tài)庫和Pods項目。LGApp鏈接LGFramework動態(tài)庫

  • 項目結(jié)構(gòu):LGApp(App) -> LGFramework(動態(tài)庫A) -> AFNetworking(靜態(tài)庫A)

LGApp項目中方咆,打開ViewController文件蛔琅,寫入以下代碼:

#import "ViewController.h"
#import <LGFramework/LGFramework.h>

@implementation ViewController

- (void)viewDidLoad {
  [super viewDidLoad];
  LGOneObject *object = [LGOneObject new];
  [object testOneObject];
}

@end

選擇真機(jī),運(yùn)行項目峻呛,輸出內(nèi)容如下:

2021-03-10 10:16:12.781430+0800 LGApp[2185:436467] LGFramework-----LGOneObject-----AFN: <AFNetworkReachabilityManager: 0x280dc7a60>
  • 動態(tài)庫LGFramework生成的過程中罗售,鏈接靜態(tài)庫AFNetworking時,會把靜態(tài)庫AFNetworking所有代碼都鏈接進(jìn)去钩述。所以編譯鏈接都不會報錯

App想使用靜態(tài)庫AFNetworking的方法

LGApp項目中寨躁,創(chuàng)建xcconfig文件,并配置到Tatget上牙勘,寫入以下代碼:

HEADER_SEARCH_PATHS = "${SRCROOT}/../LGFramework/Pods/Headers/Public/AFNetworking" $(inherited) 
  • 指定AFNetworking頭文件路徑

LGApp項目中职恳,打開ViewController.m文件,寫入以下代碼:

#import "ViewController.h"
#import <LGFramework/LGFramework.h>
#import <AFNetworking.h>

@implementation ViewController

- (void)viewDidLoad {
   [super viewDidLoad];
   LGOneObject *object = [LGOneObject new];
   [object testOneObject];
   
   AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager manager];
   NSLog(@"App使用靜態(tài)庫AFNetworking的方法:%@", manager);
}

@end

選擇真機(jī)方面,運(yùn)行項目放钦,輸出內(nèi)容如下:

2021-03-10 10:20:52.143445+0800 LGApp[2189:437620] LGFramework-----LGOneObject-----AFN: <AFNetworkReachabilityManager: 0x281490760>
2021-03-10 10:20:52.143627+0800 LGApp[2189:437620] App使用靜態(tài)庫AFNetworking的方法:<AFNetworkReachabilityManager: 0x281493d20>
  • 動態(tài)庫鏈接靜態(tài)庫時,會把靜態(tài)庫所有代碼都鏈接進(jìn)去恭金。靜態(tài)庫的導(dǎo)出符號操禀,在動態(tài)庫中依然是導(dǎo)出符號。所以App可以直接使用

隱藏靜態(tài)庫AFNetworking的全局符號

如果動態(tài)庫LGFramework不想把靜態(tài)庫AFNetworking的全局符號(導(dǎo)出符號)暴露出去横腿,可以通過-hidden-l<libraryname>參數(shù)隱藏靜態(tài)庫的全局符號

使用man ld查看ld命令颓屑,找到-hidden-l<libraryname>參數(shù)

LGFramework項目中斤寂,創(chuàng)建xcconfig文件,并配置到Tatget上揪惦,寫入以下代碼:

#include "Pods/Target Support Files/Pods-LGFramework/Pods-LGFramework.debug.xcconfig"
OTHER_LDFLAGS =  -Xlinker -hidden-l"AFNetworking" $(inherited)
  • 導(dǎo)入Pods生成的xcconfig
  • 因為Cocoapods生成的xcconfig文件包含了-framework AFNetworking參數(shù)遍搞,想要將AFNetworking指定為-hidden-l,需將其放在$(inherited)前面

編譯報錯器腋,提示錯誤信息:_OBJC_CLASS_$_AFNetworkReachabilityManager符號未定義

Undefined symbols for architecture arm64:
 "_OBJC_CLASS_$_AFNetworkReachabilityManager", referenced from:
     objc-class-ref in ViewController.o
靜態(tài)庫鏈接靜態(tài)庫

搭建LGFramework項目

LGFramework是一個靜態(tài)庫項目溪猿,項目中使用Pods導(dǎo)入靜態(tài)庫AFNetworking

打開LGOneObject.h文件,寫入以下代碼:

#import <Foundation/Foundation.h>

@interface LGOneObject : NSObject

- (void)testOneObject;

@end

打開LGOneObject.m文件纫塌,寫入以下代碼:

#ifndef AFN_HEADER_PATH
 #define AFN_HEADER_PATH <AFNetworking/AFNetworking.h>
#endif

#ifndef AFN
 #define AFN __has_include(AFN_HEADER_PATH)
#endif

#if AFN
#import AFN_HEADER_PATH
#endif

#import "LGOneObject.h"

@implementation LGOneObject

- (void)testOneObject {
#if AFN
   AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager manager];
   NSLog(@"LGFramework-----LGOneObject-----AFN: %@", manager);
#else
   NSLog(@"LGFramework-----LGOneObject-----NO AFN");
#endif
}

@end

打開LGFramework.h文件诊县,寫入以下代碼:

#import <LGFramework/LGOneObject.h>

搭建LGApp項目

LGApp是一個App項目

創(chuàng)建MulitProject.xcworkspace,加入LGFramework靜態(tài)庫和Pods項目护戳。LGApp鏈接LGFramework靜態(tài)庫

  • 項目結(jié)構(gòu):LGApp(App) -> LGFramework(靜態(tài)庫A) -> AFNetworking(靜態(tài)庫B)

LGApp項目中翎冲,打開ViewController文件,寫入以下代碼:

#import "ViewController.h"
#import <LGFramework/LGFramework.h>

@implementation ViewController

- (void)viewDidLoad {
  [super viewDidLoad];
  LGOneObject *object = [LGOneObject new];
  [object testOneObject];
}

@end

編譯報錯媳荒,提示錯誤信息:_OBJC_CLASS_$_AFNetworkReachabilityManager符號未定義

Undefined symbols for architecture arm64:
 "_OBJC_CLASS_$_AFNetworkReachabilityManager", referenced from:
     objc-class-ref in LGFramework(LGOneObject.o)
  • 靜態(tài)庫LGFramework生成時抗悍,只保存了靜態(tài)庫AFNetworking的頭文件信息或靜態(tài)庫AFNetworking的名稱(Auto-Link)。App鏈接靜態(tài)庫LGFramework后钳枕,會把靜態(tài)庫LGFramework所有代碼都鏈接進(jìn)去缴渊。但是并不知道靜態(tài)庫AFNetworking的位置和名稱。所以編譯報錯

介紹以下兩種解決辦法:

【方式一】

使用PodsLGApp項目中導(dǎo)入靜態(tài)庫AFNetworking

Pods項目中鱼炒,打開Podfile文件衔沼,寫入以下代碼:

platform :ios, '9.0'

workspace '../MulitProject.xcworkspace'

target 'LGFramework' do
 # use_frameworks!
 pod 'AFNetworking'
end

target 'LGApp' do
 project '../LGApp/LGApp.xcodeproj'
 # use_frameworks!
 pod 'AFNetworking'
end

選擇真機(jī),運(yùn)行項目昔瞧,輸出內(nèi)容如下:

2021-03-10 11:33:33.533189+0800 LGApp[2257:453229] LGFramework-----LGOneObject-----AFN: <AFNetworkReachabilityManager: 0x28164b920>
【方式二】

手動配置靜態(tài)庫AFNetworking的路徑和名稱

LGApp項目中指蚁,創(chuàng)建xcconfig文件,并配置到Tatget上自晰,寫入以下代碼:

LIBRARY_SEARCH_PATHS = $(inherited) "${BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/AFNetworking"
OTHER_LDFLAGS = $(inherited) -ObjC -l"AFNetworking"

選擇真機(jī)凝化,運(yùn)行項目,輸出內(nèi)容如下:

2021-03-10 11:37:57.163689+0800 LGApp[2275:454837] LGFramework-----LGOneObject-----AFN: <AFNetworkReachabilityManager: 0x28232b040>
靜態(tài)庫鏈接動態(tài)庫

搭建LGFramework項目

LGFramework是一個靜態(tài)庫項目酬荞,項目中使用Pods導(dǎo)入動態(tài)庫AFNetworking

打開LGOneObject.h文件搓劫,寫入以下代碼:

#import <Foundation/Foundation.h>

@interface LGOneObject : NSObject

- (void)testOneObject;

@end

打開LGOneObject.m文件,寫入以下代碼:

#ifndef AFN_HEADER_PATH
 #define AFN_HEADER_PATH <AFNetworking/AFNetworking.h>
#endif

#ifndef AFN
 #define AFN __has_include(AFN_HEADER_PATH)
#endif

#if AFN
#import AFN_HEADER_PATH
#endif

#import "LGOneObject.h"

@implementation LGOneObject

- (void)testOneObject {
#if AFN
   AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager manager];
   NSLog(@"LGFramework-----LGOneObject-----AFN: %@", manager);
#else
   NSLog(@"LGFramework-----LGOneObject-----NO AFN");
#endif
}

@end

打開LGFramework.h文件混巧,寫入以下代碼:

#import <LGFramework/LGOneObject.h>

搭建LGApp項目

LGApp是一個App項目

創(chuàng)建MulitProject.xcworkspace枪向,加入LGFramework靜態(tài)庫和Pods項目。LGApp鏈接LGFramework靜態(tài)庫

  • 項目結(jié)構(gòu):LGApp(App) -> LGFramework(靜態(tài)庫A) -> AFNetworking(動態(tài)庫A)

LGApp項目中咧党,打開ViewController文件秘蛔,寫入以下代碼:

#import "ViewController.h"
#import <LGFramework/LGFramework.h>

@implementation ViewController

- (void)viewDidLoad {
  [super viewDidLoad];
  LGOneObject *object = [LGOneObject new];
  [object testOneObject];
}

@end

編譯報錯,提示錯誤信息:_OBJC_CLASS_$_AFNetworkReachabilityManager符號未定義

Undefined symbols for architecture arm64:
 "_OBJC_CLASS_$_AFNetworkReachabilityManager", referenced from:
     objc-class-ref in LGFramework(LGOneObject.o)
  • 靜態(tài)庫LGFramework生成時,只保存了動態(tài)庫AFNetworking的名稱(Auto-Link)缠犀。App鏈接靜態(tài)庫LGFramework后数苫,會把靜態(tài)庫LGFramework所有代碼都鏈接進(jìn)去聪舒。但是并不知道動態(tài)庫AFNetworking的位置辨液,也沒有提供@rpath路徑。所以編譯報錯

介紹以下兩種解決辦法:

【方式一】

使用PodsLGApp項目中導(dǎo)入動態(tài)庫AFNetworking

Pods項目中箱残,打開Podfile文件滔迈,寫入以下代碼:

platform :ios, '9.0'

workspace '../MulitProject.xcworkspace'

target 'LGFramework' do
 use_frameworks!
 pod 'AFNetworking'
end

target 'LGApp' do
 project '../LGApp/LGApp.xcodeproj'
 use_frameworks!
 pod 'AFNetworking'
end

選擇真機(jī),運(yùn)行項目被辑,輸出內(nèi)容如下:

2021-03-10 13:35:10.416174+0800 LGApp[2380:479215] LGFramework-----LGOneObject-----AFN: <AFNetworkReachabilityManager: 0x282363440>
【方式二】

指定頭文件路徑燎悍、Framework所在目錄、@rpath路徑盼理。通過腳本谈山,將AFNetworking.framework拷貝到App中的Frameworks目錄

方式一中,Pods生成的Pods-LGApp-frameworks.sh腳本宏怔,拷貝到LGApp項目的根目錄

Pods項目中奏路,打開Podfile文件,恢復(fù)初始代碼:

platform :ios, '9.0'

target 'LGFramework' do
 use_frameworks!
 pod 'AFNetworking'
end

LGApp項目中臊诊,創(chuàng)建xcconfig文件鸽粉,并配置到Tatget上,寫入以下代碼:

HEADER_SEARCH_PATHS = $(inherited) "${BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/AFNetworking/AFNetworking.framework/Headers"
FRAMEWORK_SEARCH_PATHS = $(inherited) "${BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/AFNetworking"
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' '@executable_path/../../Frameworks'
SCRIPT_DIR = ${SRCROOT}/../LGApp/Pods-LGApp-frameworks.sh
  • 指定頭文件路徑Header Search Paths
  • 指定Framework所在目錄Framework Search Paths
  • 指定@rpath路徑
  • 定義SCRIPT_DIR變量抓艳,存儲.sh腳本路徑

LGApp項目中触机,選擇Build Phases,在Run Script中寫入腳本:"${SCRIPT_DIR}"

選擇真機(jī)玷或,運(yùn)行項目儡首,輸出內(nèi)容如下:

2021-03-10 13:46:27.990741+0800 LGApp[2393:481513] LGFramework-----LGOneObject-----AFN: <AFNetworkReachabilityManager: 0x282a61360>
配置Cocoapods
指定動態(tài)庫+靜態(tài)庫

Cocoapods導(dǎo)入三方庫,有些場景下偏友,需要指定某些庫為動態(tài)庫蔬胯、某些庫為靜態(tài)庫。此時配置use_frameworks!無法滿足需求约谈,可以使用以下代碼:

platform :ios, '9.0'

target 'LGFramework' do
  use_frameworks!
  pod 'AFNetworking'
  pod 'SDWebImage'
end

#指定需要被編譯成static_framework的庫
$static_framework = ['SDWebImage']

pre_install do |installer|
  installer.pod_targets.each do |pod|
    if $static_framework.include?(pod.name)
      def pod.build_type;
        Pod::BuildType.static_framework
      end
    end
  end
end
自定義xcworkspace

有些復(fù)雜項目笔宿,使用的并不是Cocoapods提供xcworkspace

最上層是自定義的MulitProject.xcworkspace,包含LGAppLGFramework兩個Project棱诱。而Cocoapods提供xcworkspaceLGFramework目錄中

此時往MulitProject.xcworkspace中的LGApp導(dǎo)入三方庫泼橘,可以使用以下代碼:

platform :ios, '9.0'

workspace '../MulitProject.xcworkspace'

target 'LGFramework' do
 use_frameworks!
 pod 'AFNetworking'
end

target 'LGApp' do
 project '../LGApp/LGApp.xcodeproj'
 use_frameworks!
 pod 'AFNetworking'
end
  • 指定MulitProject.xcworkspace路徑
  • LGApp中導(dǎo)入三方庫,指定Project路徑
總結(jié)

弱引用動態(tài)庫

  • 標(biāo)記-weak_framework參數(shù)迈勋,允許在運(yùn)行時不鏈接該動態(tài)庫
  • 標(biāo)記為弱引用動態(tài)庫炬灭,運(yùn)行時找不到地址不會報錯,而是返回一個NULL

靜態(tài)庫代碼沖突

  • 使用-force_load參數(shù)靡菇,強(qiáng)制鏈接指定靜態(tài)庫
  • 使用-load_hidden參數(shù)重归,將指定靜態(tài)庫的所有符號設(shè)置為隱藏
  • 指定-all_load-ObjC參數(shù)米愿,建議對靜態(tài)庫重新打包,刪除重復(fù)的Symbol

App -> 動態(tài)庫A -> 動態(tài)庫B

  • 使用PodsApp項目中導(dǎo)入動態(tài)庫B
  • 通過腳本鼻吮,將動態(tài)庫B拷貝到App中的Frameworks目錄

App想使用動態(tài)庫B的方法

  • 使用PodsApp項目中導(dǎo)入動態(tài)庫B
  • 標(biāo)記-reexport_framework-reexport_l參數(shù)育苟,重新將動態(tài)庫B通過動態(tài)庫A導(dǎo)出給App

動態(tài)庫的反向依賴

  • 使用-U參數(shù),標(biāo)記某個符號為動態(tài)查找符號
  • 使用-undefined參數(shù)椎木,將動態(tài)庫中的符號全部標(biāo)記為動態(tài)查找符號违柏。配置后動態(tài)庫中可以使用任意符號,這樣風(fēng)險較高香椎,不建議使用

App -> 動態(tài)庫A -> 靜態(tài)庫A

  • App可以直接使用靜態(tài)庫A的導(dǎo)出符號
  • 使用-hidden-l<libraryname>參數(shù)漱竖,隱藏靜態(tài)庫A的全局符號(導(dǎo)出符號)

App -> 靜態(tài)庫A -> 靜態(tài)庫B

  • 使用PodsApp項目中導(dǎo)入靜態(tài)庫B
  • 手動配置靜態(tài)庫B的路徑和名稱

App -> 靜態(tài)庫A -> 動態(tài)庫A

  • 使用PodsApp項目中導(dǎo)入動態(tài)庫A
  • 指定頭文件路徑擎厢、Framework所在目錄枫疆、@rpath路徑。通過腳本龙宏,將動態(tài)庫A拷貝到App中的Frameworks目錄
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末玛界,一起剝皮案震驚了整個濱河市万矾,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌脚仔,老刑警劉巖勤众,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異鲤脏,居然都是意外死亡们颜,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門猎醇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來窥突,“玉大人,你說我怎么就攤上這事硫嘶∽栉剩” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵沦疾,是天一觀的道長称近。 經(jīng)常有香客問我,道長哮塞,這世上最難降的妖魔是什么刨秆? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮忆畅,結(jié)果婚禮上衡未,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好缓醋,可當(dāng)我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布如失。 她就那樣靜靜地躺著,像睡著了一般送粱。 火紅的嫁衣襯著肌膚如雪褪贵。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天葫督,我揣著相機(jī)與錄音竭鞍,去河邊找鬼板惑。 笑死橄镜,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的冯乘。 我是一名探鬼主播洽胶,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼裆馒!你這毒婦竟也來了姊氓?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤喷好,失蹤者是張志新(化名)和其女友劉穎翔横,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體梗搅,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡禾唁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了无切。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片荡短。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖哆键,靈堂內(nèi)的尸體忽然破棺而出掘托,到底是詐尸還是另有隱情,我是刑警寧澤籍嘹,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布闪盔,位于F島的核電站,受9級特大地震影響辱士,放射性物質(zhì)發(fā)生泄漏泪掀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一识补、第九天 我趴在偏房一處隱蔽的房頂上張望族淮。 院中可真熱鬧,春花似錦、人聲如沸祝辣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蝙斜。三九已至名惩,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間孕荠,已是汗流浹背娩鹉。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留稚伍,地道東北人弯予。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像个曙,于是被迫代替她去往敵國和親锈嫩。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容