一个
module是机器代码和数据的最小单位,可以独立于其他代码单位进行链接通常,
module是通过编译单个源文件生成的目标文件。例如:当前的test.m被编译成目标文件test.o时,当前的目标文件就代表了一个module这里有一个问题,
module在调用的时候会产生开销,当使用一个静态库的时:@import TestStaticFramework;如果静态库中包含许多
.o文件。这岂不是会导入很多module?当然不会。在静态链接的时候,也就是静态库链接到主项目或者动态库,最终生成可执行文件或者动态库时,静态链接器可以把多个
module链接优化成一个,来减少本来多个module直接调用的问题
module原理
代码语言:javascript复制未开启
module时,当B文件导入A.h,C文件又导入了A.h和B.h*
#include:A.h会跟随B文件和C文件编译多次。使用#include造成C文件重复包含A.h,所以当C文件编译时,A.h又会被编译多次,相当于编译了`NM次 *#import:A.h依然会跟随B文件和C文件编译多次。但使用#import可以避免C文件重复包含A.h,此时C文件编译,A.h只编译一次,相当于编译了NM`次开启
module时,头文件会被预先编译成二进制文件,并且每个头文件只会被编译一次。此时无论有多少文件导入头文件,都不会被重复编译,只需要执行N次即可
Cat目录中,有A.h和B.h两个头文件,还有一个use.c代码和一个module.modulemap文件。和Cat目录平级,创建prebuilt目录,用来存储编译后的module缓存打开A.h文件,写入以下代码:
#ifdef ENABLE_A
void a() {}
#endif代码语言:javascript复制打开
B.h文件,写入以下代码:
#import "A.h"代码语言:javascript复制打开
use.c文件,写入以下代码:
#import "B.h"
void use() {
#ifdef ENABLE_A
a();
#endif
}代码语言:javascript复制在
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解读
代码语言:javascript复制查看实际开发中使用的
.modulemap文件,例如:AFNetworking打开
AFNetworking.framework中的module.modulemap文件framework module AFNetworking
{
umbrella header "AFNetworking-umbrella.h"
export *
module * { export * }
}代码语言:javascript复制定义
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[];代码语言:javascript复制
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
代码语言:javascript复制搭建
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 *
}
}代码语言:javascript复制如果想对
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代码
代码语言:javascript复制module映射
搭建
LGSwiftFramework项目
LGSwiftFramework是一个Swift动态库项目打开
LGOCStudent.h文件,写入以下代码:
#import <Foundation/Foundation.h>
@interface LGOCStudent : NSObject
- (void)speek;
@end代码语言:javascript复制打开
LGOCStudent.m文件,写入以下代码:
#import "LGOCStudent.h"
@implementation LGOCStudent
- (void)speek {
NSLog(@"LGOCStudent--speek");
}
@end代码语言:javascript复制打开
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!")
}
}代码语言:javascript复制在
LGSwiftTeacher.swift文件中,调用了OC代码。在日常项目中,使用桥接文件即可。但在Framework项目中,没有桥接文件的概念,此时编译报错解决办法:
创建
LGSwiftFramework.modulemap文件,写入以下代码:
framework module LGSwiftFramework {
umbrella "Headers"
export *
}代码语言:javascript复制定义
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
}代码语言:javascript复制但这种导入方式,无法使用
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代码语言:javascript复制另一种解决办法,通过
.modulemap文件,暴露出LGOCStudent:打开
LGSwiftFramework.modulemap文件,改为以下代码:
framework module LGSwiftFramework {
umbrella "Headers"
export *
}
module LGSwiftFramework.LGOCStudent {
header "LGOCStudent.h"
requires objc
}代码语言:javascript复制再次编译项目,使用
@import方式,此时可以找到LGOCStudent导入
LGSwiftFramework.LGOCStudent后,LGOCStudent类可以正常使用
#import "ViewController.h"
@import LGSwiftFramework.LGOCStudent;
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
LGOCStudent *student=[LGOCStudent new];
}
@end代码语言:javascript复制私有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代码语言:javascript复制创建
LGOCStudent_Private.m文件,写入以下代码:
#import "LGOCStudent_Private.h"
@implementation LGOCStudent_Private
- (void)speek {
NSLog(@"LGOCStudent_Private--speek");
}
@end代码语言:javascript复制创建
LGSwiftFramework.private.modulemap文件,写入以下代码:
framework module LGSwiftFramework_Private {
module LGOCStudent {
header "LGOCStudent_Private.h"
export *
}
}代码语言:javascript复制私有
.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];
}
@endSwift静态库
代码语言:javascript复制在
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!")
}
}代码语言:javascript复制搭建
LGSwiftB项目
LGSwiftB是一个Swift静态库项目打开
LGSwiftTeacher.swift文件,写入以下代码:
import Foundation
@objc open class LGSwiftTeacher: NSObject {
public func speek() {
print("speek!")
}
@objc public func walk() {
print("walk!")
}
}代码语言:javascript复制创建
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 usedfor 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'代码语言:javascript复制指定头文件路径
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'代码语言:javascript复制
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代码语言:javascript复制编译成功,
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'代码语言:javascript复制
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方式
代码语言:javascript复制搭建
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代码语言:javascript复制打开
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代码语言:javascript复制搭建
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)indexNS_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将做一些额外的工作,将该方法导入为私有方法,并以双下划线字符__开头代码语言:javascript复制在
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)
}
}代码语言:javascript复制问题解决,
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(模块):最小的代码单元,表示头文件与目标文件的关系undefinedmodulemap:最小的代码单元,表示头文件与目标文件的映射定义一个
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


