1.@objc
将Swift中的元素暴露给OC运行时,以便在OC中调用Swift代码。
常见的,如Swift中给按钮添加点击事件时,需要给selector添加@objc标记。因为SEL是OC运行时中的特性,runtime根据SEL查找函数的实现。要将按钮回调函数暴露给OC运行时,需要将其标记为@objc
,从而让OC运行时进行消息派发。
1 2 3 4 5 6 7 8 9 10 11
| class A: UIViewController { override func viewDidLoad() { let btn = UIButton.init(type: .custom) btn.addTarget(self, action: #selector(btnCallback), for: .touchUpInside) } @objc func btnCallback(){ } }
|
OC工程中调用Swift代码时,也需要将Swift代码标记为@objc:
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 34 35 36 37 38 39 40 41 42 43
| 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 } }
#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
|
2.@objcMember
标记Swift中的类,将其所有元素都暴露给OC,等同于为所有元素加上@objc
。
注意:这些元素不包括Swift独有而OC中没有与之对应的元素类型,如Tuple。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| @objcMembers class Animal : NSObject { func eat() { } func drink() -> (Int, Int)? { return nil } } extension Animal { func call() { } } class Cat : Animal { func miao() { } } extension Cat { func paw() { } }
|
为何有这个关键字呢?Swift3及其之前版本中继承自NSObject的类,编译器会给所有的非private类及其成员加上@objc。swift4之后苹果不再默认这么做,但提供了@objcMember关键字作为替代方案,给被其标记的类及其成员添加@objc关键字。
3.dynamic
标记类的成员,使其属性方法等进行动态派发。
被标记为@objc的元素不一定会变成动态派发,Swift依然可能会将其优化为静态调用。如果在Swift中确实需要用到动态派发机制,比如KVO或者方法交换等特性时,则需要将其标记为 dynamic。
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 34
| class People: NSObject { @objc dynamic var name = "" @objc dynamic var age = 20 }
class ViewController: UIViewController {
let p = People.init() override func viewDidLoad() { p.addObserver(self, forKeyPath: #keyPath(People.name), options: [.new], context: nil) p.addObserver(self, forKeyPath: "age", options: [.new], context: nil) } override func viewWillAppear(_ animated: Bool) { p.name = "kitt" p.age = 21 } override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { if keyPath == "name" { print("new Name:\(change![NSKeyValueChangeKey.newKey] ?? "Default")") } else if keyPath == "age" { print("new age:\(change![NSKeyValueChangeKey.newKey] ?? "Default")") } }
deinit { print("Deinit") p.removeObserver(self, forKeyPath: "name") } }
|
如果People类中去掉dynamic关键字,则给属性赋值时p.age = 21
不会触发监听的回调。