一 概述
- 背景
Objective-C 对象是基于运行时的,方法或属性使用动态派发 ,在运行调用时再决定实际调用的具体实现。而 Swift 为了追求性能,如果没有特殊需要的话,是不会在运行时再来决定这些的。也就是说,Swift 类型的成员或者方法在编译时就已经决定,而运行时便不再需要经过一次查找,而可以直接使用。
Objective-C 中所有类都继承自NSObject,Swift 中的类如果要供 Objective-C 调用,必须也继承自NSObject。
- 作用
@objc修饰符的根本目的是用来暴露接口给 Objective-C 的运行时(类、协议、属性和方法等)
添加@objc修饰符并不意味着这个方法或者属性会采用 Objective-C 的方式变成动态派发,Swift 依然可能会将其优化为静态调用
二 @objc
1、Swift 3.0
- a. 编译器默认给继承于NSObject的类的所有方法都隐式添加@objc;缺点是大量@objc导致二进制文件增大;
class MyClass: NSObject { func print() { } // 包含隐式的 @objc }
- b. Swift在某些方面非常的随意亲切,比方说类名,Swift可以使用中文命名,但OC却只能使用ASCII码,在使用@objc的时候,需要指定OC中指定的ASCII码的名称,举个小例子如下。
@objc(MyClass) class 我的类: NSObject { @objc(greeting:) func 问候(名字: String) { print("你好 \(名字)") } }
2、Swift 4.0
a. 隐式添加@objc只存在以下场景:覆盖父类的ObjC方法、符合一个ObjC的协议;
- 覆盖父类的ObjC方法
class Person : NSObject { ///计算属性 @objc var name:String { return "Swift" } ///存储属性 @objc var sex:String = "男" ///方法 @objc func greeting(){ } } class Student:Person { override var name:String { return "" } //Cannot override with a stored property 'sex' //子类不能重写父类的存储属性 //override var sex: String = "男" override func greeting() { } }
- 符合一个ObjC的协议
@objc protocol MyProtocol { func myFun() } class Person:MyProtocol { func myFun() { } }
- 当属性声明为@IBAction或者@IBOutlet
- 当属性声明为@NSManaged
Core Data 提供了基本存储和实现NSManagedObject子类的一组属性。在与Core Data 模型中管理对象子类相关的特性或者关系的每个属性定义之前,将@NSmanaged特性加入。与 Objective-C 里面的 @dynamic特性类似,@NSManaged特性告知 Swift 编译器,这个属性的存储和实现将在运行时完成。但是,与@dynamic不同的是,@NSManaged特性仅在 Core Data 支持中可用。
- @objcMembers 修饰的类,它和它子类的属性方法扩展都会隐式的添加上@objc
为什么要用这个关键字呢?
@objcMembers 在Swift 4中继承 NSObject 的 swift class 不再默认全部 bridge 到 OC,如果我们想要使用的话我们就需要在class前面加上@objcMembers 这么一个关键字。 swift3:在 swift 3 中除了手动添加 @objc 声明函数支持 OC 调用还有另外一种方式:继承 NSObject。class 继承了 NSObject 后,编译器就会默认给这个类中的所有函数都标记为 @objc ,支持 OC 调用。 swift4:苹果在Swift 4 中苹果修改了自动添加 @objc 的逻辑: 一个继承 NSObject 的 swift 类不再默认给所有函数添加 @objc。只在实现 OC 接口和重写 OC 方法时才自动给函数添加 @objc 标识。Swift4 后继承自NSObject的类不再隐式添加@objc关键字,但在某些情况下非常依赖 Objective-C 的运行时(如 XCTest),所以在 Swift4 中提供了@objcMembers关键字,对类和子类、扩展和子类扩展重新启用@objc推断。
- 实例
@objcMembers class MyClass : NSObject { func foo() { } // implicitly @objc func bar() -> (Int, Int){ // not @objc, because tuple returns。aren't representable in Objective-C return (1,1) } } extension MyClass { func baz() { } // implicitly @objc } class MySubClass : MyClass { func wibble() { } // implicitly @objc } extension MySubClass { func wobble() { } // implicitly @objc }
- 若扩展名前加@objc,则该扩展的所有方法都隐式添加@objc
class MyClass { } @objc extension MyClass { func a() { } // 包含隐式的 @objc }
b. 显式给方法添加@objc;
Selector
中调用的方法需要在方法前声明@objc
,目的是允许这个函数在“运行时”通过 Objective-C 的消息机制调用
verride func viewDidLoad() { super.viewDidLoad() let btn = UIButton(type: .contactAdd) btn.addTarget(self, action: #selector(click), for: .touchUpInside) } @objc func click() { print("Button clicked") }
- 协议的方法可选时,协议和可选方法前要用
@objc
声明
@objc protocol OptionalProtocol { @objc optional func optionalMethold1() @objc optional func optionalMethold2() }
- 用weak修饰协议时,协议前面要用
@objc
声明
@objc protocol MyDelegate{ func methold1() } class MyClass{ weak var delegate: MyDelegate? }
c 若扩展名前加@noobjc,则该扩展的所有方法都不会隐式添加@objc(排除类名前加@objcMembers的影响);
@objcMembers class MyClass : NSObject { func print() { } // 包含隐式的 @objc } @nonobjc extension MyClass { func a() { } // 不会包含隐式的 @objc }
d.设置Xcode的Build Settings中的Swift 3 @objc Inference,决定是否继续采用Swift 3.0隐式添加@objc的模式;