module(模块):最小的代码单元一个
module是机器代码和数据的最小单位,可以独立于其他代码单位进行链接通常,
module是通过编译单个源文件生成的目标文件。例如:当前的test.m被编译成目标文件test.o时,当前的目标文件就代表了一个module
这里有一个问题,
module在调用的时候会产生开销,当使用一个静态库的时:@import TestStaticFramework;如果静态库中包含许多
.o文件。这岂不是会导入很多module?当然不会。在静态链接的时候,也就是静态库链接到主项目或者动态库,最终生成可执行文件或者动态库时,静态链接器可以把多个
module链接优化成一个,来减少本来多个module直接调用的问题
module原理
未开启
module时,当B文件导入A.h,C文件又导入了A.h和B.h
#include:A.h会跟随B文件和C文件编译多次。使用#include造成C文件重复包含A.h,所以当C文件编译时,A.h又会被编译多次,相当于编译了N * M次#import:A.h依然会跟随B文件和C文件编译多次。但使用#import可以避免C文件重复包含A.h,此时C文件编译,A.h只编译一次,相当于编译了N + M次
开启
module时,头文件会被预先编译成二进制文件,并且每个头文件只会被编译一次。此时无论有多少文件导入头文件,都不会被重复编译,只需要执行N次即可
Cat目录中,有A.h和B.h两个头文件,还有一个use.c代码和一个module.modulemap文件。和Cat目录平级,创建prebuilt目录,用来存储编译后的module缓存
打开
A.h文件,写入以下代码:#ifdef ENABLE_A void a() {} #endif打开
B.h文件,写入以下代码:#import "A.h"打开
use.c文件,写入以下代码:#import "B.h" void use() { #ifdef ENABLE_A a(); #endif }
- 在
use.c文件中,使用了B.h,同时B.h使用了A.h打开
module.modulemap文件,写入以下代码:module A { header "A.h" } module B { header "B.h" export A }
module.modulemap文件的作用,它是用来描述头文件与module之间映射的关系- 定义了名称为
A和B的两个module- 在
module A中,定义了header A.h,表示module A和A.h的映射关系- 在
module B中,定义了header B.h,和A同理。export A表示将B.h导入的A.h头文件重新导出通过
clang命令,开启module并将use.c编译成目标文件clang -fmodules -fmodule-map-file=module.modulemap -fmodules-cache-path=../prebuilt -c use.c -o use.o
-fmodules:允许使用module语言来表示头文件-fmodule-map-file:module map的路径。此参数缺失,默认找module.modulemap文件。如果文件不存在,执行会报错-fmodules-cache-path:编译后的module缓存路径打开
prebuilt目录,两个.pcm文件,分别对应A.h和B.h,它们就是预编译头文件后的产物
module在Xcode中是默认开启的
如果在
Build Settings中,将Enable Modules设置为NO,导入头文件将不能使用@import方式
开启
module后,项目中导入头文件,无论使用#include、#import、@import中何种方式,最终都会映射为@import方式
module解读
查看实际开发中使用的
.modulemap文件,例如:AFNetworking
打开
AFNetworking.framework中的module.modulemap文件framework module AFNetworking { umbrella header "AFNetworking-umbrella.h" export * module * { export * } }
- 定义
module名称为AFNetworking,模块是frameworkumbrella:可以理解为伞柄。一把雨伞的伞柄下有很多伞骨,umbrella的作用是指定一个目录,这个目录即为伞柄,目录下所有.h头文件即为伞骨umbrella header AFNetworking-umbrella.h:指定module AFNetworking映射AFNetworking-umbrella.h文件中所有.h头文件export *:*表示通配符。将AFNetworking-umbrella.h文件中,所有.h头文件重新导出module * { export * }:创建子module,使用*通配符,将AFNetworking-umbrella.h中导入的头文件,按照头文件名称命名为子module名称。再使用export *将子module中导入的头文件重新导出打开
AFNetworking-umbrella.h文件#ifdef __OBJC__ #import <UIKit/UIKit.h> #else #ifndef FOUNDATION_EXPORT #if defined(__cplusplus) #define FOUNDATION_EXPORT extern "C" #else #define FOUNDATION_EXPORT extern #endif #endif #endif #import "AFNetworking.h" #import "AFHTTPSessionManager.h" #import "AFURLSessionManager.h" #import "AFCompatibilityMacros.h" #import "AFNetworkReachabilityManager.h" #import "AFSecurityPolicy.h" #import "AFURLRequestSerialization.h" #import "AFURLResponseSerialization.h" #import "AFAutoPurgingImageCache.h" #import "AFImageDownloader.h" #import "AFNetworkActivityIndicatorManager.h" #import "UIActivityIndicatorView+AFNetworking.h" #import "UIButton+AFNetworking.h" #import "UIImageView+AFNetworking.h" #import "UIKit+AFNetworking.h" #import "UIProgressView+AFNetworking.h" #import "UIRefreshControl+AFNetworking.h" #import "WKWebView+AFNetworking.h" FOUNDATION_EXPORT double AFNetworkingVersionNumber; FOUNDATION_EXPORT const unsigned char AFNetworkingVersionString[];
AFNetworking-umbrella.h文件,相当于伞柄AFNetworking-umbrella.h文件中,导入的所有.h头文件,相当于伞骨项目中,使用
@import AFNetworking,可以.出一个子module列表,它对应的也是伞柄下的伞骨列表
查看开源项目
AsyncDisplayKit中的module.modulemap打开
module.modulemap文件framework module AsyncDisplayKit { umbrella header "AsyncDisplayKit.h" export * module * { export * } explicit module ASControlNode_Subclasses { header "ASControlNode+Subclasses.h" export * } explicit module ASDisplayNode_Subclasses { header "ASDisplayNode+Subclasses.h" export * } }
- 定义
module名称为AsyncDisplayKit,模块是framework- 定义伞柄
AsyncDisplayKit.h- 将
AsyncDisplayKit.h文件中,所有.h头文件重新导出- 创建子
module,使用*通配符,将AsyncDisplayKit.h中导入的头文件,按照头文件名称命名为子module名称。将子module中导入的头文件重新导出explicit:显示指明子module名称
官方文档
更多
API可查看 官方文档
自定义module
搭建
LGOCFramework项目
LGOCFramework是一个动态库项目,创建项目后,系统默认并不提供.modulemap文件
项目编译后,在
LGOCFramework.framework中的Modules目录下,会自动生成module.modulemap文件
打开
module.modulemap文件,里面存储了基本的头文件与module之间映射的关系/* module.modulemap */ framework module LGOCFramework { // umbrella<目录> umbrella header "LGOCFramework.h" explicit module LGTeacher { header "LGTeacher.h" export * } explicit module LGStudent { header "LGStudent.h" export * } }
如果想对
module进行配置,例如:定义子module,此时需要自己创建modulemap文件在项目
LGOCFramework目录下,创建LGOCFramework.modulemap文件
将
LGOCFramework.modulemap文件加入到项目中
在
BuildSetting中,修改Module Map File配置项:
Module Map File:设置.modulemap文件路径,填写${SRCROOT}之后的路径即可打开
LGOCFramework.modulemap文件,写入以下代码:framework module LGOCFramework { umbrella header "LGOCFramework.h" explicit module LGTeacher { header "LGTeacher.h" export * } explicit module LGStudent { header "LGStudent.h" export * } }
- 定义
module名称为LGOCFramework,模块是framework- 定义伞柄
LGOCFramework.h- 显示指明子
module名称为LGTeacher,映射LGTeacher.h,将LGTeacher.h中导入的头文件重新导出- 显示指明子
module名称为LGStudent,映射LGStudent.h,将LGStudent.h中导入的头文件重新导出项目编译后,在
LGOCFramework.framework中的Modules目录下,生成的依然是名称为module.modulemap的文件
由于系统默认识别
.modulemap文件的名称是module.modulemap,所以自定义的LGOCFramework.modulemap文件在编译后,名称依然是module.modulemap,但里面的内容已经生效
搭建
LGApp项目
LGApp是一个App项目
创建
MulitProject.xcworkspace,加入LGOCFramework动态库项目。LGApp链接LGOCFramework动态库
打开
ViewController.m文件,导入LGOCFramework动态库的头文件,和module中的配置完全一致
至此自定义
module成功
Swift库使用OC代码
module映射
搭建
LGSwiftFramework项目
LGSwiftFramework是一个Swift动态库项目
打开
LGOCStudent.h文件,写入以下代码:#import <Foundation/Foundation.h> @interface LGOCStudent : NSObject - (void)speek; @end打开
LGOCStudent.m文件,写入以下代码:#import "LGOCStudent.h" @implementation LGOCStudent - (void)speek { NSLog(@"LGOCStudent--speek"); } @end打开
LGSwiftTeacher.swift文件,写入以下代码:import Foundation @objc open class LGSwiftTeacher: NSObject { public func speek() { let s = LGOCStudent() s.speek() print("speek!") } @objc public func walk() { print("walk!") } }在
LGSwiftTeacher.swift文件中,调用了OC代码。在日常项目中,使用桥接文件即可。但在Framework项目中,没有桥接文件的概念,此时编译报错
解决办法:
创建
LGSwiftFramework.modulemap文件,写入以下代码:framework module LGSwiftFramework { umbrella "Headers" export * }
- 定义
module名称为LGSwiftFramework,模块是framework- 定义伞柄
Headers目录- 将
Headers目录下所有.h头文件重新导出在
BuildSetting中,修改Module Map File配置项:
Headers目录下的.h头文件
此时
LGSwiftTeacher.swift文件中,使用的OC代码不再报错,项目编译成功
App使用Swift库
承接
自定义module的案例打开
MulitProject.xcworkspace文件,加入LGSwiftFramework动态库项目。LGApp链接LGSwiftFramework动态库
在
LGApp中,打开ViewController.m文件,使用@import LGSwiftFramework导入头文件,只能找到一个.Swift
LGSwiftFramework项目在编译时,系统在.framework中生成的module.modulemap文件,会自动生成以下代码:framework module LGSwiftFramework { umbrella "Headers" export * } module LGSwiftFramework.Swift { header "LGSwiftFramework-Swift.h" requires objc }但这种导入方式,无法使用
LGOCStudent类
解决办法:
使用
#import方式,也无法找到LGOCStudent.h头文件
但
LGSwiftFramework中的.modulemap文件,将Headers目录下所有.h文件全部重新导出。所以可以强行导入<LGSwiftFramework/LGOCStudent.h>,导入后LGOCStudent类可以正常使用#import "ViewController.h" #import <LGSwiftFramework/LGOCStudent.h> @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; LGOCStudent *student=[LGOCStudent new]; } @end
另一种解决办法,通过
.modulemap文件,暴露出LGOCStudent:打开
LGSwiftFramework.modulemap文件,改为以下代码:framework module LGSwiftFramework { umbrella "Headers" export * } module LGSwiftFramework.LGOCStudent { header "LGOCStudent.h" requires objc }再次编译项目,使用
@import方式,此时可以找到LGOCStudent
导入
LGSwiftFramework.LGOCStudent后,LGOCStudent类可以正常使用#import "ViewController.h" @import LGSwiftFramework.LGOCStudent; @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; LGOCStudent *student=[LGOCStudent new]; } @end
私有module映射
在某些情况下,是否使用特定头文件用于区分指定库的
公共API和私有API例如:一个库可能包含分别提供
公共API和私有API的头文件LGOCStudent.h和LGOCStudent_Private.h。此外,LGOCStudent_Private.h可能仅在某些版本的库中可用,而在其他版本库中不可用。使用统一的module.modulemap文件无法表达这一点
LGSwiftFramework项目创建
LGOCStudent_Private.h文件,写入以下代码:#import <Foundation/Foundation.h> @interface LGOCStudent_Private : NSObject - (void)speek; @end创建
LGOCStudent_Private.m文件,写入以下代码:#import "LGOCStudent_Private.h" @implementation LGOCStudent_Private - (void)speek { NSLog(@"LGOCStudent_Private--speek"); } @end创建
LGSwiftFramework.private.modulemap文件,写入以下代码:framework module LGSwiftFramework_Private { module LGOCStudent { header "LGOCStudent_Private.h" export * } }
- 私有
.modulemap文件的名称,中间的.private一定要加,这个是命名规则- 定义
module名称为LGSwiftFramework_Private,模块是framework- 定义私有
module名称,后面一定要加Private后缀,并且首字母大写- 定义
module名称为LGOCStudent,映射LGOCStudent_Private.h- 将
LGOCStudent_Private.h中导入的头文件重新导出在
BuildSetting中,修改Private Module Map File配置项:
LGApp项目打开
ViewController.m文件,导入LGOCStudent.h和LGOCStudent_Private.h头文件,此时它们被彻底分开了#import "ViewController.h" @import LGSwiftFramework.LGOCStudent; @import LGSwiftFramework_Private.LGOCStudent; @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; LGOCStudent *student=[LGOCStudent new]; LGOCStudent_Private *sp=[LGOCStudent_Private new]; } @end
Swift静态库
在
Xcode 9之后,Swift开始⽀持静态库
Swift没有头⽂件的概念,外界如何使⽤Swift中public修饰的类和函数?
Swift库中引⼊了⼀个全新的⽂件.swiftmodule
.swiftmodule包含序列化过的AST(抽象语法树,Abstract Syntax Tree),也包含SIL(Swift中间语⾔,Swift Intermediate Language)
Swift静态库合并
搭建
LGSwiftA项目
LGSwiftA是一个Swift静态库项目
打开
LGSwiftTeacher.swift文件,写入以下代码:import Foundation @objc open class LGSwiftTeacher: NSObject { public func speek() { print("speek!") } @objc public func walk() { print("walk!") } }
搭建
LGSwiftB项目
LGSwiftB是一个Swift静态库项目
打开
LGSwiftTeacher.swift文件,写入以下代码:import Foundation @objc open class LGSwiftTeacher: NSObject { public func speek() { print("speek!") } @objc public func walk() { print("walk!") } }
创建
MulitProject.xcworkspace,加入LGSwiftA、LGSwiftB两个静态库项目
创建
Products目录,和MuiltProject.xcworkspace平级
在
LGSwiftA、LGSwiftB项目中,选择Build Phases,创建Run Script,写入以下代码:cp -Rv -- "${BUILT_PRODUCTS_DIR}/" "${SOURCE_ROOT}/../Products"
- 使用
cp命令,将编译后的.framework文件拷贝到Products目录编译
LGSwiftA、LGSwiftB项目,打开Products目录,.framework文件已成功拷贝
使用
libtool命令,合并LGSwiftA和LGSwiftB两个静态库libtool -static \ -o \ libLGSwiftC.a \ LGSwiftA.framework/LGSwiftA \ LGSwiftB.framework/LGSwiftB由于
LGSwiftA、LGSwiftB项目中,都存在了相同的LGSwiftTeacher.swift文件,使用libtool命令合并后提示警告libtool: warning same member name (LGSwiftTeacher.o) in output file used for input files: LGSwiftA.framework/LGSwiftA(LGSwiftTeacher.o) and: LGSwiftB.framework/LGSwiftB(LGSwiftTeacher.o) due to use of basename, truncation and blank padding使用
ar -t libLGSwiftC.a命令,查看libLGSwiftC.a的文件列表__.SYMDEF LGSwiftA_vers.o LGSwiftTeacher.o LGSwiftB_vers.o LGSwiftTeacher.o如果是
OC动态库,.framework中可以舍弃Modules目录,将两个静态库的头文件拷贝到一起即可但
Swift动态库,包含了x.swiftmodule目录,里面的.swiftmodule文件不能舍弃,此时应该如何处理?
解决办法:
Products目录下,创建LGSwiftC目录,将库文件libLGSwiftC.a拷贝到LGSwiftC目录下
仿照
Cocoapods生成三方库的目录结构,在LGSwiftC目录下,创建Public目录,将LGSwiftA.framework和LGSwiftB.framework拷贝到Public目录下
打开
LGSwiftA.framework和LGSwiftB.framework文件,将里面的库文件、.plist文件、签名等信息全部删除,最终只保留Headers和Modules两个目录
虽然生成
.framework时,自动创建了Modules目录。但编译时,.modulemap文件和x.swiftmodule目录,应该和Headers目录平级将
.modulemap文件和x.swiftmodule目录,从Modules目录移动到.framework文件下,和Headers目录平级。然后删除Modules目录
此时静态库合并完成
App使用合并后的静态库
搭建
LGApp项目
LGApp是一个App项目
将
LGSwiftC目录,拷贝到LGApp项目的根目录下
将
libLGSwiftC.a库文件,拖动到项目中的Frameworks目录
勾选
Copy items if needed,点击Finish
创建
xcconfig文件,并配置到Tatget上,写入以下代码:HEADER_SEARCH_PATHS = $(inherited) '${SRCROOT}/LGSwiftC/Public/LGSwiftA.framework/Headers' HEADER_SEARCH_PATHS = $(inherited) '${SRCROOT}/LGSwiftC/Public/LGSwiftB.framework/Headers'
- 指定头文件路径
Header Search Paths在
ViewController.m中,使用module方式导入LGSwiftA,编译报错
- 使用
module方式,还需要加载modulemap文件的路径打开
xcconfig文件,改为以下代码:HEADER_SEARCH_PATHS = $(inherited) '${SRCROOT}/LGSwiftC/Public/LGSwiftA.framework/Headers' HEADER_SEARCH_PATHS = $(inherited) '${SRCROOT}/LGSwiftC/Public/LGSwiftB.framework/Headers' OTHER_CFLAGS = $(inherited) '-fmodule-map-file=${SRCROOT}/LGSwiftC/Public/LGSwiftA.framework/module.modulemap' OTHER_CFLAGS = $(inherited) '-fmodule-map-file=${SRCROOT}/LGSwiftC/Public/LGSwiftB.framework/module.modulemap'
OTHER_CFLAGS:传递给用来编译C或者OC的编译器,当前就是clang- 加载
modulemap文件的路径- 对应
Build Setting中的配置项
打开
ViewController.m,写入以下代码:#import "ViewController.h" @import LGSwiftA; @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; LGSwiftTeacher *teacher = [LGSwiftTeacher new]; } @end编译成功,
Swift静态库中的LGSwiftTeacher类,可以在OC下正常使用
但此时还有另一个问题:
在
LGSwiftTest.swift中,使用import导入LGSwiftA,还是编译报错
- 在
Swift中,还需要加载swiftmodule文件的路径打开
xcconfig文件,改为以下代码:HEADER_SEARCH_PATHS = $(inherited) '${SRCROOT}/LGSwiftC/Public/LGSwiftA.framework/Headers' HEADER_SEARCH_PATHS = $(inherited) '${SRCROOT}/LGSwiftC/Public/LGSwiftB.framework/Headers' OTHER_CFLAGS = $(inherited) '-fmodule-map-file=${SRCROOT}/LGSwiftC/Public/LGSwiftA.framework/module.modulemap' OTHER_CFLAGS = $(inherited) '-fmodule-map-file=${SRCROOT}/LGSwiftC/Public/LGSwiftB.framework/module.modulemap' SWIFT_INCLUDE_PATHS = $(inherited) '${SRCROOT}/LGSwiftC/Public/LGSwiftA.framework' SWIFT_INCLUDE_PATHS = $(inherited) '${SRCROOT}/LGSwiftC/Public/LGSwiftB.framework'
SWIFT_INCLUDE_PATHS:传递给SwiftC编译器- 在指定路径下查找
swiftmodule文件- 对应
Build Setting中的配置项
打开
LGSwiftTest.swift文件,写入以下代码:import Foundation import LGSwiftA @objc open class LGSwiftTest: NSObject { public override init() { super.init() let t = LGSwiftTeacher() t.speek() } }编译成功,
Swift静态库中的LGSwiftTeacher类,可以在Swift下正常使用
在
LGSwiftA.framework和LGSwiftB.framework两个静态库中,都存在LGSwiftTeacher,有时甚至会存在头文件相同的情况。所以在案例中,手动构建的目录结构,可以有效避免相同头文件的冲突。并且在使用的时候,导入的头文件是谁的,使用的LGSwiftTeacher对应就是谁的链接静态库,只要没指定
-all_load或-ObjC参数,默认会使用-noall_load参数。所以在同一个文件内,即使导入两个头文件,当链接一个文件找到代码后,就不会链接另一个,因此也不会冲突
OC映射到Swift方式
搭建
OCFramework项目
OCFramework是一个OC动态库项目
打开
LGToSwift.h文件,写入以下代码:#import <Foundation/Foundation.h> typedef NS_ENUM(NSUInteger, LGTeacherName) { LGTeacherNameHank, LGTeacherNameCat, }; typedef NSString * LGTeacherNameString; extern NSString *getTeacherName(void); extern NSString * const LGTeacherCat; extern LGTeacherNameString const LGTeacherHank; @interface LGToSwift : NSObject - (nullable NSString *)teacherNameForIndex:(NSUInteger)index; - (BOOL)changeTeacherName:(nullable NSDictionary<NSString *, id> *)options; @end打开
LGToSwift.m文件,写入以下代码:#import "LGToSwift.h" NSString *getTeacherName(void) { return nil; } NSString * const LGTeacherCat = @"Cat"; LGTeacherNameString const LGTeacherHank = @"Hank"; @implementation LGToSwift - (nullable NSString *)teacherNameForIndex:(NSUInteger)pageIndex { return nil; } - (BOOL)changeTeacherName:(nullable NSDictionary<NSString *, id> >*)options { return NO; } @end
搭建
SwiftProject项目
SwiftProject是一个App项目
创建
MulitProject.xcworkspace,加入OCFramework动态库项目。SwiftProject链接OCFramework动态库
在
ViewController.swift中,使用OCFramework动态库的方法,出现以下问题:
- 无法对
LGTeacherNameString类型的属性赋值枚举值teacherName方法的命名,被改为teacherName(for:),但我们预期的是teacherName(forIndex:)changeTeacherName方法,我们希望它作为私有方法,并以双下划线字符__开头
解决办法:
可以使用特定宏,改变映射规则
在
OCFramework中,打开LGToSwift.h文件,改为以下代码:#import <Foundation/Foundation.h> typedef NS_ENUM(NSUInteger, LGTeacherName) { LGTeacherNameHank, LGTeacherNameCat, }; typedef NSString * LGTeacherNameString NS_TYPED_EXTENSIBLE_ENUM; extern NSString *getTeacherName(void); extern NSString * const LGTeacherCat; extern LGTeacherNameString const LGTeacherHank; @interface LGToSwift : NSObject - (nullable NSString *)teacherNameForIndex:(NSUInteger)index NS_SWIFT_NAME(teacherName(forIndex:)); - (BOOL)changeTeacherName:(nullable NSDictionary<NSString *, id> *)options NS_REFINED_FOR_SWIFT; @end
NS_TYPED_EXTENSIBLE_ENUM:属性指示编译器,使用struct(swift_wrapper(struct)属性),通过指定NS_TYPED_ENUM宏,编译器被指示使用enum(swift_wrapper(enum)属性)NS_SWIFT_NAME:通过指定NS_SWIFT_NAME宏,可以添加一些详细信息以使函数清晰可见NS_REFINED_FOR_SWIFT:通过指定NS_REFINED_FOR_SWIFT宏,Swift的Clang Importer将做一些额外的工作,将该方法导入为私有方法,并以双下划线字符__开头在
SwiftProject中,打开ViewController.swift文件,写入以下代码:import UIKit import OCFramework class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let Hank: LGTeacherNameString = .hank let teacher: LGToSwift = LGToSwift() teacher.teacherName(forIndex: 1) } } extension LGToSwift { func change() -> Bool { return __changeTeacherName(nil) } }问题解决,
OC中的方法和属性,在Swift中使用符合预期
但另一个问题又出现了:
通过指定宏的方式,需要修改原有代码。如果一个使用
OC开发的SDK需要适配Swift,需要为每一个方法或属性指定宏,这将是工程浩大且费时费力的事情
解决办法:
使用
.apinotes文件,代替宏的方式在
OCFramework目录下,创建OCFramework.apinotes文件
在
OCFramework中,将OCFramework.apinotes文件加入到项目
.apinotes文件必须要放在SDK的目录中,采用yaml格式书写,类似JSON格式打开
OCFramework.apinotes文件,写入以下代码:--- Name: OCFramework Classes: - Name: LGToSwift # SwiftName: ToSwift Methods: - Selector: "changeTeacherName:" Parameters: - Position: 0 Nullability: O MethodKind: Instance SwiftPrivate: true Availability: nonswift AvailabilityMsg: "prefer 'deinit'"
- 将
changeTeacherName:方法,在Swift中设置为不可用编译项目,显示自定义错误提示:
prefer 'deinit'
.apinotes文件最终会被放入编译后的.framework中
官方文档
更多
API可查看 官方文档
总结
module(模块):最小的代码单元,表示头文件与目标文件的关系
modulemap:最小的代码单元,表示头文件与目标文件的映射定义一个
module:
export:导出当前代表的头文件使用的头文件export *:匹配目录下所有的头文件module *:目录下所有的头文件都当作一个子moduleexplicit *:显式声明一个module的名称
Swift库使用OC代码:
- 不能使用桥接文件
OC的头文件放到modulemap下- 使用私有
modulemap更好的表达公共API和私有API
Swift静态库合并
- 必须保留
.swiftmodule文件(Swift的头文件)- 使用
libtool命令,合并静态库本身- 用到的头文件、
Swift头文件以及modulemap文件,通过目录的形式放到一起OC要用合并的静态库:clang: other c flags:-fmodule-map-file <modulemap path>Swift要用合并的静态库:SwiftC :other swift flags显式告诉SwiftC <modulemap dir>
OC映射到Swift方式
- 宏
- 使用
.apinotes文件:<工程名称>.apinotes






















































