流着泪,做Swift3.0迁移适配

Swift3.0最大的变化,有太多太多,没有最大,只有一样大。
  先前工程除了第三方库,其余全部用Swift开发,也经历了2.1到2.2的迁移,2.2到2.3的迁移。在2.3版本时,编译器已经有了一些警告,某些语法将在Swift3.0中不能再使用,比如参数中的var声明,UIButton的构造方法等等。但让人没想到的是,Swift3.0真正到来时,更需要花费我2天的时间埋头改改改。谨以此文,记录迁移过程。

注意事项

手里两个业务工程用Swift迁移工具,其中有一个因为CocoaPods的原因,一直卡在Generating Preview的阶段。后来偶然因为CocoaPods由于GFW的原因不能执行update命令更新第三方库,我把CocoaPods去掉了,第三方库直接放主工程里,恢复原始工程结构,就可以正常迁移了。

自动迁移部分

  1. 已经声明的枚举值被全部改为首字母小写,因为新规范认为枚举相当于类,值是成员,那么成员就应当以驼峰法命名,首字母小写;
  2. 已经声明的方法被拆分调用。比如autolayout的封装库PureLayout,设置布局的方法autoPinEdgeToSuperviewEdge()变成autoPinEdge(toSuperviewEdge:);诸如此类还有很多,例如UIColor.whiteColor() -> UIColor.white......总之,尽可能的让能推断出类型的情况,少写重复词。当然,还有变长的方法名,例如view.hidden -> view.isHidden。
  3. 关于类成员变量的权限,迁移工具默认将先前的private权限改为fileprivate,因为新规范中的private就算在同文件中但不在同一个类里也是无访问权限的。因此先前的private权限,等同于现在的fileprivate。
  4. closure闭包现在有了更详细的宏声明了,声明闭包是否会作为存储型变量被强引用。因为对闭包的强引用会导致外部对象一直不能被释放,外部就需要使用weak var 来让closure对传入的对象只保持弱引用。不显式声明默认是非存储型闭包,当闭包被强引用则需要添加@escaping,位置在变量冒号后面,类型前面,例如
    failureCallback: @escaping ((_ errorMsg: String) -> Void)
  5. 方法声明参数现在分三部分,冒号左边一部分是标签,另一部分是参数名,冒号右边是变量类型。在Swift3.0以前,标签可有可无,没有标签时直接用变量名当标签使用;Swift3.0规范了方法命名,规定必须使用标签,如果强制不使用标签,那么方法声明时标签用一根下划线"_"替代。自动迁移工具无法完成更详细的识别,因此只能把大部分方法的标签先改为"_"。

手动迁移部分

  1. 按照新规范,其实所有的方法命名都应该重写。自动迁移让大部分方法的参数标签,都变为了""。这只是一个过渡的,能让程序快速通过编译的方案,如果有足够时间,方法名最好规范起来。
    例如
    func reloadUIWithData(
    data: BaseData?) {}
    新规范下,命名为:
    func reloadUI(withData data: BaseData?) {}

  2. 闭包作为参数声明时,标签只能用"_"。调用闭包则不需要标签,直接写值,多个参数值用","分隔。
    例如
    func request(_ redPacketID: Int64,
    successCallback: @escaping ((_ code: Int32, _ msg: String, _ rsp: RspLookRedPackage) -> Void),
    failureCallback: @escaping ((_ errorMsg: String) -> Void)) {
    }
    调用时,写:
    successCallback(rspMessage.rsp.retCode, rspMessage.rsp.retMsg, rspResult)

  3. Swift语言本身不支持同名方法重载,碰到需要重载的场景一般都是用不同数量参数,或不同的起始方法名来解决。如果第三方库中的方法名,被Swift3.0拆分成了相同起始方法名相同参数数量的方法,则需要Objective-C文件做桥接。
    SDWebImage库中,为UIImage下载图片有以下两个方法:
    - (void)sd_setImageWithURL:(NSURL *)url
    placeholderImage:(UIImage *)placeholder
    options:(SDWebImageOptions)options {}

     - (void)sd_setImageWithURL:(NSURL *)url 
               placeholderImage:(UIImage *)placeholder 
                      completed:(SDWebImageCompletionBlock)completedBlock { }
    

在Swift3.0中,会被识别成


Swift3.0拆分了部分方法起始名字

那么,在使用sd_setImage(with:....)时,编译器会报如下错:


方法具有二义性

解决方法就是创建一个Objective-C类别,添加起始方法名不同的方法名。
//头文件

#import <UIKit/UIKit.h>
#import <UIImageView+WebCache.h>

    @interface UIImageView (SDWebImageSwiftBridge)

    - (void)swiftBridge_sd_setImageWithURL:(NSURL *)url
                          placeholderImage:(UIImage *)placeholder
                                 completed:(SDWebImageCompletionBlock)completedBlock;

    @end
    //实现文件
    @implementation UIImageView (SDWebImageSwiftBridge)

    - (void)swiftBridge_sd_setImageWithURL:(NSURL *)url
                          placeholderImage:(UIImage *)placeholder
                                 completed:(SDWebImageCompletionBlock)completedBlock {
        [self sd_setImageWithURL:url placeholderImage:placeholder completed:completedBlock];
    }

    @end
  1. 以上全部完成后,编译运行,立即就发现了一个bug。比如声明的String!类型值,在用=赋值后,类型就变成了String?。在Swift3.0以前,String!类型的值传递或经方法返回后,可以直接当String类型的用。也就是说,如果现在String!需要传递或作为方法返回值,要么强制解包成String再传,要么不解包传到别处就是String?。String?对象在被使用时,假如值是"123",那么它将会是Optional("123")样式。比如拼接网址时,这样网址的网页就无法打开。
    写了更多的Swift3.0代码,发现了设计的根本所在。在Swift3.0中,所有声明为Optional的类型,直接打印的话都会被当做非强制解包类型。比如下面代码

Swift3.0中无论类型后是?还是!,直接打印都算有Optional()包裹

因此,以往String!类型对象打印值等同于String的情况,已经变成了String!打印值等同于String?,为Optional("")
我想,这样的变动,更多是出于严格区分普通类型和可选类型,进一步加强安全性的举措。之前的版本里,如果一个方法的返回值是String!类型,那么是可以直接当String来用;而现在,方法返回类型如果为String!,获取到的返回值实际上是String?。客观上说,String!的返回值也是可以为nil的,所以如果在外部直接使用获取的返回值,为nil的情况会直接导致crash。尤其是老旧的OC代码,没有_Nonnull或_Nullable修饰的返回值,在Swift中全部默认为强制解包类型,现在都要当?类型用了。
String!目前和String?最大共同点在于,如果当前值是Optional("123"),那么在使用该值是,将都是String?;另外,它们的区别就是,调对象方法或访问对象属性时,String!会直接以解包对象的方式去调,而String?仍然是可选链方法。详细见下图

Swift 3.0的可选类型示例
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,529评论 5 475
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,015评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,409评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,385评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,387评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,466评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,880评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,528评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,727评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,528评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,602评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,302评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,873评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,890评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,132评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,777评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,310评论 2 342

推荐阅读更多精彩内容

  • 因为要结局swift3.0中引用snapKit的问题,看到一篇介绍Xcode8,swift3变化的文章,觉得很详细...
    uniapp阅读 4,385评论 0 12
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,579评论 18 139
  • importUIKit classViewController:UITabBarController{ enumD...
    明哥_Young阅读 3,771评论 1 10
  • 多线程、特别是NSOperation 和 GCD 的内部原理。运行时机制的原理和运用场景。SDWebImage的原...
    LZM轮回阅读 2,001评论 0 12
  • “人非圣贤孰能无过”当我们做错了的时候就要学会说出对不起三个字,无论是家人还是同事,一句对不起不仅可能会挽回一段...
    潍坊泰华DDM张娟阅读 811评论 3 0