最近完成了公司项目由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