# Swift學(xué)習(xí): 從Objective-C到Swift
這篇文章是自己學(xué)習(xí)Swift的筆記與深化野瘦。希望這篇文章能夠幫助已經(jīng)有Objective-C經(jīng)驗的開發(fā)者更快地學(xué)習(xí)Swift叙身。同時也品味到Swift的精妙之處。
結(jié)論放在開頭:
我認(rèn)為Swift比Objective-C更優(yōu)雅,更安全同時也更現(xiàn)代,更性感棍矛。
文章組織脈絡(luò):
1. 從Objective-C到Swift的語法差異。我們熟悉的Objective-C特性在Swift中如何展現(xiàn)奋岁。
2. 從Objective-C到Swift的進步改進。研究對比Swift在安全性,易用性上的提升,給我們帶來的新編程范式浮还。
目錄:?
- [1.屬性(property)和實例變量(instance variable)](#1)
- [2.控制流](#2)
- [3.函數(shù)](#3)
- [4.類與初始化(Initializers)](#4)
- [5.枚舉與結(jié)構(gòu)體](#5)
- [6.協(xié)議(Protocols)與擴展(Extensions)](#6)
- [7.Swift與Cocoa](#7)
- [8.總結(jié)](#8)
<h2 id="1">1.屬性(property)和實例變量(instance variable)</h2>
### Objective-C property in Swift world
在Cocoa世界開發(fā)的過程中,我們最常打交道的是property.
典型的聲明為:
~~~objective-c
@property (strong,nonatomic) NSString *string;
~~~
而在Swift當(dāng)中,擺脫了C的包袱后,變得更為精煉,我們只需直接在類中聲明即可
~~~swift
class Shape {
? ? var name = "shape"
}
~~~
注意到這里,我們不再需要`@property`指令,而在Objective-C中,我們可以指定property的attribute,例如strong,weak,readonly等闻伶。
而在Swift的世界中,我們通過其他方式來聲明這些property的性質(zhì)。
需要注意的幾點:
- `strong`: 在Swift中是默認(rèn)的
- `weak`: 通過weak關(guān)鍵詞申明? ?
~~~swift
weak var delegate: UITextFieldDelegate?
~~~
- `readonly`,`readwrite`:
Swift沒有這兩個attribute,如果是定義一個`stored property`,通過`let`定義它只讀,通過`var`定義它可讀可寫糯俗。
如果想實現(xiàn)類似Objective-C中,對外在頭文件.h 聲明property為`readonly`,對內(nèi)在.m聲明property為`readwrite`,這種情況在Swift通過`Access Control`來實現(xiàn):
~~~swift
private(set) var property: Int
~~~
關(guān)于`Access Control`(在本文[類與初始化(Initializers)](#4)會提到)
- `copy`:通過`@NSCopying`指令聲明睦擂。
**值得注意的是String,Array和Dictionary在Swift是以值類型(value type)而不是引用類型(reference type)出現(xiàn),因此它們在賦值,初始化,參數(shù)傳遞中都是以拷貝的方式進行(簡單來說,String,Array,Dictionary在Swift中是通過`struct`實現(xiàn)的)**
[延伸閱讀:Value and Reference Types](https://developer.apple.com/swift/blog/?id=10)
- `nonatomic`,`atomic` 所有的Swift properties 都是nonatomic得湘。但是我們在線程安全上已經(jīng)有許多機制,例如NSLock,GCD相關(guān)API等。個人推測原因是蘋果想把這一個本來就用的很少的特性去掉,線程安全方面交給平時我們用的更多的機制去處理顿仇。
然后值得注意的是,在Objective-C中,我們可以跨過property直接與instance variable打交道,而在Swift是不可以的淘正。
例如:我們可以不需要將someString聲明為property,直接使用即可。即使我們將otherString聲明為property,我們也可以直接用_otherString來使用property背后的實例變量臼闻。
~~~objective-c
@interface SomeClass : NSObject {
? NSString *someString;
}
@property(nonatomic, copy) NSString* otherString;
~~~
而在Swift中,我們不能直接與instance variable打交道鸿吆。也就是我們聲明的方式簡化為簡單的一種,簡單來說在Swift中,我們只與property打交道。
> A Swift property does not have a corresponding instance variable, and the backing store for a property is not accessed directly
### 小結(jié)
- 因此之前使用OC導(dǎo)致的像巧哥指出的[開發(fā)爭議](http://blog.devtang.com/blog/2015/03/15/ios-dev-controversy-1/)就不再需要爭執(zhí)了,在Swift的世界里,我們只與property打交道述呐。
- 并且我們在OC中`init`和`dealloc`不能使用屬性`self.property = XXX`來進行設(shè)置的情況得以解決和統(tǒng)一惩淳。
(不知道這一條規(guī)定,在init直接用self.property = value 的同學(xué)請自覺閱讀[iOS夯實:內(nèi)存管理](https://github.com/100mango/zen/blob/master/iOS%E5%A4%AF%E5%AE%9E%EF%BC%9A%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86/iOS%E5%A4%AF%E5%AE%9E%EF%BC%9A%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86.md))
:)
個人覺得這看似小小一點變動使Swift開發(fā)變得更加安全以及在代碼的風(fēng)格更為統(tǒng)一與穩(wěn)定。
### Swift property延伸:
- `Stored Properties`和`Computed properties`
在Swift中,property被分為兩類:`Stored Properties`和`Computed properties`
簡單來說,就是stored properties 能夠保存值,而computed properties只提供getter與setter,利用stored properties來生成自己的值市埋。個人感覺Computed properties更像方法,而不是傳統(tǒng)意義的屬性黎泣。但是這樣一個特性存在,使得我們更容易組織我們的代碼。
延伸閱讀:[computed property vs function](http://stackoverflow.com/questions/24035276/computed-read-only-property-vs-function-in-swift)
- `Type Properties`
Swift提供了語言級別定義類變量的方法缤谎。
> In C and Objective-C, you define static constants and variables associated with a type as global static variables.In Swift, however, type properties are written as part of the type’s definition, within the type’s outer curly braces, and each type property is explicitly scoped to the type it supports.
>
在Objective-C中,我們只能通過單例,或者static變量加類方法來自己構(gòu)造類變量:
~~~objective-c
@interface Model
+ (int) value;
+ (void) setValue:(int)val;
@end
@implementation Model
static int value;
+ (int) value
{ @synchronized(self) { return value; } }
+ (void) setValue:(int)val
{ @synchronized(self) { value = val; } }
@end
~~~
~~~objective-c
// Foo.h
@interface Foo {
}
+(NSDictionary*) dictionary;
// Foo.m
+(NSDictionary*) dictionary
{
? static NSDictionary* fooDict = nil;
? static dispatch_once_t oncePredicate;
? dispatch_once(&oncePredicate, ^{
? ? ? ? // create dict
? ? });
? return fooDict;
}
~~~
> 更新:Xcode8 Release Note : Objective-C now supports class properties, which interoperate with Swift type properties. They are
declared as: @property (class) NSString *someStringProperty;. They are never synthesized.? 也就是從Xcode8之后, Objective-C也有了類變量的定義,不過getter和setter都需要我們自己編寫。這是一個典型的Swift反推Objective-C發(fā)展的例子褐着。
而在Swift中我們通過清晰的語法便能定義類變量:
通過static定義的類變量無法在子類重寫,通過class定義的類變量則可在子類重寫坷澡。
~~~swift
class Aclass {
? ? static var storedTypeProperty = "Some value."
? ? static var computedTypeProperty: Int {
? ? ? ? return 1
? ? }
? ? class var overrideableComputedTypeProperty: Int {
? ? ? ? return 107
? ? }
}
~~~
同時利用類變量我們也有了更優(yōu)雅的單例模式實現(xiàn):
~~~swift
class singletonClass {
? ? static let sharedInstance = singletonClass()
? ? private init() {} // 這就阻止其他對象使用這個類的默認(rèn)的'()'初始化方法
}
~~~
Swift單例模式探索:[The Right Way to Write a Singleton](http://krakendev.io/blog/the-right-way-to-write-a-singleton?utm_campaign=This%2BWeek%2Bin%2BSwift&utm_medium=web&utm_source=This_Week_in_Swift_45)
- 延伸:
目前Swift支持的type propertis中的`Stored Properties`類型不是傳統(tǒng)意義上的類變量(class variable),暫時不能通過class 關(guān)鍵詞定義,通過static定義的類變量類似java中的類變量,是無法被繼承的,父類與子類的類變量指向的都是同一個靜態(tài)變量含蓉。
延伸閱讀: [Class variables not yet supported](http://stackoverflow.com/questions/24015207/class-variables-not-yet-supported)
~~~
class SomeStructure {
? ? class var storedTypeProperty = "Some value."
}
//Swift 2.0
Error: Class stored properties not yet supported in classes
~~~
通過編譯器拋出的錯誤信息,相信在未來的版本中會完善`Type properties`频敛。
<h2 id="2">2.控制流</h2>
Swift與Objective-C在控制流的語法上關(guān)鍵詞基本是一致的,但是擴展性和安全性得到了很大的提升。
主要有三種類型的語句
1. if,switch和新增的guard
2. for,while
3. break,continue
主要差異有:
***
### 關(guān)于if
**語句里的條件不再需要使用`()`包裹了馅扣。**
~~~swift
let number = 23
if number < 10 {
? ? print("The number is small")
}
~~~
**但是后面判斷執(zhí)行的的代碼必須使用`{}`包裹住斟赚。**
為什么呢,在C,C++等語言中,如果后面執(zhí)行的語句只有語句,我們可以寫成:
~~~objective-c
? int number = 23
if (number < 10)
NSLog("The number is small")
~~~
但是如果有時要在后面添加新的語句,忘記添加`{}`,災(zāi)難就很可能發(fā)送。
:) 像蘋果公司自己就犯過這樣的錯誤差油。下面這段代碼就是著名的goto fail錯誤,導(dǎo)致了嚴(yán)重的安全性問題拗军。
~~~C
? if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)
? ? goto fail;
? ? goto fail;? // :)注意 這不是Python的縮減
? ... other checks ...
? fail:
? ? ... buffer frees (cleanups) ...
? ? return err;
~~~
:)? ?
最終在Swift,蘋果終于在根源上消除了可能導(dǎo)致這種錯誤的可能性。
**if 后面的條件必須為Boolean表達式**
也就是不會隱式地與0進行比較,下面這種寫法是錯誤的,因為number并不是一個boolean表達式,number != 0才是蓄喇。
~~~objective-c
int number = 0
if number{
}
~~~
***
### 關(guān)于for
for循環(huán)在Swift中變得更方便,更強大发侵。
得益于Swift新添加的范圍操作符`...`與`..<`
我們能夠?qū)⒅胺爆嵉膄or循環(huán):
~~~
for (int i = 1; i <= 5; i++)
{
? ? NSLog(@"%d", i);
}
~~~
改寫為:
~~~swift
for index in 1...5 {
? ? print(index)
}
~~~
當(dāng)然,熟悉Python的親們知道Python的range函數(shù)很方便,我們還能自由選擇步長。
像這樣:
~~~python
>>> range(1,5) #代表從1到5(不包含5)
[1, 2, 3, 4]
>>> range(1,5,2) #代表從1到5妆偏,間隔2(不包含5)
[1, 3]
~~~
雖然在《The Swift Programming Language》里面沒有提到類似的用法,但是在Swift中我們也有優(yōu)雅的方法辦到刃鳄。
~~~swift
for index in? 0.stride(through: 10, by: 2) {
? ? print(index) // 0 2 4 6 8 10?
}
~~~
然后對字典的遍歷也增強了.在Objective-c的快速枚舉中我們只能對字典的鍵進行枚舉。
~~~objective-c
NSString *key;
for (key in someDictionary){
? ? NSLog(@"Key: %@, Value %@", key, [someDictionary objectForKey: key]);
}
~~~
而在Swift中,通過tuple我們可以同時枚舉key與value:
~~~swift
let dictionary = ["firstName":"Mango","lastName":"Fang"]
for (key,value) in dictionary{
? ? print(key+" "+value)
}
~~~
***
### 關(guān)于Switch
Swich在Swift中也得到了功能的增強與安全性的提高钱骂。
**不需要Break來終止往下一個Case執(zhí)行**
也就是下面這兩種寫法是等價的叔锐。
~~~swift
let character = "a"
switch character{
? ? case "a":
? ? ? ? print("A")
? ? break
? ? case "b":
? ? ? ? print("B")
? ? break
default: print("character")
~~~
~~~swift
let character = "a"
switch character{
? ? case "a":
? ? ? ? print("A")
? ? case "b":
? ? ? ? print("B")
default: print("character")
~~~
這種改進避免了忘記寫break造成的錯誤,自己深有體會,曾經(jīng)就是因為漏寫了break而花了一段時間去debug挪鹏。
如果我們想不同值統(tǒng)一處理,使用逗號將值隔開即可。
~~~swift
switch some value to consider {
case value 1,value 2:
? ? statements
}
~~~
**Switch支持的類型**
在OC中,Swtich只支持int類型,char類型作為匹配愉烙。
而在Swift中,Switch支持的類型大大的拓寬了讨盒。實際上,蘋果是這么說的。
>? A switch statement supports any kind of data
這意味在開發(fā)中我們能夠能夠?qū)ψ址?浮點數(shù)等進行匹配了齿梁。
之前在OC繁瑣的寫法就可以進行改進了:
~~~objective-c
if ([cardName isEqualToString:@"Six"]) {
? ? [self setValue:6];
} else if ([cardName isEqualToString:@"Seven"]) {
? ? [self setValue:7];
} else if ([cardName isEqualToString:@"Eight"]) {
? ? [self setValue:8];
} else if ([cardName isEqualToString:@"Nine"]) {
? ? [self setValue:9];
}
~~~
~~~swift
switch carName{
? ? case "Six":
? ? ? ? self.vaule = 6
? ? case "Seven":
? ? ? ? self.vaule = 7
? ? case "Eight":
? ? ? ? self.vaule = 8
? ? case "Night":
? ? ? ? self.vaule = 9?
}
~~~
<h2 id="3">3.函數(shù)</h2>
對于在OC中,方法有兩種類型,類方法與實例方法催植。方法的組成由方法名,參數(shù),返回值組成。
在Swift中函數(shù)的定義基本與OC一樣勺择。
主要區(qū)別為:
1. 通過`func`關(guān)鍵詞定義函數(shù)
2. 返回值在`->`關(guān)鍵詞后標(biāo)注
各舉一個類方法與實例方法例子创南。
~~~objevtive-c
+ (UIColor*)blackColor
- (void)addSubview:(UIView *)view
~~~
對應(yīng)的swift版本
~~~swift
? class func blackColor() -> UIColor //類方法, 通過 class func 關(guān)鍵詞聲明
func addSubview(view: UIView) //實例方法
~~~
### 改進:
- 在Swift中,函數(shù)的最重要的改進就是函數(shù)作為一等公民(first-class),和對象一樣可以作為參數(shù)進行傳遞,可以作為返回值,函數(shù)式編程也成為了Swift支持的編程范式。
> In computer science, a programming language is said to have first-class functions if it treats functions as first-class citizens. Specifically, this means the language supports passing functions as arguments to other functions, returning them as the values from other functions, and assigning them to variables or storing them in data structures
讓我們初略感受一下函數(shù)式編程的魅力:
舉一個例子,我們要篩選出一個數(shù)組里大于4的數(shù)字省核。
在OC中我們可能會用快速枚舉來進行篩選稿辙。
~~~objective-c
? NSArray *oldArray = @[@1,@2,@3,@4,@5,@6,@7,@8,@9,@10];
? ? NSMutableArray *newArray;
? ? for (NSNumber* number in oldArray) {
? ? ? ? if ([number compare:@4] == NSOrderedDescending ) {
? ? ? ? ? ? [newArray addObject:number];
? ? ? ? }
? ? }
~~~
而在Swift中,我們用兩行代碼解決這個問題:
~~~swift
let oldArray = [1,2,3,4,5,6,7,8,9,10]
let newArray = oldArray.filter({$0 > 4})
~~~
進一步了解Swift的函數(shù)式編程可以通過這篇優(yōu)秀的博客[Functional Reactive Programming in Swift](http://blog.callmewhy.com/2015/05/11/functional-reactive-programming-1/#)
- 個人覺得另外一個很棒的改進是:`Default parameter values`
在我們的項目中,經(jīng)常會不斷進行功能的增添。為了新增特性,許多方法在開發(fā)的過程中不斷變動气忠。舉一個例子:我們開始有一個tableViewCell,它的設(shè)置方法一開始簡單地需要一個Model參數(shù):
~~~swift
func configureCellWithModel(Model: model)
~~~
不久之后,我們想對部分Cell增添一個設(shè)置背景顏色的功能邻储。方法需要再接收多一個參數(shù):
~~~swift
func configureCellWithModel(Model: model,color:UIColor)
~~~
這個時候方法改變,所以涉及到這些方法的地方都需要修改。給我們造成的困擾?
一是:需要做許多重復(fù)修改的工作旧噪。?
二是:無法做得很好的擴展和定制,有些地方的cell需要設(shè)置顏色,有些不需要吨娜。但是在OC里,我們只能對所有的cell都賦值。你可能覺得我們可以寫兩個方法,一個接收顏色參數(shù),一個不接受淘钟。但是我們知道這不是一個很好的解決方法,會造成冗余的代碼,維護起來也不方便宦赠。
而在Swift中,`default parameter values`的引入讓我們能夠這樣修改我們的代碼:
~~~swift
func configureCellWithModel(Model: model,color:UIColor = UIColor.whiteColor())
~~~
這樣的改進能讓我們寫出的代碼更具向后兼容性,減少了我們的重復(fù)工作量,減少了犯錯誤的可能性。
<h2 id="4">4.類與初始化(Initializers)</h2>
- 文件結(jié)構(gòu)與訪問控制
在swift中,一個類不再分為`interface`(.h)與`implementation`(.m)兩個文件實現(xiàn),直接在一個.swift文件里進行處理米母。好處就是我們只需管理一份文件,以往兩頭奔波修改的情況就得到解放了,也減少了頭文件與實現(xiàn)文件不同步導(dǎo)致的錯誤勾扭。
這時我們會想到,那么我們?nèi)绾蝸矶x私有方法與屬性呢,在OC中我們通過在`class extension`中定義私有屬性,在.m文件定義私有方法。
而在Swift中,我們通過`Access Control`來進行控制铁瞒。
>properties, types, functions等能夠進行版本控制的統(tǒng)稱為實體妙色。
>- Public:可以訪問自己模塊或應(yīng)用中源文件里的任何實體,別人也可以訪問引入該模塊中源文件里的所有實體慧耍。通常情況下身辨,某個接口或Framework是可以被任何人使用時,你可以將其設(shè)置為public級別蜂绎。
- Internal:可以訪問自己模塊或應(yīng)用中源文件里的任何實體栅表,但是別人不能訪問該模塊中源文件里的實體。通常情況下师枣,某個接口或Framework作為內(nèi)部結(jié)構(gòu)使用時怪瓶,你可以將其設(shè)置為internal級別。
- Private:只能在當(dāng)前源文件中使用的實體,稱為私有實體洗贰。使用private級別找岖,可以用作隱藏某些功能的實現(xiàn)細(xì)節(jié)
一個小技巧,如果我們有一系列的私有方法,我們可以把它們組織起來,放進一個extension里,這樣就不需要每個方法都標(biāo)記private,同時也便于管理組織代碼:
~~~swift
// MARK: Private
private extension ViewController {
? ? func privateFunction() {
? ? }
}
~~~
- 創(chuàng)建對象與`alloc`和`init`
關(guān)于初始化,在Swift中創(chuàng)建一個對象的語法很簡潔:只需在類名后加一對圓括號即可。
~~~swift
var shape = Shape()
~~~
而在Swift中,`initializer`也與OC有所區(qū)別,Swift的初始化方法不返回數(shù)據(jù)敛滋。而在OC中我們通常返回一個self指針许布。
> Unlike Objective-C initializers, Swift initializers do not return a value. Their primary role is to ensure that new instances of a type are correctly initialized before they are used for the first time.
Swift的初始化方法讓我們只關(guān)注對象的初始化。之前在OC世界中[為什么要self = [super init]绎晃?](http://www.zhihu.com/question/22295642)蜜唾。這種問題得以避免。Swift幫助我們處理了alloc的過程庶艾。也讓我們的代碼更簡潔明確袁余。
而在Swift中,`init`也有了更嚴(yán)格的規(guī)則。
- 對于所有`Stored Properties`,都**必須**在對象被創(chuàng)建出來前設(shè)置好咱揍。也就是我們必須在init方法中賦好值,或是直接給屬性提供一個默認(rèn)值颖榜。
如果有property可以被允許在初始出來時沒有值,也就是需要在創(chuàng)建出來后再賦值,或是在程序運行過程都可能不會被賦值。那么這個property必須被聲明為`optional`類型煤裙。該類型的屬性會在init的時候初始化為nil.
- initializer嚴(yán)格分為`Designated Initializer`和`Convenience Initializer`
并且有語法定義掩完。
而在Objective-C中沒有明確語法標(biāo)記哪個初始化方式是convenience方法。關(guān)于`Designated Initializer`可參閱之前的:[Objective-C 拾遺:designated initializer](https://github.com/100mango/zen/blob/master/Objective-C%20%E6%8B%BE%E9%81%97%EF%BC%9Adesignated%20initializer/Objective-C%20%E6%8B%BE%E9%81%97%EF%BC%9Adesignated%20initializer.md)
~~~swift
init(parameters) {
statements
}
convenience init(parameters) {
statements
}
~~~
<h2 id="5">5.枚舉與結(jié)構(gòu)體</h2>
- 枚舉
在Swift中,枚舉是一等公民硼砰。(first-class)且蓬。能夠擁有方法,computed properties等以往只有類支持的特性。
在C中,枚舉為每個成員指定一個整型值题翰。而在Swift中,枚舉更強大和靈活缅疟。我們不必給枚舉成員提供一個值。如果我們想要為枚舉成員提供一個值(raw value),我們可以用字符串,字符,整型或浮點數(shù)類型遍愿。
~~~swift
enum CompassPoint {
? case North
? case South
? case East
? case West
}
var directionToHead = CompassPoint.West
~~~
- 結(jié)構(gòu)體
Struct在Swift中和類有許多相同的地方,可以定義屬性,方法,初始化方法,可通過extension擴展等。
不同的地方在于struct是值類型.在傳遞的過程中都是通過拷貝進行耘斩。
在這里要提到在前面第一節(jié)處提到了`String`,`Array`和`Dictionary`在Swift是以值類型出現(xiàn)的沼填。這背后的原因就是`String`,`Array`,`Dictionary`在Swift中是通過`Struct`實現(xiàn)的。而之前在Objective-C它們都是通過class實現(xiàn)的括授。
Swift中強大的Struct使得我們能夠更多與值類型打交道坞笙。Swift的值類型增強了`不可變性(Immutabiliity)`。而不可變性提升了我們代碼的穩(wěn)定性,多線程并發(fā)的安全性荚虚。
在WWDC2014《Advanced iOS Application Architecture and Patterns》中就有一節(jié)的標(biāo)題是*Simplify with immutability*薛夜。
延伸閱讀:[WWDC心得:Advanced iOS Application Architecture and Patterns](https://github.com/100mango/zen/blob/master/WWDC%E5%BF%83%E5%BE%97%EF%BC%9AAdvanced%20iOS%20Application%20Architecture%20and%20Patterns/WWDC%E5%BF%83%E5%BE%97%EF%BC%9AAdvanced%20iOS%20Application%20Architecture%20and%20Patterns.md)
<h2 id="6">6.協(xié)議(Protocols)與擴展(Extensions)</h2>
### 協(xié)議(Protocol)
語法:
在Objective-C中我們這么聲明Protocol:
~~~objective-c
@protocol SampleProtocol <NSObject>
- (void)someMethod;
@end
~~~
而在Swift中:
~~~swift
protocol SampleProtocol
{
? ? func someMethod()
}
~~~
在Swift遵循協(xié)議:
~~~swift
class AnotherClass: SomeSuperClass, SampleProtocol
{
? ? func someMethod() {}
}
~~~
`protocol`和`delegate`是緊密聯(lián)系的。那么我們在Swift中如何定義Delegate呢版述?
~~~swift
protocol MyDelegate : class {
}
class MyClass {
? ? weak var delegate : MyDelegate?
}
~~~
注意到上面的protocol定義后面跟著的class梯澜。這意味著該protocol只能被class類型所遵守。
并且只有遵守了class protocol的delegate才能定義為weak渴析。這是因為在Swift中,除了class能夠遵守協(xié)議,枚舉和結(jié)構(gòu)同樣能夠遵守協(xié)議晚伙。而枚舉和結(jié)構(gòu)是值類型,不存在內(nèi)存管理的問題吮龄。因此只需要class類型的變量聲明為weak即可。
利用Swift的optional chaining,我們能夠很方便的檢查delegate是否為Nil,是否有實現(xiàn)某個方法:
以前我們要在Objective-C這樣檢查:
~~~objective-c
if (self.dataSource && [self.dataSource respondsToSelector:@selector(titleForSegmentAtIndex:)]) {
? ? ? ? thisSegmentTitle = [self.dataSource titleForSegmentAtIndex:index];
? ? }
~~~
在Swift中,非常的優(yōu)雅簡潔咆疗。
~~~swift
if let thisSementTitle = dataSource?.titleFroSegmentAtIndex?(index){
}
~~~
新特性:
在Swift中,protocol變得更加強大,靈活:
1. `class`,`enum`,`structure`都可以遵守協(xié)議漓帚。
2. `Extension`也能遵守協(xié)議。利用它,我們不需要繼承,也能夠讓系統(tǒng)的類也遵循我們的協(xié)議午磁。
例如:
~~~swift
protocol myProtocol {
? func hello() -> String
}
extension String: myProtocol{
? func hello() -> String {
? ? ? return "hello world!"
? }
}
~~~
我們還能夠用這個特性來組織我們的代碼結(jié)構(gòu),如下面的代碼所示,將UITableViewDataSource的實現(xiàn)移到了Extension尝抖。使代碼更清晰。
~~~swift
// MARK: - UITableViewDataSource
extension MyViewcontroller: UITableViewDataSource {
? // table view data source methods
}
~~~
3. `Protocol Oriented Programming`
隨著Swift2.0的發(fā)布,面向協(xié)議編程正式也加入到了Swift的編程范式迅皇。Cool.
這種編程方式通過怎樣的語法特性支撐的呢昧辽?
那就是我們能夠?qū)f(xié)議進行擴展(`protocol extensions`),也就是我們能夠提供協(xié)議的默認(rèn)實現(xiàn),能夠為協(xié)議添加新的方法與實現(xiàn)。
用前面的myProtocol為例子,我們在Swift里這樣為它提供默認(rèn)實現(xiàn)喧半。
~~~swift
extension myProtocol{
func hello() -> String {
? ? ? return "hello world!"
? }
}
~~~
我們還能對系統(tǒng)原有的protocol進行擴展,大大增強了我們的想象空間奴迅。Swift2.0的實現(xiàn)也有很多地方用extension protocol的形式進行了重構(gòu)。
面向協(xié)議編程能夠展開說很多,在這里這簡單地介紹了語法挺据。有興趣的朋友可以參考下面的資料:
[Session 408: Protocol-Oriented Programming in Swift](https://developer.apple.com/videos/wwdc/2015/?id=408)
[IF YOU'RE SUBCLASSING, YOU'RE DOING IT WRONG.](http://krakendev.io/blog/subclassing-can-suck-and-heres-why)
### 擴展(Extension)
`Extension`在Swift在類似Objective-C的`Category`取具。在Objective-C中,我們用它來為已有的類添加新的方法實現(xiàn)。在Swift中,我們不僅可以對類進行擴展,而且能對結(jié)構(gòu)體,枚舉類型,協(xié)議進行擴展扁耐。
語法:
Objective-C:
~~~objective-c
#import "MyClass.h"
@interface MyClass (MyClassAddition)
- (void)hello;
@end
~~~
Swift:
~~~swift
extension SomeType {
func hello(){}
}
~~~
與Objective-C的Category不同的是,Swift的`Extension`沒有名字暇检。
我們還可以利用該特性來整理代碼: 比如將私有方法集合在一起
~~~swift
private extension ViewController {
//...私有方法
}
~~~
<h2 id="7">7.Swift與Cocoa</h2>
一門語言的的強大與否,除了自身優(yōu)秀的特性外,很大一點還得依靠背后的框架。Swift直接采用蘋果公司經(jīng)營了很久的Cocoa框架⊥癯疲現(xiàn)在我們來看看使用Swift和Cocoa交互一些需要注意的地方块仆。
1. `id`與`AnyObject`
在Swift中,沒有`id`類型,Swift用一個名字叫`AnyObject`的protocol來代表任意類型的對象。
~~~objective-c
id myObject = [[UITableViewCell alloc]init];
~~~
~~~swift
var myObject: AnyObject = UITableViewCell()
~~~
我們知道id的類型直到運行時才能被確定,如果我們向一個對象發(fā)送一條不能響應(yīng)的消息,就會導(dǎo)致crash王暗。
我們可以利用Swift的語法特性來防止這樣的錯誤:
~~~swift
myObject.method?()
~~~
如果myObject沒有這個方法,就不會執(zhí)行,類似檢查delegate是否有實現(xiàn)代理方法悔据。
在Swift中,在`AnyObject`上獲取的property都是`optional`的。
2. 閉包
OC中的`block`在Swift中無縫地轉(zhuǎn)換為閉包俗壹。函數(shù)實際上也是一種特殊的閉包科汗。
3. 錯誤處理
之前OC典型的錯誤處理步驟:
~~~objective-c
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *URL = [NSURL fileURLWithPath:@"/path/to/file"];
NSError *error = nil;
BOOL success = [fileManager removeItemAtURL:URL error:&error];
if (!success) {
? NSLog(@"Error: %@", error.domain);
}
~~~
在Swift中:
~~~swift
let fileManager = NSFileManager.defaultManager()
let URL = NSURL.fileURLWithPath("/path/to/file")
do {
? try fileManager.removeItemAtURL(URL)
} catch let error as NSError {
? print("Error: \(error.domain)")
}
~~~
4. KVO。
Swift支持KVO绷雏。但是KVO在Swift,個人覺得是不夠優(yōu)雅的,KVO在Swift只限支持繼承NSObject的類,有其局限性,在這里就不介紹如何使用了头滔。
網(wǎng)上也出現(xiàn)了一些開源庫來解決這樣的問題。有興趣可以參考一下:
[Observable-Swift](https://github.com/slazyk/Observable-Swift)
KVO 在OS X中有Binding的能力,也就是我們能夠?qū)蓚€屬性綁定在一起,一個屬性變化,另外一個屬性也會變化涎显。對與UI和數(shù)據(jù)的同步更新很有幫助,也是MVVM架構(gòu)的需求之一坤检。之前已經(jīng)眼饞這個特性很久了,雖然Swift沒有原生帶來支持,Swift支持的泛型編程給開源界帶來許多新的想法。下面這個庫就是實現(xiàn)binding的效果期吓。
[Bond](https://github.com/SwiftBond/Bond)
<h2 id="8">8.總結(jié)</h2>
到這里就基本介紹完Swift當(dāng)中最基本的語法和與Objective-C的對比和改進早歇。
事實上Swift的世界相比OC的世界還有很多新鮮的東西等待我們?nèi)グl(fā)現(xiàn)和總結(jié),Swift帶來的多范式編程也將給我們編程的架構(gòu)和代碼的組織帶來更來的思考。而Swift也是一個不斷變化,不斷革新的語言。相信未來的發(fā)展和穩(wěn)定性會更讓我們驚喜缺前。這篇文章也將隨著Swift的更新而不斷更新,同時限制篇幅,突出重點蛀醉。
希望這篇文章能夠給各位同行的小伙伴們快速了解和學(xué)習(xí)Swift提供一點幫助。有疏漏錯誤的地方歡迎直接提出衅码。感謝拯刁。
--
參考:
1. [《The Swift Programming Language》](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language)
2. [Apple Swift Blog](https://developer.apple.com/swift/blog/)
3.? [Using Swift with Cocoa and Objective-C](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/)