Using Swift with Cocoa and Objective-C 官方文檔
注:不是為了混編而混編。混編只是在對開發資源、項目管理和技術發展趨勢進行綜合衡量之後做出的比較合理的選擇。
創建工程,Language 選擇 Swift 或 Objective-C 都可以。
創建 Swift 文件並添加 bridging header 文件
添加 Swift 文件時 Xcode 會自動提示你添加 bridging header 文件,選擇 Yes 即可
進行兩處關鍵設置
這兩處設置 Xcode 默認都會設置好,可以把 Objective-C Bridging Header 和 Objective-C Generated Interface Header Name 改成自己想設置的名字。
至此Swift 與 Objective-C 混編的環境就算配置完成了。
如果需要在 Swift 中使用 OC 的代碼或者庫,只需要在這個文件中 import
相應代碼或者庫的頭文件即可。
和 XXX-Bridging-Header.h 不同,XXX-Swift.h 文件不會出現在項目中,而是由 Xcode 自動生成,你可以在類似如下的路徑下找到相應項目的 XXX-Swift.h 文件:(PS:演講時沒有寫到PPT裡面,實在抱歉)
/Users/perry/Library/Developer/Xcode/DerivedData/XXX-bhlzdinkujybftbjmgwjwclndmss/Build/Intermediates/XXX.build/Debug-iphonesimulator/XXX.build/Objects-normal/x86_64/XXX-Swift.h
如果需要在 OC 中使用 Swift 代碼,在使用的文件中 #import XXX-Swift.h
(PS:其他一些在 OC 中使用 Swift 代碼的注意事項會在後面詳細說明)
查看 XXX-Swift.h 文件中的代碼:
不難發現這個文件中的內容其實是將 Swift 中的代碼轉換成了 OC 的代碼。
注:如果對項目進行清理操作,這個文件也會被刪除,而且在重新構建的過程中,只有在所有的 Swift 代碼都編譯通過的情況下才會重新生成這個文件。
Swift 編譯器不包含預處理器。取而代之的是,它充分利用了編譯時屬性,生成配置,和語言特性來完成相同的功能。所以對於上述類似的宏定義,建議用方法重新封裝一次使用。
因為 Swift 不能使用 #define
,而 OC 可以,所以你可能會在 OC 中定義一個和 Swift 中類同名字的宏,如果你從來沒有在 OC 中使用 Swift 代碼提供的功能(也就是從來沒有 #import XXX-Swift.h
),編譯時不會有任何問題,但是如果一旦使用了,就會報如下的錯誤:
這裡是因為我使用 #define MView (@MView)
將 MView
定義成了 NSString
類型的宏,而在 Swift 中 MView
是 UIView
的子類,在 #import MDemo-Swift.h
之後, MView
就被重復定義了,從而導致錯誤。
更新:如果 OC 中的類和 Swift 中的類同名,也符合上述情況。
.value
@objc
修飾符 枚舉的類型必須是 Int
@objc
修飾符 如果 Swift 類中的成員需要支持 KVC/KVO,使用 dynamic
修飾符
Swift 中沒有 IBOutletCollection
,而是如下的方式實現 IBOutletCollection
:
@IBOutlet var labels: [UILabel]!
這個 IBOutlet
在 xib/Storyboard 中的情況如下:
需要注意的是:和 OC 的 IBOutletCollection
不同,labels
中的元素不一定是按照 black,white,blue,green 的順序排列!
OC 中可能會碰到 A 類頭文件需要包含 B 類頭文件,B 類頭文件同時也需要包含 A 類頭文件的情況,這個時候用 @class
即可解決問題。但是當 OC 中的 A 類頭文件需要包含 XXX-Swift.h
,而 XXX-Bridging-Header.h
中又 import
A類頭文件,這個時候就比較尴尬了。看下面的例子:
上面例子的情況是這樣的:
首先,我們有兩個 Swift 類 MManager
和 MData
。 一個 OC 類 ViewController
。
然後,MManager
類中的一個方法需要傳入 ViewController
類實例的句柄,然後把一個 MData
類實例賦值給 ViewController
類實例中的成員變量 mData
(這個 mData
的類型是 MData
)。
在這種場景下,就會出現重復包含,因為已經有了 MManager
類中的一個方法需要傳入 ViewController
類實例的句柄 這樣一個條件,所以我們必須在 XXX-Bridging-Header.h
中 #import ViewController.h
,這樣我們就只能通過在 ViewController.m 文件中去 #import XXX-Swift.h
的方式來避免重復包含。此時我們需要將成員變量 mData
定義為 id
類型,當在ViewController.m 文件中具體用的時候再將其強制轉換為 MData
類型。如下:
即可解決重復包含的問題。
這樣的處理方式存在的問題是成員變量 mData
可以接受任何類型,所以我們在使用的時候要判斷一下 mData
是否是我們需要的數據類型,這裡我們介紹一下怎麼判斷 Swift 的 AnyObject
和 OC 的 id
是什麼類型:
OC:
if ([self.mData isKindOfClass:[MData class]]) { // 如果 self.mData 不是 MData 類型,判斷不成功
}
Swift:
if let data = self.mData as? MData { // 如果 self.mData 不是 MData 類型,判斷不成功
}
如果 OC 的類導入到 Swift 中使用,類的 properties 會有如下變化:
Swift 中的屬性都是noatomic
的,所以 OC 類中的 atomic
將會失效 OC 類中重寫的 getter/setter
方法都將失效
相比 Swift 1.2,Swift 2 強制要求將在本方法體中值不會改變的量聲明為常量,所以在編寫 Swift 1.2 的代碼時,可以提前注意這一點,從而減小轉換成本。
Swift 2 中這個方法被刪除,不要使用。
因為有變化,所以建議用 for / for…in
代替
從 Swift 2 開始,我們可以對協議進行擴展,從此正式開啟了 Swift 的面向協議編程時代。因為目前還沒有在項目中具體使用,所以研究不是很深,大家可以參考下面三篇譯文:
Swift 2:面向協議編程
如何正確使用協議
Swift 2.0 中的面向協議的MVVM