一 概述
- 背景
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的模式;