Swift三、4中的@objc、@objcMembers和dynamic

背景

Objective-C 對象是基於運行時的,方法或屬性使用動態派發 ,在運行調用時再決定實際調用的具體實現。而 Swift 爲了追求性能,若是沒有特殊須要的話,是不會在運行時再來決定這些的。也就是說,Swift 類型的成員或者方法在編譯時就已經決定,而運行時便再也不須要通過一次查找,而能夠直接使用。git

Objective-C 中全部類都繼承自NSObject,Swift 中的類若是要供 Objective-C 調用,必須也繼承自NSObjectgithub

@objc

  1. @objc修飾符的根本目的是用來暴露接口給 Objective-C 的運行時(類、協議、屬性和方法等)express

  2. 添加@objc修飾符並不意味着這個方法或者屬性會採用 Objective-C 的方式變成動態派發,Swift 依然可能會將其優化爲靜態調用swift

  3. @objc 修飾符的隱式添加:app

    • Swift 3 中繼承自NSObject的類,不須要手動添加@objc,編譯器會給全部的非private的類和成員加上@objcprivate接口想要暴露給 Objective-C 須要@objc的修飾ide

      button.addTarget(self, action: #selector(backButtonTapped), for: .touchUpInside)
      @objc private func backButtonTapped() { }
      func backButtonTapped() { }
      複製代碼
    • Swift 4 中繼承自NSObject的類的隱式@objc自動添加,只會發生在如下四種狀況:性能

      • 重寫了父類的 Objective-C 方法
      • 實現了一個 Objective-C 的協議
      • @IBAction@IBOutlet關鍵字的修飾
      • @NSManaged關鍵字的修飾
  4. 使用@objc能夠修改 Swift 接口暴露到 Objective-C 後的名字優化

    @objc(Squirrel)
    class Белка: NSObject {
        @objc(color)
        var цвет: Цвет = .Красный
    
        @objc(hideNuts:inTree:)
        func прячьОрехи(количество: Int, вДереве дерево: Дерево) { }
    }
    複製代碼

@objcMembers

Swift4 後繼承自NSObject的類再也不隱式添加@objc關鍵字,但在某些狀況下很是依賴 Objective-C 的運行時(如 XCTest),因此在 Swift4 中提供了@objcMembers關鍵字,對類和子類、擴展和子類擴展從新啓用@objc推斷。ui

@objcMembers
class MyClass : NSObject {
  func foo() { }             // implicitly @objc

  func bar() -> (Int, Int)   // not @objc, because tuple returns
      // aren't representable in Objective-C
}

extension MyClass {
  func baz() { }   // implicitly @objc
}

class MySubClass : MyClass {
  func wibble() { }   // implicitly @objc
}

extension MySubClass {
  func wobble() { }   // implicitly @objc
}
複製代碼

使用@objc@nonobjc能夠指定開啓或關閉某一extension中的全部方法的@objc推斷。this

class SwiftClass { }

@objc extension SwiftClass {
  func foo() { }            // implicitly @objc
  func bar() -> (Int, Int)  // error: tuple type (Int, Int) not
      // expressible in @objc. add @nonobjc or move this method to fix the issue
}

@objcMembers
class MyClass : NSObject {
  func wibble() { }    // implicitly @objc
}

@nonobjc extension MyClass {
  func wobble() { }    // not @objc, despite @objcMembers
}
複製代碼

dynamic

當前 Swift 的動態性依賴於 Objective-C,Swift3 中dynamic就隱式包含了@objc的意思,但考慮到之後版本的 Swift 語言和運行時將會自支持dynamic而再也不依賴於 Objective-C,因此在 Swift4 中將dynamic@objc含義進行了抽離。

class MyClass {
  dynamic func foo() { }       // error: 'dynamic' method must be '@objc'
  @objc dynamic func bar() { } // okay
}
複製代碼

參考:Limiting @objc inference

相關文章
相關標籤/搜索