Swift和OC混編時, 關於@objc的作用


Objective-C 和 Swift 在底層使用的是兩套完全不同的機制,Cocoa 中的 Objective-C 對象是基於運行時的,它從骨子里遵循了 KVC (Key-Value Coding,通過類似字典的方式存儲對象信息) 以及動態派發 (Dynamic Dispatch,在運行調用時再決定實際調用的具體實現)。而 Swift 為了追求性能,如果沒有特殊需要的話,是不會在運行時再來決定這些的。也就是說,Swift 類型的成員或者方法在編譯時就已經決定,而運行時便不再需要經過一次查找,而可以直接使用。

顯而易見,這帶來的問題是如果我們要使用 Objective-C 的代碼或者特性來調用純 Swift 的類型時候,我們會因為找不到所需要的這些運行時信息,而導致失敗。解決起來也很簡單,在 Swift 類型文件中,我們可以將需要暴露給 Objective-C 使用的任何地方 (包括類,屬性和方法等) 的聲明前面加上@objc 修飾符。注意這個步驟只需要對那些不是繼承自 NSObject 的類型進行,如果你用 Swift 寫的 class 是繼承自 NSObject 的話,Swift 會默認自動為所有的非 private 的類和成員加上 @objc。這就是說,對一個 NSObject 的子類,你只需要導入相應的頭文件就可以在 Objective-C 里使用這個類了。

@objc 修飾符的另一個作用是為 Objective-C 側重新聲明方法或者變量的名字。雖然絕大部分時候自動轉換的方法名已經足夠好用 (比如會將 Swift 中類似 init(name: String) 的方法轉換成 -initWithName:(NSString *)name 這樣),但是有時候我們還是期望 Objective-C 里使用和 Swift 中不一樣的方法名或者類的名字,比如 Swift 里這樣的一個類:

class 我的類: NSObject { func 打招呼(名字: String) { print("哈嘍,\(名字)") } } 我的類().打招呼("小明")

Objective-C 的話是無法使用中文來進行調用的,因此我們必須使用 @objc 將其轉為 ASCII 才能在 Objective-C 里訪問:

    @objc(MyClass) class 我的類 { @objc(greeting:) func 打招呼(名字: String) { print("哈嘍,\(名字)") } } 

這樣,我們在 Objective-C 里就能調用 [[MyClass new] greeting:@"XiaoMing"] 這樣的代碼了 (雖然比起原來一點都不好玩了)。

另外,正如上面所說的以及在 Selector 一節中所提到的,即使是NSObject 的子類,Swift 也不會在被標記為 private 的方法或成員上自動加 @objc,以保證盡量不使用動態派發來提高代碼執行效率。

如果我們確定使用這些內容的動態特性的話,我們需要手動給它們加上 @objc 修飾。

但是需要注意的是,添加 @objc 修飾符並不意味着這個方法或者屬性會變成動態派發,Swift 依然可能會將其優化為靜態調用。

如果你需要和 Objective-C 里動態調用時相同的運行時特性的話,你需要使用的修飾符是 dynamic

一般情況下在做 app 開發時應該用不上,但是在施展一些像動態替換方法或者運行時再決定實現這樣的 "黑魔法" 的時候,我們就需要用到 dynamic 修飾符了。

在 KVO 一節中,我們提到了一個關於使用 dynamic 的實例。

關於 Swift 和 Objective-C 混用的一個好消息是,隨着 Swift 的發展,Apple 正在努力改善 SDK。

在 Objective-C 中添加的 nonnull 和 nullable,以及泛型的數組和字典等,其實上都是為了使 SDK 更加適合用 Swift 來使用所做的努力,我們還是很有希望在不久的未來能夠擺脫掉這些妥協和束縛的。

 

本文來自王巍的@OBJC 和 DYNAMIC


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM