swift小知识点之@objc

一,概述

  • 背景

    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 的消息机制调用

        override 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?  
        }
    • e. 若扩展名前加@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的模式;

posted on 2021-07-14 13:29  梁飞宇  阅读(3361)  评论(0)    收藏  举报