遷移程序到 Swift 3.0

avmt0535 8年前發布 | 17K 次閱讀 Swift IOS Apple Swift開發

在昨晚 WWDC 之后,相必大家都已經下載到最新 Beta 版本的 XCode 甚至已經把手機升級到 iOS10 了吧。本次的 WWDC 雖然有很多人表示不滿,感覺并無新意,但是在折騰完 Beta 版,投入實際開發后,還是對 iOS10 的內功有了一定的認識,不夸張的講,這還真是內功了。當然這內功好不好練,以及練了是否值得,還有待各位看官自行判斷,反正我是練了。。。

好了,還是回到正題,講講 Swift 3。每當看到新的編程語言我總是會有相當大的興趣,是的,Swift 3是一門『新語言』,因為它看起來,怎么都不像過往的 Swift。打開一個老的項目時,發現滿目瘡痍,基本上能標紅的代碼全被標紅了。先來個截圖提提神:

遷移程序到 Swift 3.0

圖一 代碼標紅

怎么樣,清醒點了沒?可能你會覺得奇怪,怎么連 whiteColor() 這種通用的東西都不能用了?其實這是本次 Swift(和 SDK)大改的一個重要目標,就是『去重』。新的 SDK 去除了大量可有可無的東西,并且把庫做深度的修整,現在的 SDK 看起來更好用了(當然你也可以吐槽又要記大量新的東西了,不過 Swift 哪次新版發布時你不改程序了,對吧)。下面我一一來講述。

一、去重

Swift 3 對于重復輸入的表達式深惡痛絕,如上圖所示的 whiteColor(),現在一律被改為了 white()。我們可以推斷出其他類似的改動(或者讓 IDE 幫助我們),如:

UIColor.whiteColor() 
被改為
UIColor.white()
list.objectAtIndex(i)
被改為
list.object(at: i)

同樣的,方法也被如此修改了,如:

presentViewController(controller, animated: true)
被改為
present(toViewController: controller, animated: true)
dequeueReusableCellWithIdentifier("Cell", forIndexPath:indexPath)
被改為
dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
func numberOfSectionsInTableView(tableView: UITableView) -> Int
被改為
func numberOfSections(in tableView: UITableView) -> Int

一個很不幸而且幾乎會讓人崩潰的消息是,這樣的改動幾乎發生在每一處函數調用,這使用開發人員不得不重新審視幾乎所有的代碼,因為老的代碼可能都在新的 SDK 下變得毫無建樹。

二、參數

Swift 3 對參數的傳入做了大幅改動,主要是添加了參數名稱的限定,在 Swift 3 下,一個方法所接受的參數必須擁有『名稱』,而讓全部參數都擁有名稱這件事,改動實在太大了,因此 Swift 又想出了一招可以讓你不寫名稱,使用單個下劃線作為允許匿名符號。參考以下實例:

override func viewWillAppear(animated: Bool)
被改為
override func viewWillAppear(_ animated: Bool)

下面再看一個實例的調用例子,加深印象:

func myFunc(x: Int, y: Int)
這個函數在調用時必須使用以下代碼
myFunc(x: 1, y: 2)

若是允許匿名參數,則修改之:

func myFunc(_ x: Int, _ y: Int)
這個函數在調用時允許不帶參數名稱
myFunc(1, 2)

這樣的改動較大程度的影響了原有繼承下來的方法,如viewWillAppear,UITableView 的回調函數等。對于自己寫的方法,稍加注意即可,這兩種方法都有著各自適用的場景。

三、命名

在早期的 Objective-C 或 Swift 中,系統內的常量、函數等的命名一直是一件讓人頭疼的事,例如UIControlContentHorizontalAlignmentCenter這種,簡直又臭又長,超級難記,雖說有 Xcode 這種強大的 IDE,也難免會弄錯。在 Swift 3 內,所有的相關常量都被劃到指定的枚舉類中,而常量和函數均遵守相當良好的代碼規范。來看幾個實例:

lbl.textAlignment = NSTextAlignment.Center
被改為
lbl.textAlignment = NSTextAlignment.center
tableFooterView = UIView(frame: CGRectZero)
被改為
tableFooterView = UIView(frame: CGRect.zero)

另外,現在的新的命名方式也更注重突出實際作用,再給個例子體會下:

lbl.hidden = false
被改為
lbl.isHidden = false

與此同時,所有NS類的類名也變得簡潔了,不再需要NS前綴,再配合其他的改動(如去重),對代碼的精簡形成了相當大的影響:

let bundle = NSBundle.mainBundle()
被改為
let bundle = Bundle.main()
let mgr = NSFileManager.defaultManager()
被改為
let mgr = FileManager.default()

再次告知一個不幸的消息,這樣的改動幾乎也牽動了全部的代碼!!講到這里可能有一部分人已經哭了,居然要改那么多代碼,還能不能好好玩耍了? 別擔心,下面還有一半。

四、方法的返回值處理

我們在開發中可能會經常調用一些帶有返回值的方法,但是卻不處理返回值,例如以下這種:

navigationController!.popViewControllerAnimated(true)

這個方法實際上返回一個 UIViewController,但是很少有人會用,更多的場景是把它當成無返回的方法來使用。但是在 Swift 3 中,這樣做是不行的,你必須處理掉這個返回值,如下:

let _ = navigationController!.popViewController(animated: true)

使用單個下劃線來指代一個不會被使用的變量。

