Swift從2014年發(fā)布到現(xiàn)在,馬上接近三年,經(jīng)過蘋果的不斷改進(jìn)和完善,語法方面已經(jīng)趨于穩(wěn)定。如果是新建的項(xiàng)目,嚴(yán)重建議使用Swift,因?yàn)镾wift必定會取代Objective-C。然后對于用Objective-C寫的舊項(xiàng)目,我們有兩個(gè)選擇:1)直接整個(gè)項(xiàng)目用Swift重寫;2)在舊項(xiàng)目的基礎(chǔ)上,新的東西用Swift編寫,然后再把之前用Objective-C寫的代碼慢慢改為Swift。我個(gè)人更偏向于在舊項(xiàng)目的基礎(chǔ)上逐漸把整個(gè)項(xiàng)目轉(zhuǎn)為Swift。下面我將會結(jié)合實(shí)際工作和蘋果的官方文檔《Using Swift with Cocoa and Objective-C (Swift 3.1)》來總結(jié)下如何將舊的Objective-C項(xiàng)目逐漸轉(zhuǎn)為Swift項(xiàng)目。
學(xué)習(xí)Swift
首先,你要懂得Swift(這TMD不是講廢話嗎 ...)。英文能力不錯(cuò)的建議看官方的文檔《The Swift Programming Language (Swift 3.1)》,官方的文檔總是最好的。不嫌棄的話,可以看看我寫的《Swift文集》,總結(jié)了Swift的關(guān)鍵知識點(diǎn)。另外,大家可以看看Swift翻譯組翻譯的內(nèi)容。
Objective-C和Swift的互用
在這部分內(nèi)容里,我將會根據(jù)官方的文檔,總結(jié)下Objective-C和Swift是如何互用的。
初始化
在Objective-C中,類的初始化方法通常是以init或者initWith開頭的。在Swift中使用Objective-C的類時(shí),Swift會把init開頭的方法作為初始化方法,如果是以initWith開頭的,在Swift中調(diào)用時(shí),會把With去掉,例如:
在Objective-C中:
- (instancetype)init;
- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style;
在Swift中調(diào)用上面的接口,就會是下面這種形式:
init() { /* ... */ }
init(frame: CGRect, style: UITableViewStyle) { /* ... */ }
類方法和便利初始化器
在Objective-C的類方法,在Swift中會被作為便利初始化器:
在Objective-C中:
UIColor *color = [UIColor colorWithRed:0.5 green:0.0 blue:0.5 alpha:1.0];
在Swift中,就會是下面這種形式:
let color = UIColor(red: 0.5, green: 0.0, blue: 0.5, alpha: 1.0)
訪問屬性
Objective-C中的屬性將會按照下面這個(gè)規(guī)則來導(dǎo)入Swift:
- 被
nonnull,nullable和null_resettable標(biāo)記的屬性導(dǎo)入Swift之后,會變成optional和nonoptional類型 - 被
readonly標(biāo)記的屬性導(dǎo)入Swift之后,變成計(jì)算屬性 ({ get })。 - 被
weak標(biāo)記的屬性導(dǎo)入Swift之后,同樣是被weak標(biāo)記 (weak var)。 - 被
assign,copy,strong或者unsafe_unretained標(biāo)記的,將會以適當(dāng)?shù)拇鎯?dǎo)入Swift。 - 被
class標(biāo)記的屬性導(dǎo)入Swift之后,變成類型屬性。 - 原子性屬性(
atomic和nonatomic)在對應(yīng)的Swift屬性中沒有反應(yīng)出來,但是在Swift中被訪問的時(shí)候,Objective-C原子性的實(shí)現(xiàn)仍然會保留。 -
getter=和setter=在Swift中被省略。
在Swift中,直接用點(diǎn)語法來訪問Objective-C的屬性。
方法
同樣地,在Swift中也是使用點(diǎn)語法來訪問方法。
當(dāng)Objective-C的方法被導(dǎo)入Swift后,Objective-C的Selector的第一部分會被作為Swift的方法名。例如:
在Objective-C中:
[myTableView insertSubview:mySubview atIndex:2];
導(dǎo)入Swift后:
myTableView.insertSubview(mySubview, at: 2)
id兼容性
Objective-C的id類型,導(dǎo)入Swift之后,成為Swift的Any類型。
Swift還有一個(gè)類型AnyObject,可以代表所有的class類型,它可以動(dòng)態(tài)的搜索任何@objc方法,而無需向下轉(zhuǎn)型。例如:
var myObject: AnyObject = UITableViewCell()
myObject = NSDate()
let futureDate = myObject.addingTimeInterval(10)
let timeSinceNow = myObject.timeIntervalSinceNow
但是我們在運(yùn)行代碼之前,AnyObject的具體類型是不確定的,所以上面這種寫法非常危險(xiǎn)。,例如下面這個(gè)例子,在運(yùn)行的時(shí)候會crash:
myObject.character(at: 5)
// crash, myObject doesn't respond to that method
我們可以使用可選鏈或者if let來解決這個(gè)問題:
// 可選鏈
let myChar = myObject.character?(at: 5)
// if let
if let fifthCharacter = myObject.character?(at: 5) {
print("Found \(fifthCharacter) at index 5")
}
空屬性和可選
我們都知道在Objective-C中,可以使用一些注釋來標(biāo)記屬性、參數(shù)或者返回值是否可以為空,例如_nullable、_Nonull等等。他們會按照下面的規(guī)則來導(dǎo)入Swift:
- 被
_Nonnull標(biāo)記的,在導(dǎo)入Swift之后,會被作為非可選類型 - 被
_Nullable標(biāo)記的,在導(dǎo)入Swift之后,會被作為可選類型 - 沒有被任何注釋標(biāo)記的,在導(dǎo)入Swift之后,會被作為隱式解包可選類型
例如,在Objective-C中:
@property (nullable) id nullableProperty;
@property (nonnull) id nonNullProperty;
@property id unannotatedProperty;
NS_ASSUME_NONNULL_BEGIN
- (id)returnsNonNullValue;
- (void)takesNonNullParameter:(id)value;
NS_ASSUME_NONNULL_END
- (nullable id)returnsNullableValue;
- (void)takesNullableParameter:(nullable id)value;
- (id)returnsUnannotatedValue;
- (void)takesUnannotatedParameter:(id)value;
導(dǎo)入Swift之后:
var nullableProperty: Any?
var nonNullProperty: Any
var unannotatedProperty: Any!
func returnsNonNullValue() -> Any
func takesNonNullParameter(value: Any)
func returnsNullableValue() -> Any?
func takesNullableParameter(value: Any?)
func returnsUnannotatedValue() -> Any!
func takesUnannotatedParameter(value: Any!)
輕量級泛型
在Swift中:
@property NSArray<NSDate *> *dates;
@property NSCache<NSObject *, id<NSDiscardableContent>> *cachedData;
@property NSDictionary <NSString *, NSArray<NSLocale *>> *supportedLocales;
導(dǎo)入Swift之后:
var dates: [Date]
var cachedData: NSCache<AnyObject, NSDiscardableContent>
var supportedLocales: [String: [Locale]]
擴(kuò)展
Swift的擴(kuò)展其實(shí)類似于Objective-C的分類。Swift的擴(kuò)展可以對現(xiàn)有的類、結(jié)構(gòu)和枚舉添加新的成員,即使是在Objective-C中定義的類、結(jié)構(gòu)和枚舉,都可以進(jìn)行擴(kuò)展。
例如下面這個(gè)例子,為UIBezierPath添加一個(gè)便利初始化器,可用來畫一個(gè)等邊三角形:
extension UIBezierPath {
convenience init(triangleSideLength: CGFloat, origin: CGPoint) {
self.init()
let squareRoot = CGFloat(sqrt(3.0))
let altitude = (squareRoot * triangleSideLength) / 2
move(to: origin)
addLine(to: CGPoint(x: origin.x + triangleSideLength, y: origin.y))
addLine(to: CGPoint(x: origin.x + triangleSideLength / 2, y: origin.y + altitude))
close()
}
}
閉包
Objective-C的block,導(dǎo)入Swift之后變?yōu)镃losure。例如在Objective-C中有一個(gè)block:
void (^completionBlock)(NSData *) = ^(NSData *data) {
// ...
}
在Swift中是這樣的:
let completionBlock: (Data) -> Void = { data in
// ...
}
Objective-C的block和Swift的Closure基本上可以說是等價(jià)的,但是有一點(diǎn)不同的是:外部的變量在Swift的Closure中是可變的,我們可以直接在Closure內(nèi)部更新變量的值;而在Objective-C中,需要用__block標(biāo)記變量。
解決Block中的循環(huán)引用問題
在Objective-C中:
__weak typeof(self) weakSelf = self;
self.block = ^{
__strong typeof(self) strongSelf = weakSelf;
[strongSelf doSomething];
};
在Swift中是這樣解決的,[unowned self]被稱為捕獲列表(Capture List):
self.closure = { [unowned self] in
self.doSomething()
}
對象之間的比較
在Swift中,比較兩個(gè)對象是否相等有兩種方法:1) ==:比較兩個(gè)對象的內(nèi)容是否相等;2) ===:比較兩個(gè)常量或者變量是否引用著同一個(gè)對象實(shí)例。
Swift為繼承自NSObject的子類提供了默認(rèn)的==和===實(shí)現(xiàn),并實(shí)現(xiàn)了Equatable協(xié)議。默認(rèn)的==實(shí)現(xiàn)調(diào)用了isEqual:方法,默認(rèn)的===實(shí)現(xiàn)檢查指針是否相等。我們不能重寫從Objective-C導(dǎo)入的類的這兩個(gè)操作符。
Swift類型的兼容性
下面這些Swift特有的類型,是不兼容Objective-C的:
- 泛型
- 元組
- Swift中定義的沒有
Int類型原始值的枚舉 - Swift中定義的結(jié)構(gòu)
- Swift中定義的高階函數(shù)
- Swift中定義的全局變量
- Swift中定義的類型別名
- Swift風(fēng)格的variadics
- 嵌套類型
- Curried functions
Swift轉(zhuǎn)換為Objective-C:
- 可選類型,被
__nullable標(biāo)記 - 非可選類型,被
__nonnull標(biāo)記 - 常量和計(jì)算屬性,變成只讀屬性
- 類型屬性在Objective-C中被
class標(biāo)記 - 類型方法在Objective-C是類方法
- 初始化器和實(shí)例方法變成Objective-C的實(shí)例方法
- 會拋出錯(cuò)誤的方法,在Objective-C中會多了一個(gè)
NSerror **參數(shù)。如果Swift的方法沒有返回值,在Objective-C中會返回一個(gè)BOOL。
例如,在Swift中:
class Jukebox: NSObject {
var library: Set<String>
var nowPlaying: String?
var isCurrentlyPlaying: Bool {
return nowPlaying != nil
}
class var favoritesPlaylist: [String] {
// return an array of song names
}
init(songs: String...) {
self.library = Set<String>(songs)
}
func playSong(named name: String) throws {
// play song or throw an error if unavailable
}
}
轉(zhuǎn)換成Objective-C后:
@interface Jukebox : NSObject
@property (nonatomic, strong, nonnull) NSSet<NSString *> *library;
@property (nonatomic, copy, nullable) NSString *nowPlaying;
@property (nonatomic, readonly, getter=isCurrentlyPlaying) BOOL currentlyPlaying;
@property (nonatomic, class, readonly, nonnull) NSArray<NSString *> * favoritesPlaylist;
- (nonnull instancetype)initWithSongs:(NSArray<NSString *> * __nonnull)songs OBJC_DESIGNATED_INITIALIZER;
- (BOOL)playSong:(NSString * __nonnull)name error:(NSError * __nullable * __null_unspecified)error;
@end
自定義Swift在Objective-C的接口
我們可以使用@objc(name)自定義Swift的類、屬性、方法、枚舉類型或者枚舉case在Objective-C中使用時(shí)的名字。
例如,在Swift中:
@objc(Color)
enum Цвет: Int {
@objc(Red)
case Красный
@objc(Black)
case Черный
}
@objc(Squirrel)
class Белка: NSObject {
@objc(color)
var цвет: Цвет = .Красный
@objc(initWithName:)
init (имя: String) {
// ...
}
@objc(hideNuts:inTree:)
func прячьОрехи(количество: Int, вДереве дерево: Дерево) {
// ...
}
}
Swift還提供了一個(gè)屬性@nonobjc,被這個(gè)屬性標(biāo)記的成員將不能在Objective-C中使用。
需要?jiǎng)討B(tài)調(diào)度
當(dāng)Swift的API被Objective-C runtime使用時(shí),不能保證能動(dòng)態(tài)調(diào)度屬性、方法、下標(biāo)或者初始化器。Swift的編譯器仍然會反虛擬化或者內(nèi)聯(lián)成員訪問來優(yōu)化代碼的屬性,并繞過Objective-C runtime。
我們可以使用dynamic在使用Objective-C runtime時(shí)動(dòng)態(tài)的訪問成員。需要?jiǎng)討B(tài)調(diào)度的情況是非常少的。但是,在Objective-C runtime中使用key-value observing或者method_exchangeImplementations時(shí),我們就需要?jiǎng)討B(tài)調(diào)度,在運(yùn)行的時(shí)候來動(dòng)態(tài)地替換一個(gè)方法的實(shí)現(xiàn)。
注意:使用了dynamic標(biāo)記的聲明,不能再使用@nonobjc。因?yàn)槭褂昧?code>@nonobjc,就意味著不能在Objective-C中使用,而dynamic就是為了給Objective-C使用,這兩個(gè)屬性是完全沖突的。
Selector
在Objective-C中,我們使用@selector來構(gòu)造一個(gè)Selector;而在Swift中,我們要使用#selector
Key和Key Path
在Swift中,可以使用#keyPath來生成編譯器檢查(也就是說編譯的時(shí)候就能知道key和keyPath是否有誤,而不必等到運(yùn)行時(shí)才能確定)的key和keyPath,然后就可以給這些方法使用:value(forKey:)、value(forKeyPath:)、addObserver(_:forKeyPath:options:context:)。#keyPath支持鏈?zhǔn)椒椒ɑ蛘邔傩?,?code>#keyPath(Person.bestFriend.name)。
例如:
class Person: NSObject {
var name: String
var friends: [Person] = []
var bestFriend: Person? = nil
init(name: String) {
self.name = name
}
}
let gabrielle = Person(name: "Gabrielle")
let jim = Person(name: "Jim")
let yuanyuan = Person(name: "Yuanyuan")
gabrielle.friends = [jim, yuanyuan]
gabrielle.bestFriend = yuanyuan
#keyPath(Person.name)
// "name"
gabrielle.value(forKey: #keyPath(Person.name))
// "Gabrielle"
#keyPath(Person.bestFriend.name)
// "bestFriend.name"
gabrielle.value(forKeyPath: #keyPath(Person.bestFriend.name))
// "Yuanyuan"
#keyPath(Person.friends.name)
// "friends.name"
gabrielle.value(forKeyPath: #keyPath(Person.friends.name))
// ["Yuanyuan", "Jim"]
Cocoa Frameworks
Swift能自動(dòng)地將一些類型在Swift和Objective-C之間互相轉(zhuǎn)換。例如我們可以傳一個(gè)String值給NSString參數(shù)。
Foundation
橋接類型
Swift Foundation提供了下列橋接值類型:
| Objective-C引用類型 | Swift值類型 |
|---|---|
| NSAffineTransform | AffineTransform |
| NSArray | Array |
| NSCalendar | Calendar |
| NSCharacterSet | CharacterSet |
| NSData | Data |
| NSDateComponents | DateComponents |
| NSDateInterval | DateInterval |
| NSDate | Date |
| NSDecimalNumber | Decimal |
| NSDictionary | Dictionary |
| NSIndexPath | IndexPath |
| NSIndexSet | IndexSet |
| NSMeasurement | Measurement |
| NSNotification | Notification |
| NSNumber | Swift的數(shù)字類型(Int和Float等等) |
| NSPersonNameComponents | PersonNameComponents |
| NSSet | Set |
| NSString | String |
| NSTimeZone | TimeZone |
| NSURL | URL |
| NSURLComponents | URLComponents |
| NSURLQueryItem | URLQueryItem |
| NSURLRequest | URLRequest |
| NSUUID | UUID |
我們可以看到,就是直接把Objective-C的前綴NS去掉,就是Swift的值類型(但是有些情況例外)。這些Swift的值類型擁有Objective-C引用類型的所有方法。任何使用Objective-C引用類型的地方,都可以使用對應(yīng)的Swift值類型。
統(tǒng)一的Logging
統(tǒng)一的logging系統(tǒng)提供了一些平臺通用的API來打印一些信息,但是這個(gè)API只在 iOS 10.0, macOS 10.12, tvOS 10.0和watchOS 3.0以后的版本才可用。
下面是使用的例子:
import os.log
// 直接打印一個(gè)String
os_log("This is a log message.")
// 拼接一個(gè)或多個(gè)參數(shù)
let fileSize = 1234567890
os_log("Finished downloading file. Size: %{iec-bytes}d", fileSize)
// 定義打印等級,例如info、debug、error、fault
os_log("This is additional info that may be helpful for troubleshooting.", type: .info)
// 打印信息到特定的子系統(tǒng)
let customLog = OSLog("com.your_company.your_subsystem_name.plist", "your_category_name")
os_log("This is info that may be helpful during development or debugging.", log: customLog, type: .debug)
Cocoa的結(jié)構(gòu)
當(dāng)Swift的結(jié)構(gòu)被橋接成Objective-C時(shí),下面這些結(jié)構(gòu)會變成NSValue。
- CATransform3D
- CLLocationCoordinate2D
- CGAffineTransform
- CGPoint
- CGRect
- CGSize
- CGVector
- CMTimeMapping
- CMTimeRange
- CMTime
- MKCoordinateSpan
- NSRange
- SCNMatrix4
- SCNVector3
- SCNVector4
- UIEdgeInsets
- UIOffset
Cocoa設(shè)計(jì)模式
代理
代理設(shè)計(jì)模式,是我們經(jīng)常用到的。在Objective-C中,在調(diào)用代理方法之前,我們首先要檢查代理是否有實(shí)現(xiàn)這個(gè)代理方法。而在Swift中,我們可以使用可選鏈來調(diào)用代理方法。例如:
class MyDelegate: NSObject, NSWindowDelegate {
func window(_ window: NSWindow, willUseFullScreenContentSize proposedSize: NSSize) -> NSSize {
return proposedSize
}
}
myWindow.delegate = MyDelegate()
if let fullScreenSize = myWindow.delegate?.window(myWindow, willUseFullScreenContentSize: mySize) {
print(NSStringFromSize(fullScreenSize))
}
Lazy初始化
一個(gè)lazy屬性只會在第一次被訪問的時(shí)候才會初始化,相當(dāng)于在Objective-C的懶加載(重寫getter方法)。當(dāng)需要進(jìn)行比較復(fù)雜或者耗時(shí)的計(jì)算才能初始化一個(gè)屬性時(shí),我們應(yīng)該盡量使用lazy屬性。
在Objective-C中:
@property NSXMLDocument *XML;
- (NSXMLDocument *)XML {
if (_XML == nil) {
_XML = [[NSXMLDocument alloc] initWithContentsOfURL:[[Bundle mainBundle] URLForResource:@"/path/to/resource" withExtension:@"xml"] options:0 error:nil];
}
return _XML;
}
而在Swift,我們使用lazy屬性:
lazy var XML: XMLDocument = try! XMLDocument(contentsOf: Bundle.main.url(forResource: "document", withExtension: "xml")!, options: 0)
對于其他需要更復(fù)雜的初始化的屬性,可以寫成:
lazy var currencyFormatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.currencySymbol = "¤"
return formatter
}()
單例
單例模式使我們在開發(fā)中經(jīng)常用到的。
在Objective-C中,我們通常用GCD來實(shí)現(xiàn):
+ (instancetype)sharedInstance {
static id _sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedInstance = [[self alloc] init];
});
return _sharedInstance;
}
在Swift中,直接使用static類型屬性即可,可以保證只初始化一次,即使時(shí)在多線程中被同時(shí)訪問。
class Singleton {
static let sharedInstance = Singleton()
}
如果我們需要其他設(shè)置,可以寫成:
class Singleton {
static let sharedInstance: Singleton = {
let instance = Singleton()
// setup code
return instance
}()
}
API可用性
有些類和方法并不是在所有平臺或者版本都可用的,所有有時(shí)我們需要進(jìn)行API可用性檢查。
例如,CLLocationManager的requestWhenInUseAuthorization方法只能在iOS 8.0和macOS 10.10以后的版本才能使用:
let locationManager = CLLocationManager()
if #available(iOS 8.0, macOS 10.10, *) {
locationManager.requestWhenInUseAuthorization()
}
*是為了處理未來的平臺。
平臺名稱:
- iOS
- iOSApplicationExtension
- macOS
- macOSApplicationExtension
- watchOS
- watchOSApplicationExtension
- tvOS
- tvOSApplicationExtension
同樣地,我們在寫自己的API時(shí),也可以指定那些平臺可以使用:
@available(iOS 8.0, macOS 10.10, *)
func useShinyNewFeature() {
// ...
}
Swift和Objective-C混編
把Objective-C代碼導(dǎo)入Swift
為了把Objective-C代碼導(dǎo)入Swift中,我們需要用到Objective-C bridging header。當(dāng)你把Objective-C文件拖入Swift項(xiàng)目中時(shí),Xcode會提示你是否新建一個(gè)bridging header,如下圖:

