iOS高级强化--010:动态库与静态库实战

弱引用动态库

弱引用动态库:标记-weak_framework参数,允许在运行时不链接该动态库

正常情况下,运行时找不到动态库所在位置,程序崩溃并提示image not found

搭建LGApp项目,将动态库SYTimer.framework拷贝到项目根目录

打开LGApp项目,创建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三要素,编译可以成功。但运行时,程序崩溃

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-weak_framework 指定动态库为weak imports。如果在运行时找不到该库,会自动将该库的地址及内容返回NULL

使用man ld查看ld命令,找到-weak_framework参数

  • -weak_framework:标记为弱引用动态库。运行时找不到地址不会报错,而是返回一个NULL

打开xcconfig文件,修改OTHER_LDFLAGS配置项:

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

项目运行成功,输出内容如下:

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

使用-weak_framework标记为弱引用动态库,Mach-O中记录的Load Command名称不再是LC_LOAD_DYLIB,变为LC_LOAD_WEAK_DYLIB

使用otool -l test | grep 'DYLIB' -A 2命令,查看Mach-O中动态库的路径

         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)
静态库代码冲突

搭建LGApp项目,将静态库AFNetworking代码,在项目根目录下拷贝两份。将静态库目录,分别命名为AFNetworkingAFNetworking2

打开AFNetworking2目录,将libAFNetworking.a文件,重命名为libAFNetworking2.a

打开LGApp项目,创建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"

此时编译可以通过,因为链接静态库默认使用-noall_load参数。当链接静态库AFNetworking找到代码后,就不会链接静态库AFNetworking2

但真实项目中,我们使用Pods导入的三方库,大部分使用-all_load-ObjC参数,此时问题出现了

打开xcconfig文件,修改OTHER_LDFLAGS配置项:

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

编译报错,出现大量的重复符号

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参数,强制链接的静态库AFNetworking
  • 使用-load_hidden参数,将静态库AFNetworking2的所有符号设置为隐藏

编译报错,依然提示重复符号

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

结论:如果导入相同静态库,又指定了-all_load-ObjC参数,建议对静态库重新打包,删除重复的Symbol

动态库链接动态库

搭建LGFramework项目

LGFramework是一个动态库项目,项目中使用Pods导入动态库AFNetworking

使用Pods向一个Framework中导入三方库,Pods只会配置链接参数,不会将三方库拷贝到指定位置,也不会生成.sh脚本

打开LGOneObject.h文件,写入以下代码:

#import 

@interface LGOneObject : NSObject

- (void)testOneObject;

@end

打开LGOneObject.m文件,写入以下代码:

#ifndef AFN_HEADER_PATH
 #define AFN_HEADER_PATH 
#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:表示头文件
  • AFN:表示是否存在头文件
  • 导入头文件:判断AFN,如果存在,执行#import AFN_HEADER_PATH代码
  • testOneObject方法:判断AFN,如果存在,初始化AFNetworkReachabilityManager实例对象,并打印manager对象。否则打印NO AFN

打开LGFramework.h文件,写入以下代码:

#import 

搭建LGApp项目

LGApp是一个App项目

创建MulitProject.xcworkspace,加入LGFramework动态库和Pods项目。LGApp链接LGFramework动态库

  • 项目结构:LGApp(App) -> LGFramework(动态库A) -> AFNetworking(动态库B)

LGApp项目中,打开ViewController文件,写入以下代码:

#import "ViewController.h"
#import 

@implementation ViewController

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

@end

运行项目后,程序直接崩溃

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项目中,创建xcconfig文件,并配置到Tatget上,写入以下代码:

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

选择模拟器,运行项目,输出内容如下:

2021-03-09 16:05:33.972325+0800 LGApp[10862:9536454] LGFramework-----LGOneObject-----AFN: 

选择真机,运行项目,程序崩溃:

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
  • 在设备上运行App,需要将链接的动态库拷贝到App包内
【方式二】

使用PodsLGApp项目中导入动态库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

选择真机,运行项目,输出内容如下:

2021-03-09 16:18:57.810101+0800 LGApp[1471:289515] LGFramework-----LGOneObject-----AFN: 

使用PodsApp中导入三方库,会生成一个.sh脚本。它的作用就是将.framework拷贝到App中的Frameworks目录

