迁移程序到 Swift 3.0

作者: 何晓杰Dev | 来源:发表于2016-06-14 22:56 被阅读12436次

    在昨晚 WWDC 之后,相必大家都已经下载到最新 Beta 版本的 XCode 甚至已经把手机升级到 iOS10 了吧。本次的 WWDC 虽然有很多人表示不满,感觉并无新意,但是在折腾完 Beta 版,投入实际开发后,还是对 iOS10 的内功有了一定的认识,不夸张的讲,这还真是内功了。当然这内功好不好练,以及练了是否值得,还有待各位看官自行判断,反正我是练了。。。

    好了,还是回到正题,讲讲 Swift 3。每当看到新的编程语言我总是会有相当大的兴趣,是的,Swift 3是一门『新语言』,因为它看起来,怎么都不像过往的 Swift。打开一个老的项目时,发现满目疮痍,基本上能标红的代码全被标红了。先来个截图提提神:

    图一 代码标红

    怎么样,清醒点了没?可能你会觉得奇怪,怎么连 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这个选项了,目前也只能这么做。方括号非常容易引起对于数组或是集合等的联想,但是此处却又完全不是数组或集合,方括号的语义并不清晰。

    ** 结束语 **

    就先讲这么些吧,已经够各位改一阵子了。虽然 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
    

    相关文章

      网友评论

      • IMSk:Swift 同感啊楼主。当初 每当升级Xcode 我都心虚,两点,自己写的代码一片红要大面积改,二如果引入第三方library,运气好人家支持了的还好,运气不好,哭吧。
      • zBk9cN:selector的变化真的是蛋疼
      • yohunl:并没有所有的以NS开头的都去掉了NS了,例如
        NSImageView
        NSWindow
        NSTextView
        NSTextField
        等,并没有去掉NS前缀,按我发现的,应该只有Foundation那一层的绝大部分去掉了NS前缀
      • 王老詹:寫的很清楚 謝謝您
      • 青莲佛子:写的很好,加油!
      • kemchenj:btn.setTitleColor(UIColor.whiteColor(), forState: UIControlState.Normal)
        被改为
        btn.setTitleColor(UIColor.white(), for: [])

        方括号表示的确实是数组, 你可以同时给设置多个state设置颜色标题等各种参数, 避免了以前那种重复写好几行重复代码去给不同的state设置相同参数的情况, 例如:
        btn.setTitleColor(UIColor.white(), for: [.highlight, .selected])

        但空数组含义确实也有点怪怪的, 上面的这段代码我也不太清楚会不会作用到.normal状态上
        何晓杰Dev:@kemchenj 多谢提醒😄
      • 林凌子文:NSTextAlignment.center不是直接.center就可以吗,就是说枚举变量直接.OneCase不用SomeEnumType.OneCase,这里很困惑。
      • 彼蓝:讲真最痛苦的迁移莫过于第三方库。要么等作者更新要么自己解锁了来改 :joy:
      • 彼蓝:写的好!学习!
        顺便NSTextAlignment原来的版本好像应该把点去了。:yum:
      • 木子知日_:好文章,赞一个
      • 光明程辉: :smile: 最近也开始尝试使用swift,希望多多交流 :smile:
      • Mellong:感谢楼主分享
      • 389c20d5a244:写的很好,不能理解怎么会有人说华而不实。
      • onehao16:淡淡的忧伤
      • Adrift:非常不错
      • findM:之前把app混编了,本来打算改为纯swift的 经历了一次swift改版以后就把swift干掉了,太他妈的痛苦了,还是等以后稳定了以后再用吧,目测还需要等几个大版本
        金银岛:@findM 频繁变动的语言特性,普通玩家真的玩不起啊
        findM:@何晓杰Dev 我们不是,业务压力太大了,实在没有精力改这些,每次出新版本,就跟来次大姨妈似的
        何晓杰Dev:@findM 我也有过同样的经历,不过最后还是狠下心来全部上了swift,其实如果没有业务压力的话,看着一门语言的演变也是一件有意思的事
      • 文兴:edit-convert-to latest swift syntax能自动迁徙大部分似乎
        文兴:@何晓杰Dev 希望swift3的工具会更完善吧,不然真的手工改要骂娘了!
        何晓杰Dev:@文兴 是的,可以自动迁移很多代码,但是这个工具并不好用,我尝试转换了一个项目的代码,发现转换后有部分逻辑错误,主要是optional的问题,不运行程序很难排查,所以后面全部手工修改了。也正是因为这样,才有了这篇文章
      • 呵呵哒1991:此文绝壁秀,3.0都不稳定,就瞎bb
        mark666: @关秋生 最讨厌你们这类喷子,有本事你也写一篇来装一下逼,行吗你?
        呵呵哒1991:@mark666 cnm
        mark666:@关秋生 你懂吗?逼玩意
      • 透亮心情:let view = UIView.init(frame:CGRectMake(0, 0, 100, 100))

        let view = UIView(frame:CGRectMake(0, 0, 100, 100))

        前辈我看你用的第二种初始化控件,问题是我这里必须加init,使用的xcode7.3.1这个是怎么回事???
        透亮心情:@何晓杰Dev 谢谢!主要我编译器2种都可以编译,但是只有第一种有提示!
        何晓杰Dev:@透亮心情 init不是必须的,这两种方法都是合法的,而且效果一样。
      • 一整块腹肌:啪啪啪
      • 0294b96fe4db: :clap: 啪啪啪

      本文标题:迁移程序到 Swift 3.0

      本文链接:https://www.haomeiwen.com/subject/jlvudttx.html