Swift3.2 到Swift4 的升级工作(要处理SDK API的bug)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/dangyalingengjia/article/details/79072591

最近完成了公司项目由Swift3.2 -> Swift4的升级工作,记录一下。
这里将Swift4较Swift3.2的新特性以及必要的处理列出来:

1,private关键字作用域扩大到整个文件。
说明:可以用private替代fileprivate了
处理建议:考虑到private没有向后兼容性(在swift3.2及之前版本无法在extension中使用),所以建议保留原来的fileprivate关键字。

2,扩展Key Path的写法
说明:
Swift 3.2中Key Path写法:

class Kid: NSObject {
     var nickname: String = ""
     var age: Double = 0.0
     var friends: [Kid] = []

    init(nickname: String, age: Double) {
        self.nickname = nickname
        self.age = age
    }
}

class ViewController {
    func testKeyPath() {
        var ben = Kid(nickname: "Benji", age: 5.5)
        let kidsNameKeyPath = #keyPath(Kid.nickname)
        ben.setValue("Ben", forKeyPath: kidsNameKeyPath)
    }
}

Swift4中增加的写法:

func testKeyPath() {
    let ben = Kid(nickname: "Benji", age: 5.5)
    let kidsNameKeyPath = \Kid.nickname
    ben[keyPath: \Kid.nickname] = "Ben"
}

处理建议:代码可以不用改,见了\的用法认识就可以。

3,关于@objc的使用
说明:
Swift编译器会推断需要暴露给OC的函数(所有继承于NSObject的类的函数),并为其增加@objc关键字修饰。这样做的问题有:
(1)增大二进制文件大小。
(2)程序员不知道编译器何时会推断为需要增加@objc

用Swift4编译代码时,在check dependencies这一步,提示:


The use of Swift 3 @objc inference in Swift 4 mode is deprecated. Please address deprecated @objc inference warnings, test your code with “Use of deprecated Swift 3 @objc inference” logging enabled, and then disable inference by changing the “Swift 3 @objc Inference” build setting to “Default” for the “XXX” target.


按照提示将xxx target - build setting- Swift 3 @objc Inference 由On改为Default之后,#selector的函数以及dynamic变量如果没有用@objc修饰的话,会报错:
Argument of ‘#selector’ refers to instance method ‘xxx()’ that is not exposed to Objective-C
给#selector中的函数和dynamic变量前面加上@objc就可以了。
处理建议:必须按照说明中的方法修改,否则编译报错。

4,结构体UILayoutPriority中的运算符重载被删除
说明:
setContentCompressionResistancePriority(_:for:)等方法的参数类型是结构体UILayoutPriority。
Swift3.2写法:

self.detail1Label.setContentCompressionResistancePriority(UILayoutPriorityDefaultLow-10, for: UILayoutConstraintAxis.horizontal)

self.detail2Label.setContentHuggingPriority(UILayoutPriorityRequired, for: UILayoutConstraintAxis.horizontal)

Swift4写法:

self.detail1Label.setContentCompressionResistancePriority(UILayoutPriority(rawValue: UILayoutPriority.RawValue(Int(UILayoutPriority.defaultLow.rawValue)-10)), for: UILayoutConstraintAxis.horizontal)

self.detail2Label.setContentHuggingPriority(UILayoutPriority.required, for: UILayoutConstraintAxis.horizontal)

处理建议:必须按照说明中的方法修改,否则编译报错。

5,NSAttributedString相关OC的全局变量无法在Swift4中继续使用
说明:
看下这段代码:

let textMATString = NSMutableAttributedString(string: "abcd")
let titleOptions: [String : Any] = [NSFontAttributeName : UIFont.systemFont(ofSize: 12)]
textMATString.addAttributes(titleOptions, range: NSMakeRange(0, 4))

这段代码在Swift3.2上编译无问题,但是在Swift4上报错:
Cannot convert value of type ‘NSAttributedStringKey’ to expected dictionary key type ‘String’
查了SDK,NSFontAttributeName是OC中的全局变量名,在Swift中相应的是NSAttributedStringKey.font。看来Swift4中严格限制了OC中的全局变量在Swif4中的使用。

以下是Swift4中的写法:

let titleOptions: [NSAttributedStringKey : Any] = [NSAttributedStringKey(rawValue: NSAttributedStringKey.font.rawValue) : UIFont.systemFont(ofSize: 12)]

处理建议:必须按照说明中的方法修改,否则编译报错。

6,更严格的Block参数检查
说明:
Swift4中对Block中参数的个数进行了严格的检查,即使是用_表示的未使用参数,个数也要严格对应才能编译通过。
例如,Kingfisher中的Block定义

DownloadProgressBlock = ((_ receivedSize: Int64, _ totalSize: Int64) -> ())

使用方式:

KingfisherManager.shared.retrieveImage(with: url, options: options, progressBlock: {_ in}, completionHandler: { [weak self] (image, error, cacheType, url) in

上面的代码Swift3.2中编译正常,Swift4中报错:
Cannot convert value of type ‘(_) -> ()’ to expected argument type ‘DownloadProgressBlock?’ (aka ‘Optional<(Int64, Int64) -> ()>’)
修正方式:

KingfisherManager.shared.retrieveImage(with: url, options: options, progressBlock: {_, _ in}, completionHandler: { [weak self] (image, error, cacheType, url) in
or
KingfisherManager.shared.retrieveImage(with: url, options: options, progressBlock: nil, completionHandler: { [weak self] (image, error, cacheType, url) in

处理建议:使用时需要符合block中参数的个数

7,AVCaptureDevice、AVCaptureSession、AVMediaType等类中的全局变量无法在Swift4中继续使用
说明:
会有编译error。
处理建议:
消去编译error,工作并没有完成。你如果测试一下你的Barcode功能的话,你会发现原来运行的完好的扫码功能,扫不出来结果了。原因是回调函数不被调用了:

func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!)

苹果大大真厉害,原来是偷偷的将回调函数改为了,还有没有测试人员了??!!:

func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection)

修改了函数名后,好用了。参考:
https://stackoverflow.com/questions/46011211/barcode-on-swift-4/46027337#46027337

在参考文档中讲解的下面的功能点经证实在Swift3.2就已经有了,并不是Swif4新增加的功能:
1,有些教程中说“类型和协议的组合类型”是Swift4中新增加的特性,这是错误的,在Swift3.2就已经有个这个特性,在代码中指定类型为(类型 & 协议)是不会报错的。
2,用”“”控制字符串可以实现多行字符串字面量。
3,对于字典集合的顺序初始化,过滤等内容。

参考:
1,最全的 Swift 4 新特性解析 https://www.jianshu.com/p/c4f5db08bcab
2,What’s New in Swift 4? https://www.raywenderlich.com/163857/whats-new-swift-4

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页