ios - 如何将 Objective-C 静态扩展库链接/嵌入到 Xcode 13 项目中?

标签 ios swift objective-c xcode objective-c-category

上下文

我正在开发一个现有的 Xcode 项目,该项目利用 SPM 管理多个内部包,其中包括在 iOS 应用程序中使用的包。

其中一个内部包包含 Objective-C 代码,这些代码定义了对象的多个扩展并在整个应用程序中导入。

这个包不再被积极开发,所以我想将它嵌入到 iOS 应用程序中以减少干净构建期间的编译器工作量。

目标

以二进制方式链接静态库,该静态库包含在整个现有应用程序中使用的对象扩展,以减少编译时间。

我尝试过的

首先,我开始设置和创建外部项目来存放内部包

  1. 创建一个新的静态库 Xcode 项目(我们称之为 ExtensionLibrary)。
  2. 将代码从主应用程序复制到新的 ExtensionLibrary 项目。此代码是用 Objective-C 编写的,由几个定义扩展的 .h 和 .m 文件组成。
  3. 将扩展的所有头文件导入到 ExtensionLibrary.h 文件中 示例:#import "SomeExtension.h
  4. 将所有 .m 文件添加到构建阶段选项卡下的 ExtensionLibrary 目标编译源中。
  5. 在 ExtensionLibrary 中创建一个新的聚合目标。
  6. 添加执行以下命令的运行脚本。
  • 在 ExtensionLibrary 上为 iphoneos SDK 和 iphonesimulator SDK 运行 xcodebuild
  • 使用 lipo 从已完成的构建中创建通用二进制库

在此之后,我继续使用以下过程将其链接到主应用程序:

  1. 从主应用程序中删除了内部包
  2. 将通用二进制库 ExtensionLibrary.a 文件和 ExtensionLibrary.h 文件添加到新文件夹中
  3. 将复制 .a 和 .h 文件的文件夹路径添加到 header 搜索路径标志
  4. 在应用委托(delegate)头文件中导入复制的扩展库头文件 示例:#import <ExtensionLibrary.h>
  5. 将 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工程模板,文件结构如下:

enter image description here

静态库项目

假设使用的IDE是Xcode,创建一个项目作为另一个项目的一部分的步骤如下:

  1. 在打开主项目的同时,打开文件/新建/项目菜单 enter image description here
  2. 选择 iOS/Static Library 模板并进行下一步 enter image description here
  3. 给项目起个名字(我给了SLProject)然后去下一个 步骤
  4. 在此步骤中,选择主项目的根文件夹,并确保将主项目指定为添加到:选项的目标 enter image description here

您最终应该得到类似于这样的项目结构:

enter image description here

这不是必需的,但对于静态库,我习惯制作一个“主 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 包含在静态库目标的复制文件阶段:

enter image description here

链接项目

现在转到主要项目目标的构建阶段

  1. 首先,您需要在Link Binary With Libraries阶段将主项目与静态库目标链接起来(点击+并找到我们刚刚创建的静态库项目的目标)。<
  2. 我通常还会在 Dependencies 部分添加我当前项目所依赖的项目 enter image description here
  3. 由于 a known issue,我们处理编译为静态库一部分的类别方法,不要忘记将 -ObjC 标志添加到 Build Settings 下主项目的目标链接器选项 enter image description here

对于 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/

相关文章:

ios - 当我滚动 tableview 时,UITableViewCell 会更改数据和设计

ios - Theos实例方法调用

ios - 程序化 NSLayoutConstraint 动画问题

ios - 不确定如何使用 Objective-C 在 iOS 中调用应用内购买的购买流程

ios - MKMapView:偏移贴图使标注可见

iphone - 有没有办法限制仅将电子邮件发送到预定义的目的地?

objective-c - 对象值在方法之间传递

swift - 正确释放嵌入在 subview 中的 NSViewController

ios - (setValue :) Cannot store object of type _SwiftValue at pictureURL. 只能存储NSNumber, NSString, NSDictionary, and NSArray类型的对象

ios - 当代理质询提供带有 Proxy-Authenticate header 的 407 响应时,弹出代理身份验证对话框