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

弱引用動(dòng)態(tài)庫

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

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

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

打開LGApp項(xiàng)目,創(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)行時(shí),程序崩潰

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>指定動(dòng)態(tài)庫為weak imports。如果在運(yùn)行時(shí)找不到該庫,會(huì)自動(dòng)將該庫的地址及內(nèi)容返回NULL

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

  • -weak_framework:標(biāo)記為弱引用動(dòng)態(tài)庫。運(yùn)行時(shí)找不到地址不會(huì)報(bào)錯(cuò),而是返回一個(gè)NULL

打開xcconfig文件,修改OTHER_LDFLAGS配置項(xiàng):

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

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

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

使用-weak_framework標(biāo)記為弱引用動(dòng)態(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中動(dòng)態(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項(xiàng)目,將靜態(tài)庫AFNetworking代碼,在項(xiàng)目根目錄下拷貝兩份。將靜態(tài)庫目錄,分別命名為AFNetworkingAFNetworking2

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

打開LGApp項(xiàng)目,創(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"

此時(shí)編譯可以通過,因?yàn)殒溄屿o態(tài)庫默認(rèn)使用-noall_load參數(shù)。當(dāng)鏈接靜態(tài)庫AFNetworking找到代碼后,就不會(huì)鏈接靜態(tài)庫AFNetworking2

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

打開xcconfig文件,修改OTHER_LDFLAGS配置項(xiàng):

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

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

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配置項(xiàng):

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的所有符號(hào)設(shè)置為隱藏

編譯報(bào)錯(cuò),依然提示重復(fù)符號(hào)

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

動(dòng)態(tài)庫鏈接動(dòng)態(tài)庫

搭建LGFramework項(xiàng)目

LGFramework是一個(gè)動(dòng)態(tài)庫項(xiàng)目,項(xiàng)目中使用Pods導(dǎo)入動(dòng)態(tài)庫AFNetworking

使用Pods向一個(gè)Framework中導(dǎo)入三方庫,Pods只會(huì)配置鏈接參數(shù),不會(huì)將三方庫拷貝到指定位置,也不會(huì)生成.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實(shí)例對象,并打印manager對象。否則打印NO AFN

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

#import <LGFramework/LGOneObject.h>

搭建LGApp項(xiàng)目

LGApp是一個(gè)App項(xiàng)目

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

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

LGApp項(xiàng)目中,打開ViewController文件,寫入以下代碼:

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

@implementation ViewController

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

@end

運(yùn)行項(xiàng)目后,程序直接崩潰

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項(xiàng)目中,打開Pods-LGFramework.debug.xcconfig文件,查看LD_RUNPATH_SEARCH_PATHS配置項(xiàng)

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

來到項(xiàng)目編譯后的目錄,AFNetworking.framework存放在AFNetworking目錄下,而AFNetworkingLGApp.app平級(jí)。所以按照LD_RUNPATH_SEARCH_PATHS配置項(xiàng),無法找到AFNetworking.framework

介紹以下三種解決辦法:

【方式一】

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

LGFramework項(xiàng)目中,創(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)行項(xiàng)目,輸出內(nèi)容如下:

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

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

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,需要將鏈接的動(dòng)態(tài)庫拷貝到App包內(nèi)
【方式二】

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

Pods項(xiàng)目中,打開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)行項(xiàng)目,輸出內(nèi)容如下:

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

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

【方式三】

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

方式二中,Pods生成的Pods-LGApp-frameworks.sh腳本,拷貝到LGApp項(xiàng)目的根目錄

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

platform :ios, '9.0'

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

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

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

LGApp項(xiàng)目中,選擇Build Phases,在Run Script中寫入腳本:"${SCRIPT_DIR}"

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

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

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

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

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

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

LGApp項(xiàng)目中,打開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使用動(dòng)態(tài)庫B的方法:%@", manager);
}

@end

編譯失敗,提示錯(cuò)誤信息:找不到頭文件

'AFNetworking/AFNetworking.h' file not found

介紹以下兩種解決辦法:

【方式一】

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

Pods項(xiàng)目中,打開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項(xiàng)目中,打開xcconfig文件,寫入以下代碼:

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

選擇真機(jī),運(yùn)行項(xiàng)目,輸出內(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使用動(dòng)態(tài)庫B的方法:<AFNetworkReachabilityManager: 0x283afac60>
【方式二】

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

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

platform :ios, '9.0'

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

LGFramework項(xiàng)目中,打開xcconfig文件,寫入以下代碼:

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

LGApp項(xiàng)目中,打開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)行項(xiàng)目,輸出內(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使用動(dòng)態(tài)庫B的方法:<AFNetworkReachabilityManager: 0x283afac60>
動(dòng)態(tài)庫的反向依賴

動(dòng)態(tài)庫的反向依賴,由于符合的作用空間,在運(yùn)行時(shí),動(dòng)態(tài)庫可以動(dòng)態(tài)找到App的符號(hào)。所以只要在編譯期間,繞過符號(hào)未定義的錯(cuò)誤即可

LGApp項(xiàng)目中,打開LGAppObject.h文件,寫入以下代碼:

#import <Foundation/Foundation.h>

@interface LGAppObject : NSObject

- (void)test_app;

@end

LGApp項(xiàng)目中,打開LGAppObject.m文件,寫入以下代碼:

#import "LGAppObject.h"

@implementation LGAppObject

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

@end

LGFramework項(xiàng)目中,打開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項(xiàng)目中的LGAppObject.h頭文件
  • 調(diào)用了LGAppObjecttest_app方法

LGFramework項(xiàng)目中,打開xcconfig文件,寫入以下代碼:

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

編譯報(bào)錯(cuò),提示錯(cuò)誤信息:_OBJC_CLASS_$_LGAppObject符號(hào)未定義

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

介紹以下兩種解決辦法:

【方式一】

使用-U參數(shù),標(biāo)記某個(gè)符號(hào)為動(dòng)態(tài)查找符號(hào)

LGFramework項(xiàng)目中,打開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)記符號(hào)_OBJC_CLASS_$_LGAppObject為動(dòng)態(tài)查找符號(hào)