點(diǎn)擊Create Bridging Header,項(xiàng)目的文件路徑下就會創(chuàng)建一個(gè)名為項(xiàng)目名稱-Bridging-Header.h的文件(如果項(xiàng)目名稱不是英文,將會以_代替;如果第一個(gè)是字母,也會以_代替)。
當(dāng)然,我們也可以手動(dòng)創(chuàng)建:File > New > File > (iOS, watchOS, tvOS, or macOS) > Source > Header File。
當(dāng)-Bridging-Header.h文件創(chuàng)建好之后,我們還需要進(jìn)行以下操作:
- 把Swift中要用到的Objective-C類的頭文件,以下面這種形式添加到
Bridging-Header.h文件
#import "XYZCustomCell.h"
#import "XYZCustomView.h"
#import "XYZCustomViewController.h"
- 在Build Settings > Swift Compiler - General > Objective-C Bridging Header添加
-Bridging-Header.h的路徑,路徑的格式:項(xiàng)目名/項(xiàng)目名稱-Bridging-Header.h如圖

這樣我們就配置完成了,可以在Swift中調(diào)用Objective-C的代碼:
let myCell = XYZCustomCell()
myCell.subtitle = "A custom cell"
Swift代碼導(dǎo)入Objective-C
當(dāng)需要在Objective-C中使用Swift的代碼時(shí),我們依賴于Xcode自動(dòng)生成的頭文件,這個(gè)頭文件的名稱是項(xiàng)目名-Swift.h(如果項(xiàng)目名稱不是英文,將會以_代替;如果第一個(gè)是字母,也會以_代替)。
默認(rèn)情況下,這個(gè)自動(dòng)生成的頭文件包含了在Swift中被public或者open標(biāo)記的聲明,如果這個(gè)項(xiàng)目中有Objective-C bridging header,那么,internal標(biāo)記的聲明也包含在內(nèi)。被private和fileprivate標(biāo)記的不包含在內(nèi)。私有的聲明不會暴露給Objective-C,除非他們被@IBAction、@IBOutlet或者@objc標(biāo)記。
當(dāng)需要在Objective-C中使用Swift的代碼時(shí),直接導(dǎo)入頭文件項(xiàng)目名-Swift.h,然后我們就可以在Objective-C中調(diào)用Swift的接口,用法與Objective-C的語法相同:
// 初始化實(shí)例,并調(diào)用方法
MySwiftClass *swiftObject = [[MySwiftClass alloc] init];
[swiftObject swiftMethod];
// 引用類或者協(xié)議
// MyObjcClass.h
@class MySwiftClass;
@protocol MySwiftProtocol;
@interface MyObjcClass : NSObject
- (MySwiftClass *)returnSwiftClassInstance;
- (id <MySwiftProtocol>)returnInstanceAdoptingSwiftProtocol;
// ...
@end
// 實(shí)現(xiàn)Swift協(xié)議
// MyObjcClass.m
#import "ProductModuleName-Swift.h"
@interface MyObjcClass () <MySwiftProtocol>
// ...
@end
@implementation MyObjcClass
// ...
@end
注意:如果是剛剛寫的Swift代碼,馬上就想在Objective-C調(diào)用,我們需要先編譯一下,然后Objective-C中才能訪問到Swift的接口。
聲明可以被Objective-C使用的Swift協(xié)議
為了聲明一個(gè)可以被Objective-C使用的Swift協(xié)議,我們要用@objc標(biāo)記,如果協(xié)議的方法是optional,也需要用@objc。
@objc public protocol MySwiftProtocol {
func requiredMethod()
@objc optional func optionalMethod()
}
把Objective-C代碼轉(zhuǎn)為Swift
前面講了一大堆基礎(chǔ)知識,就是為了更好地將Objective-C代碼轉(zhuǎn)為Swift。
遷移過程
- 創(chuàng)建一個(gè)對應(yīng)Objective-C
.m和.h的Swift類,創(chuàng)建方法:File > New > File > (iOS, watchOS, tvOS, or macOS) > Source > Swift File。類的名稱可以相同,也可以不同。 - 導(dǎo)入相關(guān)的系統(tǒng)框架
- 如果要需要用到Objective-C的代碼,需要在bridging header中導(dǎo)入相關(guān)的頭文件
- 為了讓這個(gè)Swift類可以在Objective-C中使用,需要讓這個(gè)類繼承自O(shè)bjective-C的類。如果要自定義在Objective-C中調(diào)用的Swift接口的名稱,使用
@objc(name)。 - 我們可以通過繼承Objective-C的類,實(shí)現(xiàn)Objective-C協(xié)議等來集成Objective-C已有的成員。
- 在遷移過程中,我們要知道:1)Objective-C的語言特性轉(zhuǎn)換成Swift后,是變成怎樣;2)Cocoa框架中Objective-C的類型,在Swift中是什么類型;3)常用的設(shè)計(jì)模式;4)Objective-C的屬性如何遷移到Swift。這些大部分內(nèi)容我上面都有提到。
- Objective-C的(
-)和(+)方法,對應(yīng)到Swift就是func和class func。 - Objective-C的簡單的宏定義改為全局常量,復(fù)雜的宏定義改為方法
- 遷移完成后,在有導(dǎo)入Objective-C類的地方,用
#import "項(xiàng)目名稱-Swift.h"替換。 - 把之前的
.m文件的target membership這個(gè)勾去掉。先別著急把之前的.m和.h文件刪掉,因?yàn)槲覀儎倓倢懲甑腟wift類可能不太完善,我們還需要用之前的文件來解決問題。

- 如果Swift的類名和之前的Objective-C的類名不一樣,在用到Objective-C的類的地方,更新為新的類名。
完
有任何問題,歡迎大家留言!
歡迎加入我管理的Swift開發(fā)群:536353151,本群只討論Swift相關(guān)內(nèi)容。
原創(chuàng)文章,轉(zhuǎn)載請注明出處。謝謝!