上下文
我正在开发一个现有的 Xcode 项目,该项目利用 SPM 管理多个内部包,其中包括在 iOS 应用程序中使用的包。
其中一个内部包包含 Objective-C 代码,这些代码定义了对象的多个扩展并在整个应用程序中导入。
这个包不再被积极开发,所以我想将它嵌入到 iOS 应用程序中以减少干净构建期间的编译器工作量。
目标
以二进制方式链接静态库,该静态库包含在整个现有应用程序中使用的对象扩展,以减少编译时间。
我尝试过的
首先,我开始设置和创建外部项目来存放内部包
- 创建一个新的静态库 Xcode 项目(我们称之为 ExtensionLibrary)。
- 将代码从主应用程序复制到新的 ExtensionLibrary 项目。此代码是用 Objective-C 编写的,由几个定义扩展的 .h 和 .m 文件组成。
- 将扩展的所有头文件导入到 ExtensionLibrary.h 文件中
示例:
#import "SomeExtension.h
- 将所有 .m 文件添加到构建阶段选项卡下的 ExtensionLibrary 目标编译源中。
- 在 ExtensionLibrary 中创建一个新的聚合目标。
- 添加执行以下命令的运行脚本。
- 在 ExtensionLibrary 上为 iphoneos SDK 和 iphonesimulator SDK 运行 xcodebuild
- 使用 lipo 从已完成的构建中创建通用二进制库
在此之后,我继续使用以下过程将其链接到主应用程序:
- 从主应用程序中删除了内部包
- 将通用二进制库 ExtensionLibrary.a 文件和 ExtensionLibrary.h 文件添加到新文件夹中
- 将复制 .a 和 .h 文件的文件夹路径添加到 header 搜索路径标志
- 在应用委托(delegate)头文件中导入复制的扩展库头文件
示例:
#import <ExtensionLibrary.h>
- 将 ExtensionLibary.a 文件添加到 iOS 应用程序的链接二进制文件和库构建阶段
错误
执行此过程后,主应用程序中出现两个主要错误。
- 在 iOS 应用程序中找不到 ExtensionLibrary 头文件中的扩展的导入头文件。
- 这些扩展不适用于任何 UIKit 对象。
附加说明
主 iOS 应用程序设置了其他链接器标志 -ObjC 和 -all_load。
主要的 iOS 应用程序应用委托(delegate)是用 Objective-C 编写的。
主要的 iOS 应用程序是 Swift 和 Objective-C 代码的组合,它们都使用 ExtensionLibrary 中定义的扩展。
使用 ExtensionLibrary 中提供的扩展在主要 iOS 应用程序中重构现有代码的成本会非常高,因此链接库需要尽可能少地更改现有应用程序。
ExtensionLibrary 可能会在未来进行开发,因此对于没有此过程经验的 future 开发人员来说,重新链接库的过程应该很容易重复。
扩展的对象示例:UIApplication、UIView、UIFont、NSDate、NSArray 和 NSError
问题
我的主要问题是如何创建全局可用的 Objective-C 扩展的静态库并将其嵌入到使用 Objective-C 和 Swift 编写的现有 iOS 应用程序中?
最佳答案
坦率地说,根据您提供的步骤,我无法发现任何具体问题。我唯一不同的部分是通用二进制文件,因为我看不出这样做的理由(是的,当你切换到另一个平台时,它会要求你重新编译库,但只需要一次。还有你将拥有“更少脂肪”的最终二进制文件和动态更改其源代码的自由)。让我用下面的说明总结所有步骤:
示例项目
我们的示例工程是Xcode 13为iOS应用在Objective-C中构建的iOS工程模板,文件结构如下:
静态库项目
假设使用的IDE是Xcode,创建一个项目作为另一个项目的一部分的步骤如下:
- 在打开主项目的同时,打开文件/新建/项目菜单
- 选择 iOS/Static Library 模板并进行下一步
- 给项目起个名字(我给了SLProject)然后去下一个 步骤
- 在此步骤中,选择主项目的根文件夹,并确保将主项目指定为添加到:选项的目标
您最终应该得到类似于这样的项目结构:
这不是必需的,但对于静态库,我习惯制作一个“主 header ”,其中包含所有其他库接口(interface)(类似于框架的伞形 header )以及所述库的名称(SLProject. h
),所以我暂时删除了它的实现文件(SLProject.m
)以及 header 本身的内容(没有要包含的内容)。现在,让我们为 UIView 创建一个小类别,它打印出 View 本身和层次结构中的所有其他 View (如果可用):
UIView+PrintHierarchy.h
@import UIKit;
NS_ASSUME_NONNULL_BEGIN
@interface UIView (PrintHierarchy)
- (void)tdw_printHierarchy;
@end
NS_ASSUME_NONNULL_END
UIView+PrintHierarchy.m
#import "UIView+PrintHierarchy.h"
@implementation UIView (PrintHierarchy)
- (void)tdw_printHierarchy {
NSLog(@"%@", self);
if (self.superview) {
[self.superview tdw_printHierarchy];
}
}
@end
接下来,我将此功能包含在我的“主标题”中:
SLProject.h
#import "UIView+PrintHierarchy.h"
并确保所有 header 包含在静态库目标的复制文件阶段:
链接项目
现在转到主要项目目标的构建阶段。
- 首先,您需要在Link Binary With Libraries阶段将主项目与静态库目标链接起来(点击+并找到我们刚刚创建的静态库项目的目标)。<
- 我通常还会在 Dependencies 部分添加我当前项目所依赖的项目
- 由于 a known issue,我们处理编译为静态库一部分的类别方法,不要忘记将
-ObjC
标志添加到 Build Settings 下主项目的目标链接器选项
对于 Objective-C 部分就是这样!从这里您只需在需要的地方导入静态库 header 并使用那里定义的方法:
#import <SLProject/SLProject.h>
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self.view tdw_printHierarchy];
}
@end
链接 Swift 类
为了为 Swift 类提供对相同库的访问,只需将此“主 header ”添加到项目中的桥接 header 即可:
iOSStaticLibDemo-Bridging-Header.h
#import <SLProject/SLProject.h>
现在,所有内容都可用于 Swift 类,无需任何额外导入:
import UIKit
class SwiftView: UIView {
func someMethod() {
tdw_printHierarchy();
}
}
请随意使用我在编写这些说明时创建的项目 here作为引用。
关于ios - 如何将 Objective-C 静态扩展库链接/嵌入到 Xcode 13 项目中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73310869/