選擇真機(jī),運(yùn)行項(xiàng)目,輸出內(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ù),將動(dòng)態(tài)庫中的符號(hào)全部標(biāo)記為動(dòng)態(tài)查找符號(hào)。配置后動(dòng)態(tài)庫中可以使用任意符號(hào),這樣風(fēng)險(xiǎn)較高,不建議使用

LGFramework項(xiàng)目中,打開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ù),將整個(gè)LGFramework動(dòng)態(tài)庫中的符號(hào),都標(biāo)記為動(dòng)態(tài)查找符號(hào)

選擇真機(jī),運(yùn)行項(xiàng)目,輸出內(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>
動(dòng)態(tài)庫鏈接靜態(tài)庫

搭建LGFramework項(xiàng)目

LGFramework是一個(gè)動(dòng)態(tài)庫項(xiàng)目,項(xiàng)目中使用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項(xiàng)目

LGApp是一個(gè)App項(xiàng)目

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

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

LGApp項(xiàng)目中,打開ViewController文件,寫入以下代碼:

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

@implementation ViewController

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

@end

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

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

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

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

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

LGApp項(xiàng)目中,打開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)行項(xiàng)目,輸出內(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>
  • 動(dòng)態(tài)庫鏈接靜態(tài)庫時(shí),會(huì)把靜態(tài)庫所有代碼都鏈接進(jìn)去。靜態(tài)庫的導(dǎo)出符號(hào),在動(dòng)態(tài)庫中依然是導(dǎo)出符號(hào)。所以App可以直接使用

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

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

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

LGFramework項(xiàng)目中,創(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
  • 因?yàn)?code>Cocoapods生成的xcconfig文件包含了-framework AFNetworking參數(shù),想要將AFNetworking指定為-hidden-l,需將其放在$(inherited)前面

編譯報(bào)錯(cuò),提示錯(cuò)誤信息:_OBJC_CLASS_$_AFNetworkReachabilityManager符號(hào)未定義

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

搭建LGFramework項(xiàng)目

LGFramework是一個(gè)靜態(tài)庫項(xiàng)目,項(xiàng)目中使用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項(xiàng)目

LGApp是一個(gè)App項(xiàng)目

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

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

LGApp項(xiàng)目中,打開ViewController文件,寫入以下代碼:

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

@implementation ViewController

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

@end

編譯報(bào)錯(cuò),提示錯(cuò)誤信息:_OBJC_CLASS_$_AFNetworkReachabilityManager符號(hào)未定義

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

介紹以下兩種解決辦法:

【方式一】

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

Pods項(xiàng)目中,打開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)行項(xiàng)目,輸出內(nèi)容如下:

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

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

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

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

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

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

搭建LGFramework項(xiàng)目

LGFramework是一個(gè)靜態(tài)庫項(xiàng)目,項(xiàng)目中使用Pods導(dǎo)入動(dòng)態(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項(xiàng)目

LGApp是一個(gè)App項(xiàng)目

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

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

LGApp項(xiàng)目中,打開ViewController文件,寫入以下代碼:

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

@implementation ViewController

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

@end

編譯報(bào)錯(cuò),提示錯(cuò)誤信息:_OBJC_CLASS_$_AFNetworkReachabilityManager符號(hào)未定義

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

介紹以下兩種解決辦法:

【方式一】

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

Pods項(xiàng)目中,打開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)行項(xiàng)目,輸出內(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項(xiàng)目的根目錄

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

platform :ios, '9.0'

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

LGApp項(xiàng)目中,創(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變量,存儲(chǔ).sh腳本路徑

LGApp項(xiàng)目中,選擇Build Phases,在Run Script中寫入腳本:"${SCRIPT_DIR}"

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

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

Cocoapods導(dǎo)入三方庫,有些場景下,需要指定某些庫為動(dòng)態(tài)庫、某些庫為靜態(tài)庫。此時(shí)配置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ù)雜項(xiàng)目,使用的并不是Cocoapods提供xcworkspace

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

此時(shí)往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é)

弱引用動(dòng)態(tài)庫

  • 標(biāo)記-weak_framework參數(shù),允許在運(yùn)行時(shí)不鏈接該動(dòng)態(tài)庫
  • 標(biāo)記為弱引用動(dòng)態(tài)庫,運(yùn)行時(shí)找不到地址不會(huì)報(bào)錯(cuò),而是返回一個(gè)NULL

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

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

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

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

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

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

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

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

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

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

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

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

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

  • 使用PodsApp項(xiàng)目中導(dǎo)入動(dòng)態(tài)庫A
  • 指定頭文件路徑、Framework所在目錄、@rpath路徑。通過腳本,將動(dòng)態(tài)庫A拷貝到App中的Frameworks目錄
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,967評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,273評(píng)論 3 415
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,870評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,742評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,527評(píng)論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,010評(píng)論 1 322
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,108評(píng)論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,250評(píng)論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,769評(píng)論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,656評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,853評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,371評(píng)論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,103評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,472評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,717評(píng)論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,487評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,815評(píng)論 2 372

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