1.混编
本文所用 Xcode 版本:9.4.1
2.Swift中混编OC
Swift 工程中使用 OC 文件时需要依赖 桥接头文件
将 OC 中的属性、接口等暴露给 Swift。默认情况下,在 Swift 工程中首次创建 OC 文件时,Xcode 会自动提示你是否创建桥接头文件:
选择创建后,工程目录下会多出一个以 “工程名-Bridging-Header.h” 命名的头文件:
可在 “Targets–>Build Settings–>Swift Compiler - General” 查看,一般不用做额外修改:
如果你忽略了 Xcode 的提示,没创建桥接头文件,没关系后面还有机会。稍后你可以按照上面的命名规则自己新建一个头文件,在 “Targets–>Build Settings–>Swift Compiler - General” 中手动设置好 “Objective-C Bridging Header” 选项的路径即可。
接下来就可以在桥接头文件 “HelloSwift-Bridging-Header.h” 中导入你想暴露的 OC 头文件了:
这样,就可以在 Swift 工程中使用刚刚创建的 OC 类OCFile
了,完整示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| typedef NS_ENUM(NSInteger, Direction) { DirectionEast, DirectionWest, DirectionSouth, DirectionNorth, };
@interface OCFile : NSObject
@property (nonatomic, assign) int aInt; @property (nonatomic, strong, nonnull) NSArray *anArr; @property (nonatomic, copy) NSString *str;
- (void)instanceFunction:(Direction)direction; + (void)classFunction;
@end
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| #import "OCFile.h"
@interface OCFile() @property (nonatomic, strong) NSDictionary *aDic; @end
@implementation OCFile
-(instancetype)init { self = [super init]; if (self) { aDouble = 0.001; aInte = 1; _aInt = 2; _anArr = @[@(123)]; _aDic = @{@"k1":_anArr}; } return self; }
- (void)instanceFunction:(Direction)direction { NSLog(@"++++call instance method"); }
+ (void)classFunction { NSLog(@"++++call class method"); }
@end
|
在AppDelegate.swift
中直接使用OC类,不需要再导入其头文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import UIKit
@UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { let ocf = OCFile() ocf.instanceFunction(Direction.east) OCFile.classFunction() print("+++\(ocf.anArr)") return true } }
|
3.OC中混编Swift
OC 工程中使用 Swift 文件需要一个命名格式为 “工程名-Swift.h” 的头文件。它是一个 OC 头文件,包含了工程 Target 里所有 Swift 代码中定义的接口、属性等。有了它 Swift 文件中标记为 open
、public
、internal
的信息才能暴露给 OC 。在编译工程(command+B)后 Xcode 自动生成,不需要自己创建,也不会显示在工程目录中。下面是具体步骤:
在 OC 工程中新建 Swift 文件:“command+N -> swift File”,目录如下:
首次创建 Swift 文件时,Xcode 会提示新建桥接头文件,这里可以不创建。之后 Xcode 会自动设置好 “Objective-C Generated Interface Header Name” 选项,我们不用做什么修改:
自定义你的 Swift 类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import Foundation
class SwiftFile : NSObject { @objc var name : String? @objc let nick : String @objc init(name:String,nick:String) { self.name = name self.nick = nick super.init() } @objc func swiftInstanceMethod(name:String) -> String { self.name = name print("name is:\(name)") return name; } @objc class func swiftClassMethod(nick:String) -> String { print("nick is:\(nick)") return nick } }
|
在用到此 Swift 文件的地方导入 “工程名-swift.h” 头文件即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| #import "AppDelegate.h" #import "ASDF-Swift.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { SwiftFile *sf = [[SwiftFile alloc] initWithName:@"swift" nick:@"sf"]; sf.name = @"Swift4.1"; [sf swiftInstanceMethodWithName:@"Hello"]; [SwiftFile swiftClassMethodWithNick:@"newNick"]; return YES; } @end
|
command + B 编译之后,command + 点击这个ASDF-Swift.h
就能看到其中的内容了。里面是 Xcode 帮我们生成的 SwiftFile.swift
的 OC 版头文件,内容很长,重要内容摘录如下:
1 2 3 4 5 6 7 8
| @interface SwiftFile : NSObject @property (nonatomic, copy) NSString * _Nullable name; @property (nonatomic, readonly, copy) NSString * _Nonnull nick; - (nonnull instancetype)initWithName:(NSString * _Nonnull)name nick:(NSString * _Nonnull)nick OBJC_DESIGNATED_INITIALIZER; - (NSString * _Nonnull)swiftInstanceMethodWithName:(NSString * _Nonnull)name SWIFT_WARN_UNUSED_RESULT; - (nonnull instancetype)init SWIFT_UNAVAILABLE; + (nonnull instancetype)new SWIFT_DEPRECATED_MSG("-init is unavailable"); @end
|
注意事项
OC 中使用 Swift 文件时,Swift类需要继承NSObject
并用@objc
标记需要暴露给OC的元素;并且工程名-Swift.h
文件中只能将 Swift文件中标注为open
、public
、internal
的类、属性和接口等暴露给 OC,而fileprivate
和private
标记的信息对 OC 不可见。Swift 中默认的访问控制修饰符是internal
。
Swift 3.x 中类只要继承了 NSObject,编译器会隐式地为所有public
的属性或方法添加@objc
标注,这样就能把 Swift 的属性、函数等暴露给 OC;但 Swift 4.x 中继承自 NSObject 的类不再隐式添加@objc
,需要手动标注。
Swift类可以继承自 OC 类,但反过来 OC 类不能继承 Swift 类。
Swift中使用 OC 中的宏
时,可以将简单的宏定义成全局常量,复杂的宏可以将其定义成函数。
将 Swift 代码导入 OC 后,你可以访问在 Swift 类或协议中使用@objc
标记的任何对象,只要该对象与 OC 兼容。部分 Swift 独有的特性不能在 OC 中使用,包括:泛型、元组、非Int类型的枚举、可变参数、函数嵌套等。
相关参考:
#©Apple