在Swift 中使用Objective-C
橋接文件
橋接文件是一個在Swift中調用OC類或方法的通道卿城。Swift在同模塊內(nèi)文件是可以相互調用的(不能模塊之間調用是需要引入模塊的)贴汪,但是OC文件是需要引入頭文件才能使用溉知。
在Swift中首次創(chuàng)建OC文件的時候纳寂,xcode會彈出一個窗口优构,詢問是否要創(chuàng)建橋接文件。橋接文件默認的命名為 “項目名-Bridging-Header.h”芙沥。只需要將OC 的頭文件在橋接文件中#import一下就可以在Swift中使用玫膀。
如果在詢問是否創(chuàng)建橋接文件的時候,沒有選擇創(chuàng)建斯辰,或者不小心將橋接文件刪除了舶担,我們也可以在 Targets-->Build Settings-->Swift Compiler - General-->Objective-C Bridging Header中 手動配置。創(chuàng)建一個頭文件椒涯,文件的命名可以按照自己習慣命名柄沮,并不一定需要按照默認橋接文件的命名方式,創(chuàng)建完成后只需要在Targets-->Build Settings-->Swift Compiler - General-->Objective-C Bridging Header中 配置橋接頭文件的路徑就可以了废岂。
NS_ASSUME_NONNULL_BEGIN 和 NS_ASSUME_NONNULL_END的作用
在Swift中祖搓,變量是有可選(optional)和非可選(non-optional)之分的,但是在OC中并沒有對一個變量是否是nil進行區(qū)分湖苞,這就造成一個問題拯欧,在 Swift與Objective-C混編時,Swift編譯器并不知道一個Objective-C對象到底是optional
還是non-optional
财骨,這種情況下镐作,編譯器會默認的將OC對象當成是非可選。
為了解決這個問題隆箩,蘋果在Xcode 6.3引入了一個Objective-C的新特性:nullability annotations该贾。這一新特性的核心是兩個新的類型注釋: __nullable 和 __nonnull 。從字面上我們可以猜到捌臊,__nullable
表示對象可以是NULL
或nil
杨蛋,而__nonnull
表示對象不應該為空。當我們不遵循這一規(guī)則時,編譯器就會給出警告逞力。
但是每個屬性或者方法返回值或者參數(shù)都去指定是否nullable曙寡,實在是太繁瑣了,NS_ASSUME_NONNULL_BEGIN 和 NS_ASSUME_NONNULL_END就是為了簡化這種操作而誕生的寇荧,在這兩個宏之間的代碼举庶,所有簡單指針對象都被假定為nonnull,因此我們只需要去指定那些nullable的指針揩抡。
//Person.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Person : NSObject
@property(nonatomic,copy,nullable)NSString *name;
@property(nonatomic,copy)NSString *sex;
-(instancetype)initWithName:(NSString *)name sex:(NSString *)sex;
@end
NS_ASSUME_NONNULL_END
//Person.m
#import "Person.h"
@implementation Person
-(instancetype)initWithName:(NSString *)name sex:(NSString *)sex{
if (self = [super init]) {
self.name = name;
self.sex = sex;
}
return self;
}
@end
let per = Person.init(name: "小明", sex: "男")
print(per.name)
print(per.sex)
//輸出:
//Optional("小明")
//男
Swift 中如何使用C++
在OC文件是直接可以使用C語言的户侥,使用C++,只需要將.m文件后綴名改為.mm就可以使用C++的語法了捅膘。
在Swift中也是可以使用C語言的添祸,具體使用Swift和C語言的混合使用
但是對與C++的使用相對就麻煩很多,一般采用OC對象包裹的形式去使用C++的對象寻仗。在Swift項目中OC頭文件中是不能引入C++的,否則的會報file not found 的錯誤凡壤,我們需要在.mm文件中使用C++對象
在Objective-C中使用Swift
相對與在Swift 中使用Objective-C來說署尤,在Objective-C中使用Swift要復雜很多。
如何將Swift文件引入到OC項目中
第一次在OC項目中創(chuàng)建Swift文件時亚侠,xcode也是會提醒創(chuàng)建橋接文件的曹体,這個文件的作用也是給Swift文件中調用OC使用的.
-
在OC文件中使用當前項目的Swift文件
我們可以在 Targets-->Build Settings-->Swift Compiler - General-->Objective-C Generated Interface Header Name 看到我們需要在OC中將引入Swift內(nèi)容的頭文件。這個文件默認是 **"項目名-Swift.h" ** ,這個頭文件也可以改成我們自定義的名稱硝烂。只需要在OC中#import這個頭文件就可以了箕别。這種方式是
#import "ProductModuleName-Swift.h"
-
在OC文件中使用當前其他Framework中的Swift文件
ProductModuleName-Swift.h和上面的一樣,只需要在前面加上個項目名就可以使用其他framework中的swift文件
#import <ProductName/ProductModuleName-Swift.h>
在OC使用Swift需要注意什么
Objective-C 對象是基于運行時的滞谢,在運行調用時再決定實際調用的具體實現(xiàn)串稀。而 Swift 為了追求性能,通常情況下是不會在運行時再來決定這些的狮杨。也就是說母截,Swift 類型的成員或者方法在編譯時就已經(jīng)決定,而運行時便不再需要經(jīng)過一次查找橄教。Objective-C 中所有類都繼承自NSObject
清寇,為了能在OC中調用 Swift 中的類,Swift也必須繼承NSObject护蝶。
首先我們需要先了解@objc华烟、@objcMembers和dynamic這三個關鍵字的含義
@objc Swift @objc 關鍵字文檔
@objc
修飾符是用來暴露接口給 Objective-C 的運行時(類、協(xié)議持灰、屬性和方法等盔夜。@nonobjc
修飾符不暴露接給 Objective-C 的運行時,通常可以是使用了@objcMembers修飾了類比吭,但是某些方法不想在OC中使用添加
@objc
修飾符并不意味著這個方法或者屬性會采用 Objective-C 的方式變成動態(tài)派發(fā)绽族,Swift 依然可能會將其優(yōu)化為靜態(tài)調用-
@objc
修飾符的隱式添加的情況-
覆重寫一個@objc聲明的計算屬性或者方法
import Foundation class Person: NSObject { @objc var name :String{ return "小明" } @objc var sex:String = "男" @objc func demo(){ } } class Student: Person { override var name: String{ return "" } //Cannot override with a stored property 'sex' //子類不能重寫父類的存儲屬性 //override var sex: String = "男" override func demo() { } }
-
實現(xiàn)一個使用@objc修飾協(xié)議
@objc protocol MyProtocol { func myFun() } class Person: MyProtocol { func myFun() { } }
當屬性聲明為@IBAction或者@IBOutlet
當屬性聲明為@NSManaged
-
@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 (Swift4以后)
- 需要特別指明
dynamic
不再隱式添加@objc
class MyClass {
dynamic func foo() { } // error: 'dynamic' method must be '@objc'
@objc dynamic func bar() { } // okay
}
- NSObject的子類將不再隱式添加@objc
class MyClass : NSObject {
func foo() { } //在OC不能使用
}
即遵循 Swift API Design Guidelines 的代碼將使用的Swift名稱衩藤,這些名稱通常會轉換為非常差的Objective-C名稱吧慢,從而違反了針對Objective-C Coding Guidelines for Cocoa.
class MyNumber : NSObject {
init(_ int: Int) { }
init(_ double: Double) { } // error: initializer 'init' with Objective-C selector 'init:'
// conflicts with previous declaration with the same Objective-C selector
}
class MyNumber : NSObject {
@objc(initWithInteger:) init(_ int: Int) { }
@objc(initWithDouble:) init(_ double: Double) { }
}
@objc(Student)// Only classes that inherit from NSObject can be declared @objc
class Person: NSObject {
}
class Person: NSObject {
@objc(age) var name = ""
}
@objcMembers
@objcMembers只能用來修飾類。使用@objcMembers修飾的類赏表,系統(tǒng)會在自動給這個類的所有方法添加@objc检诗,暴露給OC。
dynamic (使用dynamic修飾的屬性,在Swift4之后是不會自動添加上@objc的)
dynamic修飾的屬性和方法是告訴編譯器使用動態(tài)分發(fā)而不是靜態(tài)分發(fā)瓢剿,使用動態(tài)分發(fā)逢慌,您可以更好的與OC中runtime的一些特性(如CoreData,KVC/KVO)進行交互间狂。
在Swift中使用KVO
class Person: NSObject {
@objc dynamic var name = "" //@objc在Swift4之后必須要添加
func demo() {
addObserver(self, forKeyPath: #keyPath(Person.name), options: [.initial,.new], context: nil)
}
}