簡述如何從Objective-C到Swift過渡,objective-cswift

來源:互聯網
上載者:User

簡述如何從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^
 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.