另外,Swift 3 不再允許傳入傳出的對象,之前帶有 var 的方法聲明將全部作廢:

func myFunc(var a: Int) -> Int

如以上這種聲明,已不可再使用,對于有多個值要返回的方法,必須改為:

fun myFunc(a: Int) -> (Int, Int)

五、可選類型

Swift 3 對可選類型的處理更嚴格了,必須顯式的在任何地方使用感嘆號,例如我們有以下代碼:

var str: String! = "a"
var s = str
print(s)

在 Swift 以往的版本中,print 語句會打印出 str 的值,也就是『a』。而在 Swift 2.3 下,則是會打印出『Optional("a")』。因此很多對字符串處理(特別是路徑處理)的代碼都會出錯,因為會莫名其妙的帶上了各種各樣的『Optional』字樣。正確的處理方式是:

var str: String! = "a"
var s = str!
print(s)

或者

var str: String! = "a"
var s = str
print(s!)

同樣的,在實際開發中,如果用到字符串模板,也需要非常注意這樣的變化:

var str  = "Loaded: \(data)"
需要被修改為
var str  = "Loaded: \(data!)"

六、Selector

Swift 對于 Selector 的修改可能是最讓人無奈的了,我們來細數一下經歷過的版本:

self.performSelector(onMainThread: #selector(handle(ret:)), with: ret, waitUntilDone: true)
就以 Swift 3 下的這個函數為基準吧,老版本的Selector獲取方法是這樣的:
#selector(ViewController.handle(_:))    // 2.2
#selector(ViewController.handle(:))     // 2.1
#selector(handle)                       // 2.0
@selector("handle:")                    // 1.x  x等于幾已經不記得了
"handle:"                               // 沒記錯的話是 1.0 時代,直接傳個字符串就是 Selector
N/A                                     // Swift 的歷史上,還真有過沒有 Selector 的版本

回到 Swift 3 上來,目前的 Selector 寫法如最上面那種,需要注意的是,Selector 的方法名和參數名必須與實際被調用的方法完全一致,否則編譯時就會報錯。

另外,Selector 傳參時,只能傳遞對象,不能傳基礎數據類型,傳基礎數據類型的情況下,一律變成0(希望這只是當前版本的 bug,不然太蛋疼了)。雖說蘋果已經把大部分的 NS 類都去掉了前綴,但是 NSNumber 這東西還是得經常用一下呢,至少目前是這樣的。

七、類庫

隨著 Swift 3 一起發布的 iOS SDK 10,其改動也不小,特別是對一些類庫的改動,刪除了大量的方法,有些方法可能是對我們過去的開發帶來大量幫助的。但是沒有辦法,我們必須接受這樣的改變。

類庫的改動沒有辦法一一說明,我用到的 SDK 成員也非常有限,所以就只講幾個例子。

  • 針對有 option 選項的 protocol,目前強制要求為每一條都寫上 @objc,如:

    @objc protocol MyProtocol: NSObjectProtocol {
          optional func foo(myClass: MyClass?)
          optional func bar(myClass: MyClass?)
    }
    需要改為
    @objc protocol MyProtocol: NSObjectProtocol {
          @objc optional func foo(myClass: MyClass?)
          @objc optional func bar(myClass: MyClass?)
    }
  • 不再有CGRectMakeCGSizeMake等常用函數了,如:

    UIView(frame: CGRectMake(0, 0, 48, 48))
    被改為
    UIView(frame: CGRect(x: 0, y: 0, width: 48, height: 48))
  • Normal選項用方括號對來代替,如:

    btn.setTitleColor(UIColor.whiteColor(), forState: UIControlState.Normal)
    被改為
    btn.setTitleColor(UIColor.white(), for: [])

    至于為什么會如此改,我是沒想明白,不管怎么說,UIControlState內已經沒有normal這個選項了,目前也只能這么做。方括號非常容易引起對于數組或是集合等的聯想,但是此處卻又完全不是數組或集合,方括號的語義并不清晰。

  • 圖形圖象庫有較大改動,給個具體例子參考下:

    let imgData = UIImageJPEGRepresentation(img, 1)
    let imgPath = "\(FileUtils.getDocumentPath())/\(name)"
    imgData!.writeToFile(imgPath, atomically: true)
    必須改為
    let imgData = UIImageJPEGRepresentation(img, 1)
    let imgPath = "\(FileUtils.getDocumentPath()!)/\(name!)"
    NSData(data: imgData!).write(toFile: imgPath, atomically: true)

結束語

就先講這么些吧,已經夠各位改一陣子了。雖然 Swift 這么虐,但是隨著一次次迭代,還是可以看到它在穩固的進步著,希望以后不要再那么大變化了吧。至少語法給個兼容,類庫給個 Mapping,不要直接全屏飄紅(可以飄黃呀...),或許這樣才能走得更快更穩吧。

最后多講一句,臨時改標題是不對的,但是我還是改了,跟大家說聲抱歉。昨天犯了個錯誤,XCode8 里面的確是 Swift3,只是我們都被它內置的 2.3 給誤導了,真正的 Swift3 編譯器被放在一個奇怪的地方:

Swift 2.2: Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift
Swift 2.3: Developer/Toolchains/Swift_2.3.xctoolchain/usr/bin/swift
Swift 3:   Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-migrator/swift

 

文/何曉杰Dev(簡書)

 本文由用戶 avmt0535 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!