Swift3、4中的@objc、@objcMembers和dynamic

Natai發表於2019-04-17

背景

Objective-C 物件是基於執行時的,方法或屬性使用動態派發 ,在執行呼叫時再決定實際呼叫的具體實現。而 Swift 為了追求效能,如果沒有特殊需要的話,是不會在執行時再來決定這些的。也就是說,Swift 型別的成員或者方法在編譯時就已經決定,而執行時便不再需要經過一次查詢,而可以直接使用。

Objective-C 中所有類都繼承自NSObject,Swift 中的類如果要供 Objective-C 呼叫,必須也繼承自NSObject

@objc

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

  2. 新增@objc修飾符並不意味著這個方法或者屬性會採用 Objective-C 的方式變成動態派發,Swift 依然可能會將其優化為靜態呼叫

  3. @objc 修飾符的隱式新增:

    • Swift 3 中繼承自NSObject的類,不需要手動新增@objc,編譯器會給所有的非private的類和成員加上@objcprivate介面想要暴露給 Objective-C 需要@objc的修飾

      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推斷。

@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推斷。

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

相關文章