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

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

#import <LGFramework/LGOneObject.h>

搭建LGApp项目

LGApp是一个App项目

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

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

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

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

@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: <AFNetworkReachabilityManager: 0x600000856920>

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

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: <AFNetworkReachabilityManager: 0x280d231c0>

使用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: <AFNetworkReachabilityManager: 0x2831fa2c0>

此时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 <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使用动态库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: <AFNetworkReachabilityManager: 0x283afa7a0>
2021-03-09 18:32:15.142263+0800 LGApp[1583:320382] App使用动态库B的方法:<AFNetworkReachabilityManager: 0x283afac60>
【方式二】

标记-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: <AFNetworkReachabilityManager: 0x283afa7a0>
2021-03-09 18:32:15.142263+0800 LGApp[1583:320382] App使用动态库B的方法:<AFNetworkReachabilityManager: 0x283afac60>
动态库的反向依赖

动态库的反向依赖,由于符合的作用空间,在运行时,动态库可以动态找到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
  • 导入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: <AFNetworkReachabilityManager: 0x282785a60>
【方式二】

使用-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: <AFNetworkReachabilityManager: 0x2828047a0>
动态库链接静态库

搭建LGFramework项目

LGFramework是一个动态库项目,项目中使用Pods导入静态库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项目

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

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

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

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

@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: <AFNetworkReachabilityManager: 0x280dc7a60>
  • 动态库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 <LGFramework/LGFramework.h>
#import <AFNetworking.h>

@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: <AFNetworkReachabilityManager: 0x281490760>
2021-03-10 10:20:52.143627+0800 LGApp[2189:437620] App使用静态库AFNetworking的方法:<AFNetworkReachabilityManager: 0x281493d20>
  • 动态库链接静态库时,会把静态库所有代码都链接进去。静态库的导出符号,在动态库中依然是导出符号。所以App可以直接使用

隐藏静态库AFNetworking的全局符号

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

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

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 <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项目

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

  • 项目结构:LGApp(App) -> LGFramework(静态库A) -> AFNetworking(静态库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)
  • 静态库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: <AFNetworkReachabilityManager: 0x28164b920>
【方式二】

手动配置静态库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: <AFNetworkReachabilityManager: 0x28232b040>
静态库链接动态库

搭建LGFramework项目

LGFramework是一个静态库项目,项目中使用Pods导入动态库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项目

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

  • 项目结构:LGApp(App) -> LGFramework(静态库A) -> AFNetworking(动态库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)
  • 静态库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: <AFNetworkReachabilityManager: 0x282363440>
【方式二】

指定头文件路径、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: <AFNetworkReachabilityManager: 0x282a61360>
配置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<libraryname>参数,隐藏静态库A的全局符号(导出符号)

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

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

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

  • 使用PodsApp项目中导入动态库A
  • 指定头文件路径、Framework所在目录、@rpath路径。通过脚本,将动态库A拷贝到App中的Frameworks目录
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容