【方式三】

通过脚本,将AFNetworking.framework拷贝到App中的Frameworks目录

方式二中,Pods生成的Pods-LGApp-frameworks.sh脚本,拷贝到LGApp项目的根目录

Pods项目中,打开Podfile文件,恢复初始代码:

platform :ios, '9.0'

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

LGApp项目中,创建xcconfig文件,并配置到Tatget上,写入以下代码:

SCRIPT_DIR = ${SRCROOT}/Pods-LGApp-frameworks.sh
  • 定义SCRIPT_DIR变量,存储.sh脚本路径

LGApp项目中,选择Build Phases,在Run Script中写入脚本:"${SCRIPT_DIR}"

选择真机,运行项目,输出内容如下:

2021-03-09 17:51:16.113484+0800 LGApp[1556:311935] LGFramework-----LGOneObject-----AFN: 

此时LGApp项目不再使用Pods导入AFNetworking动态库,而是通过脚本,将AFNetworking.framework拷贝到App中的Frameworks目录

App想使用动态库B的方法

基于上述案例:LGApp(App) -> LGFramework(动态库A) -> AFNetworking(动态库B)

如果App想使用动态库B的方法,也就是让LGApp直接调用AFNetworking的方法

LGApp项目中,打开ViewController.m文件,写入以下代码:

#import "ViewController.h"
#import 
#import 

@implementation ViewController

- (void)viewDidLoad {
   [super viewDidLoad];
   LGOneObject *object = [LGOneObject new];
   [object testOneObject];
   
   AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager manager];
   NSLog(@"App使用动态库B的方法:%@", manager);
}

@end

编译失败,提示错误信息:找不到头文件

'AFNetworking/AFNetworking.h' file not found

介绍以下两种解决办法:

【方式一】

使用PodsLGApp项目中导入动态库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"
  • 导入Pods生成的xcconfig

选择真机,运行项目,输出内容如下:

2021-03-09 18:32:15.142101+0800 LGApp[1583:320382] LGFramework-----LGOneObject-----AFN: 
2021-03-09 18:32:15.142263+0800 LGApp[1583:320382] App使用动态库B的方法:
【方式二】

标记-reexport_framework-reexport_l参数,重新将AFNetworking通过动态库LGFramework导出给LGApp

Pods项目中,打开Podfile文件,恢复初始代码:

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参数,想要将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"

选择真机,运行项目,输出内容如下:

2021-03-09 18:32:15.142101+0800 LGApp[1583:320382] LGFramework-----LGOneObject-----AFN: 
2021-03-09 18:32:15.142263+0800 LGApp[1583:320382] App使用动态库B的方法:
动态库的反向依赖

动态库的反向依赖,由于符合的作用空间,在运行时,动态库可以动态找到App的符号。所以只要在编译期间,绕过符号未定义的错误即可

LGApp项目中,打开LGAppObject.h文件,写入以下代码:

#import 

@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 
#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
  • 导入LGApp项目中的LGAppObject.h头文件
  • 调用了LGAppObjecttest_app方法

LGFramework项目中,打开xcconfig文件,写入以下代码:

#include "Pods/Target Support Files/Pods-LGFramework/Pods-LGFramework.debug.xcconfig"
HEADER_SEARCH_PATHS = $(inherited) "${SRCROOT}/../LGApp/LGApp"
  • 导入LGApp项目中的头文件

编译报错,提示错误信息:_OBJC_CLASS_$_LGAppObject符号未定义

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

介绍以下两种解决办法:

【方式一】

使用-U参数,标记某个符号为动态查找符号

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参数,标记符号_OBJC_CLASS_$_LGAppObject为动态查找符号

选择真机,运行项目,输出内容如下:

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: 
【方式二】

使用-undefined参数,将动态库中的符号全部标记为动态查找符号。配置后动态库中可以使用任意符号,这样风险较高,不建议使用

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参数,将整个LGFramework动态库中的符号,都标记为动态查找符号

选择真机,运行项目,输出内容如下:

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: 
动态库链接静态库

搭建LGFramework项目

LGFramework是一个动态库项目,项目中使用Pods导入静态库AFNetworking

打开LGOneObject.h文件,写入以下代码:

#import 

@interface LGOneObject : NSObject

