簡述如何從Objective-C到Swift過渡,objective-cswift
本篇文章簡述了如何從Objective-C向Swift過渡。
話不多說,開門見山注意:本文討論的開發環境為Xcode 6 beta 2版本。
單一檔案結構 VS 介面-實現
最值得一提的一大改動便是在Objective-C中“[介面].h/[實現].m”這種檔案結構被取締了。
其實我本人是很支援這種檔案結構的,因為通過介面檔案來擷取及共用類特性的方式相當安全而且簡單,不過現在不得不面對它不複存在的現實了。
在Swift中並不存在介面與實現分割成兩個檔案的現象,我們僅需要依靠實現來構建一個類就行了(並且在寫的時候甚至不可能添加關於可訪問性的修改)。
如果對於這一改動感到無法忍受的話應注意以下事項:
最為明顯的:靠直覺。
我們可以藉助漂亮的文檔來提高類的可讀性。舉個例子,我們可以把所有想作為public的要素全部挪到檔案開頭去,也可以採用擴充來區分public和private。
另一個很實用的辦法就是在private的方法和變數命名前加一個底線‘_'作為首碼。
下面是混合了以上兩種方案的樣本:
<span style="font-size:14px;">01// Public02extension DataReader {03 04 var data { }05 func readData(){06 var data = _webserviceInteraction()07 }08}09 10// Private implementation11class DataReader: NSObject { let _wsURL = NSURL(string: "http://theurl.com") func _webserviceInteraction()->String{ // ... }}</span>
雖然我們沒辦法修改類中各元素的可見度,不過我們可以試著讓某些訪問變得“困難一些”。
一個特殊的方法就是使用嵌套類來把private部分隱藏起來(至少是自動的隱藏),下面是例子:
<span style="font-size:14px;">import UIKitclass DataReader: NSObject { // Public *********************** var data:String?{ get{return private.internalData} } init(){ private = DataReaderPrivate() } func publicFunction(){ private.privateFunc() } // Private ********************** var private:DataReaderPrivate class DataReaderPrivate { var internalData:String? init(){ internalData = "Private data!" } func privateFunc (){} }}</span>
我們將private的實現放入一個private常量的執行個體中,然後用“正常”的類的實現來充當介面。不過private部分並非會真正的隱藏起來,只不過在訪問的時候需要加上一個private關鍵字:
<span style="font-size:14px;">let reader = DataReader()reader.private.privateFunc()</span>
問題來了:僅是為了最後要將這些private的部分隱藏起來,就要把代碼寫得這樣怪異值得嗎?
我的建議是在可見度的修改出來之前(蘋果正在忙這個事),我們還是採用詳細的文檔或者多少用一點擴充來完成這個事。
常量和變數
在寫Objective-C的時候我會很少的使用到const關鍵字,甚至於我知道有的資料時不會變的。然而在Swift中蘋果建議開發人員們多花點心思在使用常量(let)而不是變數(var)上。所以請注意要弄明白你的變數的具體要做什麼。你會使用常量的頻繁度將是你從未想象過的。
更加簡化的寫法
來看一下下面的兩行代碼,比較一下有何不同:
<span style="font-size:14px;">let wsURL:NSURL = NSURL(string:"http://wsurl.com");vslet wsURL = NSURL(string:"http://wsurl.com")</span>
在我最開始接觸Swift的前兩個星期,我強迫自己不要在每一行代碼最後都添加分號(不過現在寫Objective-C的時候我不會加分號了)。
類型推斷可以直接根據變數的定義為其指派類型,相比較Objective-C這類冗雜的語言來說,在這裡倒是可圈可點。
我們應該使用一致的命名方式,否則其他的開發人員(包括你自己)就很難通過極其糟糕的命名來推測其類型:
<span style="font-size:14px;">let a = something()</span>
更加合理的命名是這樣的:
<span style="font-size:14px;">let a = anInt()</span>
還有一個改動就是關於括弧號,他們不再需要配對了:
<span style="font-size:14px;">if (a > b){} vs if a > b {}</span>
不過請記住,我們在括弧中間寫入的部分會被認為是一個運算式,在這裡並不代表這樣寫總是對的。在變數綁定時我們不能像下面這樣使用括弧:
<span style="font-size:14px;">if (let x = data){} // Error! if let x = data {} // OK!</span>
使用類型判斷和刪除分號及括弧並不完全必要,不過我們可以考慮一下用以上建議的方式來寫Swift代碼,這樣的話會提高代碼的可讀性並且減少一些輸入量。
可選值
有多少次我們困惑與函數的返回值該如何設定?我曾經使用過NSNotFound, -1, 0,自訂的返回值來表示返回為空白。
現在可選值的出現很好的解決了返回值為空白的問題,我們僅需要在資料類型的後面添加一個問號就可以了。
我們可以這樣寫:
<span style="font-size:14px;">class Person{ let name:String let car:Car? // Optional value init(name:String){ self.name = name }} // ACCESSING THE OPTIONAL VALUE ***********var Mark = Person(name:"mark")// use optional binding if let car = Mark.car { car.accelerate()}// unwrap the valueMark.car?.accelerate()</span>
這是個用了可選值來描述“某人有一輛車”的例子,它表示car這一特徵可以是沒有的,因為這表示某人沒有車。
然後我們可以用optional binding或者unwrap來取得它的值。
如果對於一個屬性沒有設定為可選值,我們又沒有為其賦值的話,編譯器會立馬不爽快的。
一旦初始化了之後便沒有設定非可選屬性的機會了。
所以我們應該事先考慮一下類的屬性與其它部分的關係,以及在類進行執行個體化的時候它們會發生什麼變化。
這些改進徹底的改變了構思一個類的方式。
可選值的拆包
你會發現可選值這個東西難以理喻,因為你不會理解為什麼編譯器會提示你在使用之前對其進行拆包。
Mark.car?
我建議你把可選值當做一個結構體(當做結構體的話會好理解一些),其中包括了一個你所設定的值。不過外麵包裹了其他東西(wrap)。如果裡面的值有定義,你就可以進行拆包(unwrap)然後得到你所想得到的值。否則你就得到一個空值(nil)。
使用驚嘆號"!"來進行強制拆包而不管其中的值是否有定義,這樣做是有風險的,因為如果裡面的值沒有定義的話應用會崩掉。
委託模式
經過多年的Objective-C和Cocoa代碼編寫,我想大部分人都對使用委託模式養成了一種嗜好。注意了!我們還是可以繼續保留這種嗜好的,下面是一個非常簡單的例子:
<span style="font-size:14px;">@objc protocol DataReaderDelegate{ @optional func DataWillRead() func DataDidRead()}class DataReader: NSObject { var delegate:DataReaderDelegate? var data:NSData? func buildData(){ delegate?.DataWillRead?() // Optional method check data = _createData() delegate?.DataDidRead() // Required method check }}</span>
這裡我們使用了一個簡單的@optional來替換了使用respondToSelector檢測委託方法是否存在。
<span style="font-size:14px;">delegate?.DataWillRead?()</span>
請注意我們在協議之前必須加@obj首碼,因為後面使用了@optional。同時編譯器也會在這裡報一個警告的訊息以防你沒有加上@obj。
要實現協議的話,我們需要構建一個類來實現它然後用曾經在OC上用過的方式來指派。
<span style="font-size:14px;">class ViewController: UIViewController, DataReaderDelegate { override func viewDidLoad() { super.viewDidLoad() let reader = DataReader() reader.delegate = self } func DataWillRead() {...} func DataDidRead() {...}}</span>
目標-動作模式
另一常用的設計模式:目標-動作模式。我們仍然同樣可以像在OC中使用它那樣在Swift中實現它。
<span style="font-size:14px;">class ViewController: UIViewController { @IBOutlet var button:UIButton override func viewDidLoad() { super.viewDidLoad() button.addTarget(self, action: "buttonPressed:", forControlEvents: UIControlEvents.TouchUpInside) } func buttonPressed(sender:UIButton){...}}</span>
這裡唯一不同的地方就是如何定義一個selector選取器。我們可以變形使用像下面這樣的字串來寫方法原型:
<span style="font-size:14px;">Selector("buttonPressed:")</span>
單件模式
簡直又愛又恨。單件模式依舊是設計模式中最為常用的模式之一。
我們可以用GCD和dispatch_once來實現它,當然還可以用let關鍵字來實現安全執行緒。
<span style="font-size:14px;">class DataReader: NSObject { class var sharedReader:DataReader { struct Static{ static let _instance = DataReader() } return Static._instance }...}</span>
我們來快速探索一下這段代碼:
1、sharedReader是一個靜態複合屬性(我們也可以替換為方法)。
2、靜態屬性不允許在類被實現的時候重構,所以由於內部類型是被允許的,我們可以再這裡加入一個結構體。
3、_instance是一個常量,它不會被重寫而且保證安全執行緒。
可以參考下面DataReader單例的用法:
<span style="font-size:14px;">DataReader.sharedReader</span>
結構和枚舉
Swift中的結構和枚舉簡直神乎其神,你根本不會在其他的語言裡面找到像它們這樣的。
它們支援方法:
<span style="font-size:14px;">struct User{ // Struct properties let name:String let ID:Int // Method!!! func sayHello(){ println("I'm " + self.name + " my ID is: \(self.ID)") }} let pamela = User(name: "Pamela", ID: 123456)pamela.sayHello()</span>
如你所見在這裡的結構體使用了初始化,而且這個是Swift自動建立的(我們可以添加一些自定的實現)。
枚舉類型的文法比起我們用過的會稍難。
它的定義需要用到關鍵字case:
<span style="font-size:14px;">enum Fruit { case orange case apple}</span>
而且枚舉並不局限於int型:
<span style="font-size:14px;">enum Fruit:String { case .orange = "Orange" case .apple = "Apple"}</span>
而且還可以用的更複雜一些:
<pre name="code" class="plain"><span style="font-size:14px;">enum Fruit{ // Available Fruits case orange case apple // Nested type struct Vitamin{ var name:String } // Compound property var mainVitamin:Vitamin { switch self{ case .orange: return Vitamin(name: "C") case .apple: return Vitamin(name: "B") } }} let Apple = Fruit.applevar Vitamin = Apple.mainVitamin</span>
在上面我們為Fruit枚舉類添加了一個內部類型Vitamin和一個複合的mainVitamin,並且這樣的結構還可以根據枚舉的值來進行初始化裡面的元素……是不是已經感到眼花繚亂了?
可變與不可變類
在OC中我們總會用到可變以及不可變類,舉個例子?NSArray和NSDictionary。在Swift裡面我們不在像這樣來區分資料了,只需要用常量和變數的定義來替代。
資料變數是可以變的,而常數陣列的值不可更改。所以請記下這個公式:
“let = immutable. var = mutable”.
塊和閉包
我非常喜歡塊的文法,因為它相當簡單而且好記。
<br>
順帶提一下,因為有多年Cocoa的變成習慣,所以有時候我會偏愛於用塊來替代簡單的委託作業。這是很靈活快捷的方式,而且非常實用。
Swift中與塊相對的是閉包。閉包的作用極為強大而且蘋果在將其簡單化上做得很棒,很容易就可以實現。
官方文檔裡的樣本只能說讓我無言以對。
它是這樣定義的:
<span style="font-size:14px;">reversed = sort(names, { (s1: String, s2: String) -> Bool in return s1 > s2})</span>
然後是這樣重構的:
<span style="font-size:14px;">reversed = sort(names, >)</span>
所以,由於類型判斷的存在,我們能以不同的方式來實現一個閉包、速寫參數($0, $1)和直接操作函數(>)。
在這篇文章裡我打算遍曆一下閉包的用法,不過此前我想對如何擷取閉包中的值說幾句。
在OC裡面,我們定義一個變數像_block這樣,以方便我們想預備將它壓入塊。不過在閉包裡面這些都沒有必要。
我們可以使用和修改周圍的值。事實上閉包被設計得非常聰明,足夠它擷取外部的元素來給內部使用。每個被擷取的元素會作為拷貝或者是引用。如果閉包會修改它的值則建立一個引用,否則就產生一份拷貝。
如果閉包引用了一個包含或調用了閉包本身的執行個體,我們就會進入一個迴圈強引用。
來看一下例子:
<span style="font-size:14px;">class Person{ var age:Int = 0 @lazy var agePotion: (Int) -> Void = { (agex:Int)->Void in self.age += agex } func modifyAge(agex:Int, modifier:(Int)->Void){11 modifier(agex) } } var Mark:Person? = Person()Mark!.modifyAge(50, Mark!.agePotion)Mark = nil // Memory Leak</span>
這個agePotion閉包引用了它本身,而對當前的執行個體保證了強引用。同時執行個體保持了一個隊閉包的引用。BOOM~~~我們進入了一個迴圈強引用。
為了避免這種情況我們需要使用擷取列表Capture List.,這個列表維護了我們想使用的執行個體的無主弱引用。文法十分簡單,只需要在閉包定義前添加 [unowned/strong self]就行,然後你會得到一個無主的弱引用來替代以前的強引用。
<span style="font-size:14px;">@lazy var agePotion: (Int) -> Void = { [unowned self](agex:Int)->Void in self.age += agex}</span>
無主弱引用
在OC裡面我們知道弱引用是怎麼運作的。在Swift裡面也一樣,基本沒什麼變化。
所以什麼是無主引用呢。我仔細的看了看這個關鍵詞的介紹,因為它很好的說明了類間的關係的定義。
讓我們來簡單的描述一下人Person與銀行賬戶BankAccount間的關係:
1、一個人可以擁有一個銀行賬戶(可選)。
2、一個銀行賬戶屬於一個人(必須)。
<span style="font-size:14px;">We can describe this relation with code: class Person{ let name:String let account:BankAccount! init(name:String){ self.name = name self.account = BankAccount(owner: self) }} class BankAccount{ let owner:Person init(owner:Person){ self.owner = owner }}</span>
這寫關係會建立一個引用迴圈。第一種解決方案添加了一個弱引用給“BankAccount.owner”屬性。不過還用了一個無主引用作為約束:這個屬性必須有一個值,不可為空(之前的列表裡的第二點令人滿意)。
好了,關於無主引用沒有更多要說的了。其實它恰好就像一個沒有為所指引用增加作用的弱引用,不過為其保證了一個不為空白的值。
最後你會發現,在Swift耗費的實驗和測試的次數越多,就會越清晰的明白其價值所在。查看更多>>>
Objective-C怎使用Swift類?
developer.apple.com/...h.html
學習swift語言需不需要學objective-c?
非常明確的告訴你:不用的。
swift是蘋果開發出來用來取代ObjC的,如果學習他還必須有ObjC基礎,那也太喪心病狂了。。。
不僅如此,一個程式新手學習swift的難度小於其他程式設計語言。
希望我的回答對你有協助!^o^