- (void)testOneObject;

@end

打开LGOneObject.m文件,写入以下代码:

#ifndef AFN_HEADER_PATH
 #define AFN_HEADER_PATH 
#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 

搭建LGApp项目

LGApp是一个App项目

创建MulitProject.xcworkspace,加入LGFramework动态库和Pods项目。LGApp链接LGFramework动态库

  • 项目结构:LGApp(App) -> LGFramework(动态库A) -> AFNetworking(静态库A)

LGApp项目中,打开ViewController文件,写入以下代码:

#import "ViewController.h"
#import 

@implementation ViewController

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

@end

选择真机,运行项目,输出内容如下:

2021-03-10 10:16:12.781430+0800 LGApp[2185:436467] LGFramework-----LGOneObject-----AFN: 
  • 动态库LGFramework生成的过程中,链接静态库AFNetworking时,会把静态库AFNetworking所有代码都链接进去。所以编译链接都不会报错

App想使用静态库AFNetworking的方法

LGApp项目中,创建xcconfig文件,并配置到Tatget上,写入以下代码:

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

LGApp项目中,打开ViewController.m文件,写入以下代码:

#import "ViewController.h"
#import 
#import 

@implementation ViewController

- (void)viewDidLoad {
   [super viewDidLoad];
   LGOneObject *object = [LGOneObject new];
   [object testOneObject];
   
   AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager manager];
   NSLog(@"App使用静态库AFNetworking的方法:%@", manager);
}

@end

选择真机,运行项目,输出内容如下:

2021-03-10 10:20:52.143445+0800 LGApp[2189:437620] LGFramework-----LGOneObject-----AFN: 
2021-03-10 10:20:52.143627+0800 LGApp[2189:437620] App使用静态库AFNetworking的方法:
  • 动态库链接静态库时,会把静态库所有代码都链接进去。静态库的导出符号,在动态库中依然是导出符号。所以App可以直接使用

隐藏静态库AFNetworking的全局符号

如果动态库LGFramework不想把静态库AFNetworking的全局符号(导出符号)暴露出去,可以通过-hidden-l参数隐藏静态库的全局符号

使用man ld查看ld命令,找到-hidden-l参数

LGFramework项目中,创建xcconfig文件,并配置到Tatget上,写入以下代码:

#include "Pods/Target Support Files/Pods-LGFramework/Pods-LGFramework.debug.xcconfig"
OTHER_LDFLAGS =  -Xlinker -hidden-l"AFNetworking" $(inherited)
  • 导入Pods生成的xcconfig
  • 因为Cocoapods生成的xcconfig文件包含了-framework AFNetworking参数,想要将AFNetworking指定为-hidden-l,需将其放在$(inherited)前面

编译报错,提示错误信息:_OBJC_CLASS_$_AFNetworkReachabilityManager符号未定义

Undefined symbols for architecture arm64:
 "_OBJC_CLASS_$_AFNetworkReachabilityManager", referenced from:
     objc-class-ref in ViewController.o
静态库链接静态库

搭建LGFramework项目

LGFramework是一个静态库项目,项目中使用Pods导入静态库AFNetworking

打开LGOneObject.h文件,写入以下代码:

#import 

@interface LGOneObject : NSObject

- (void)testOneObject;

@end

打开LGOneObject.m文件,写入以下代码:

#ifndef AFN_HEADER_PATH
 #define AFN_HEADER_PATH 
#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 

搭建LGApp项目

LGApp是一个App项目

创建MulitProject.xcworkspace,加入LGFramework静态库和Pods项目。LGApp链接LGFramework静态库

  • 项目结构:LGApp(App) -> LGFramework(静态库A) -> AFNetworking(静态库B)

LGApp项目中,打开ViewController文件,写入以下代码:

#import "ViewController.h"
#import 

@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)
  • 静态库LGFramework生成时,只保存了静态库AFNetworking的头文件信息或静态库AFNetworking的名称(Auto-Link)。App链接静态库LGFramework后,会把静态库LGFramework所有代码都链接进去。但是并不知道静态库AFNetworking的位置和名称。所以编译报错

介绍以下两种解决办法:

【方式一】

使用PodsLGApp项目中导入静态库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

选择真机,运行项目,输出内容如下:

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

手动配置静态库AFNetworking的路径和名称

LGApp项目中,创建xcconfig文件,并配置到Tatget上,写入以下代码:

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

选择真机,运行项目,输出内容如下:

2021-03-10 11:37:57.163689+0800 LGApp[2275:454837] LGFramework-----LGOneObject-----AFN: 
静态库链接动态库

搭建LGFramework项目

LGFramework是一个静态库项目,项目中使用Pods导入动态库AFNetworking

打开LGOneObject.h文件,写入以下代码:

#import 

@interface LGOneObject : NSObject

- (void)testOneObject;

@end

打开LGOneObject.m文件,写入以下代码:

#ifndef AFN_HEADER_PATH
 #define AFN_HEADER_PATH 
#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 

搭建LGApp项目

LGApp是一个App项目

创建MulitProject.xcworkspace,加入LGFramework静态库和Pods项目。LGApp链接LGFramework静态库

  • 项目结构:LGApp(App) -> LGFramework(静态库A) -> AFNetworking(动态库A)

LGApp项目中,打开ViewController文件,写入以下代码:

#import "ViewController.h"
#import 

@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)
  • 静态库LGFramework生成时,只保存了动态库AFNetworking的名称(Auto-Link)。App链接静态库LGFramework后,会把静态库LGFramework所有代码都链接进去。但是并不知道动态库AFNetworking的位置,也没有提供@rpath路径。所以编译报错

介绍以下两种解决办法:

【方式一】

使用PodsLGApp项目中导入动态库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

选择真机,运行项目,输出内容如下:

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

指定头文件路径、Framework所在目录、@rpath路径。通过脚本,将AFNetworking.framework拷贝到App中的Frameworks目录

方式一中,Pods生成的Pods-LGApp-frameworks.sh脚本,拷贝到LGApp项目的根目录

Pods项目中,打开Podfile文件,恢复初始代码:

platform :ios, '9.0'

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

LGApp项目中,创建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}"

选择真机,运行项目,输出内容如下:

2021-03-10 13:46:27.990741+0800 LGApp[2393:481513] LGFramework-----LGOneObject-----AFN: 
配置Cocoapods
指定动态库+静态库

Cocoapods导入三方库,有些场景下,需要指定某些库为动态库、某些库为静态库。此时配置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

有些复杂项目,使用的并不是Cocoapods提供xcworkspace

最上层是自定义的MulitProject.xcworkspace,包含LGAppLGFramework两个Project。而Cocoapods提供xcworkspaceLGFramework目录中

此时往MulitProject.xcworkspace中的LGApp导入三方库,可以使用以下代码:

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中导入三方库,指定Project路径
总结

弱引用动态库

  • 标记-weak_framework参数,允许在运行时不链接该动态库
  • 标记为弱引用动态库,运行时找不到地址不会报错,而是返回一个NULL

静态库代码冲突

  • 使用-force_load参数,强制链接指定静态库
  • 使用-load_hidden参数,将指定静态库的所有符号设置为隐藏
  • 指定-all_load-ObjC参数,建议对静态库重新打包,删除重复的Symbol

App -> 动态库A -> 动态库B

  • 使用PodsApp项目中导入动态库B
  • 通过脚本,将动态库B拷贝到App中的Frameworks目录

App想使用动态库B的方法

  • 使用PodsApp项目中导入动态库B
  • 标记-reexport_framework-reexport_l参数,重新将动态库B通过动态库A导出给App

动态库的反向依赖

  • 使用-U参数,标记某个符号为动态查找符号
  • 使用-undefined参数,将动态库中的符号全部标记为动态查找符号。配置后动态库中可以使用任意符号,这样风险较高,不建议使用

App -> 动态库A -> 静态库A

  • App可以直接使用静态库A的导出符号
  • 使用-hidden-l参数,隐藏静态库A的全局符号(导出符号)

App -> 静态库A -> 静态库B

  • 使用PodsApp项目中导入静态库B
  • 手动配置静态库B的路径和名称

App -> 静态库A -> 动态库A

  • 使用PodsApp项目中导入动态库A
  • 指定头文件路径、Framework所在目录、@rpath路径。通过脚本,将动态库A拷贝到App中的Frameworks目录

你可能感兴趣的:(iOS高级强化--010:动态库与静态库实战)