swift4.2_xcode 10.2由swift3.0改成4.0错误 - CSDN
  • swift4.2新特性

    2018-08-27 09:45:57
    swift4.2新特性 CaseInterable协议 条件一致性 优化Equatable 和Hashable 更加安全的Hashable 随机数生成 检查平台条件 #warning和#error编译指令 removeAll(where:) allSatisfy() toggle() last(where:)和lastIndex...

    swift4.2新特性

    CaseInterable协议

    在4.2之前我们想打印枚举里面所有的case只能自己通过以下方式去实现:

    /// swift4.2之前,错误示例
    enum Gait {
        case walk
        case trot
        case canter
        case gallop
    
        static var allCases: [Gait] = [.walk, .trot, .canter, .gallop]
    }
    
    for gait in Gait.allCases {
        print(gait)
    }

    这样每次新增和删除一个case都要去维护allCases这个数组,这样造成了诸多不便,在Swift 4.2中新增了CaseIterable协议,通过遵循这个协议,会自动生成allCases,例如:

    enum Gait: CaseIterable {
        case walk
        case trot
        case canter
        case gallop
        case jog
    }
    
    for gait in Gait.allCases {
        print(gait)
    }

    这样我们就不用去自己维护allCases这个集合了

    条件一致性

    条件一致性是在Swift 4.1引入的,例如在Swift 4.0的时候下面代码运行会报错:

    let animals = ["cat", "dog", "weasel"]
    animals.contains("dog") // OK
    let coins = [[1, 2], [3, 6], [4, 12]]
    /// Error because the element type [Int] is not equatable
    coins.contains([3, 6])

    然而在Swift 4.1之后类似这种的问题得到了解决:

    extension Array: Equatable where Element: Equatable {
        static func ==(lhs: Array<Element>, rhs: Array<Element>) -> Bool {
            let count = lhs.count
            if count != rhs.count { return false } for x in 0..<count {
            if lhs[x] != rhs[x] { return false } }
            return true
        }
    }
    
    let coins = [[1, 2], [3, 6], [4, 12]] 
    coins.contains([3, 6]) // This now works!

    类似的这种一致性有EquatableHashableEncodableDecodable

    优化EquatableHashable

    例如在Swift 4.1需要实现一下代码的功能需要这样写:

    enum Either<Left, Right> {
        case left(Left)
        case right(Right)
    }
    
    extension Either: Equatable where Left: Equatable, Right: Equatable {
        static func == (a: Either<Left, Right>, b: Either<Left, Right>) -> Bool {
            switch (a, b) {
            case (.left(let x), .left(let y)):
                return x == y
            case (.right(let x), .right(let y)):
                return x == y
            default:
                return false
            }
        }
    }
    
    extension Either: Hashable where Left: Hashable, Right: Hashable {
        var hashValue: Int {
            switch self {
            case .left(let x):
                return x.hashValue
            case .right(let x):
                return x.hashValue
            }
        }
    }
    
    var mySet = Set<Either<Int, String>>()

    但是在Swift 4.2中不用写以上代码了,帮你默认实现了:

    enum Either<Left, Right> {
        case left(Left)
        case right(Right)
    }
    
    extension Either: Equatable where Left: Equatable, Right: Equatable { }
    extension Either: Hashable where Left: Hashable, Right: Hashable { }
    
    var mySet = Set<Either<Int, String>>()

    更加安全的Hashable

    Swift 4.1我们遵循Hashable协议时需要这样写:

    struct City {
        let name: String
        let state: String
        /// 城市不用比较人口
        let population: Int
    }
    
    extension City: Equatable {
        static func ==(a: City, b: City) -> Bool {
            return a.name == b.name && a.state == b.state
        }
    }
    
    extension City: Hashable {
        var hashValue: Int {
            return name.hashValue ^ state.hashValue 
        }
    }

    Swift 4.2引入了一个新的Hasher结构,它提供了一个随机播种的通用散列函数:

    struct City {
        let name: String
        let state: String
        /// 城市不用比较人口
        let population: Int
    }
    
    extension City: Hashable {
        func hash(into hasher: inout Hasher) {
            name.hash(into: &hasher)
            state.hash(into: &hasher)
        }
    }

    Hasher中的hash合并算法可以有效的平衡hash的质量和性能,还可以低于阻断式服务攻击,它使用了App启动时生成的随机预处理种子,因此在Swift 4.2中每次启动App时的hash值不一样了,会导致之前一些有序字典或者集合变的无序,当然我们也可以添加环境变量( SWIFT_DETERMINISTIC_HASHING=1)来关掉这个功能

    随机数生成

    之前的随机数用法:

    #if os(iOS) || os(tvOS) || os(watchOS) || os(macOS)
        return Int(arc4random()) 
    #else
        return random() // or Int(rand())
    #endif
    
    // Return random number in the range 1...6 
    func diceRoll() -> Int {
        return 1 + (arc4random() % 6)
    }

    Swift 4.2中:

    let randomIntFrom0To10 = Int.random(in: 0 ..< 10)
    let randomFloat = Float.random(in: 0 ..< 1)
    let greetings = ["hey", "hi", "hello", "hola"]
    print(greetings.randomElement()!)
    
    let randomlyOrderedGreetings = greetings.shuffled()
    print(randomlyOrderedGreetings)

    还可以自定义随机生成器,遵循RandomNumberGenerator,具体用法在这里就不详细阐述了

    检查平台条件

    在之前检测平台时,代码如下:

    
    #if os(iOS) || os(watchOS) || os(tvOS)
        import UIKit
        ...
    #else
        import AppKit
        ...
    #endif

    Swift 4.2中:

     #if canImport(UIKit)
        import UIKit
        ...
     #else
         import AppKit
         ...
     #endif

    检测是否是模拟器:

    #if hasTargetEnvironment(simulator)
        ...
    #else
        #warning("We need to test this better")
        ...
    #endif

    #warning#error编译指令

    #warning("这是一个警告")
    #error("这是一个错误")

    removeAll(where:)

    /// removeAll(where:)
    var pythons = ["John", "Michael", "Graham", "Terry", "Eric", "Terry"]
    pythons.removeAll { $0.hasPrefix("Terry") }
    print(pythons)
    
    /// filter
    var python2 = ["John", "Michael", "Graham", "Terry", "Eric", "Terry"]
    python2 = python2.filter { !$0.hasPrefix("Terry") }
    print(python2)

    allSatisfy()

    //判断数组的所有元素是否全部大于85
    let scores = [86, 88, 95, 92]
    //返回一个BOOL
    let passed = scores.allSatisfy({ $0 > 85 })
    print(passed)
    
    //输出: true

    toggle()

    var isSwift = true
    //toggle函数没有返回值
    isSwift.toggle()
    print(isSwift)

    last(where:)lastIndex(where:)

    //获取满足条件的数组中的第一个值
    let a = [20, 30, 10, 40, 20, 30, 10, 40, 20]
    print(a.first(where: { $0 > 25 }))  
    print(a.index(where: { $0 > 25 }))
    print(a.index(of: 10))
    
    //输出:
    30
    1
    2
    
    //在Swift4.1中
    print((a.reversed().index(where: { $0 > 25 })?.base).map({ a.index(before: $0) }))
    //输出: 7
    
    //Swift 4.2
    //获取满足条件的元素
    print(a.last(where: { $0 > 25 }))   //40
    //获取满足条件的元素的索引
    print(a.lastIndex(where: { $0 > 25 }))   //7
    展开全文
  • 参考项目实际、官方文档、raywenderlich(传送门)等大神总结的swift语言的编码规范,适应目前swift 4.2,笔者会不定期更新,欢迎指正补充 约定,请尽量确保代码编译不残留warning,这有可以规避很多问题 目录命名...

    参考项目实际、官方文档、raywenderlich(传送门)等大神总结的swift语言的编码规范,适应目前swift 4.2,笔者会不定期更新,欢迎指正补充
    约定,请尽量确保代码编译不残留warning,这有可以规避很多问题

    文章目录

    命名规范

    常量,变量,函数,方法的命名规则使用小驼峰规则,首字母小写;

    类别名称(类、结构体、枚举和协议)使用大驼峰规则,首字母大写。

    正例

    let maximumWidgetCount = 100
    class WidgetContainer {
    	var widgetButton: UIButton
    	let widgetHeightPercentage = 0.85
    }
    

    反例

    let MAX_WIDGET_COUNT = 100
    class app_widgetContainer {
    	var wBut: UIButton
    	let wHeightPct = 0.85
    }
    

    缩写

    缩写和简写只能使用常用的或者约定俗成的缩写,缩写和简写中的所有字符的大小写要一致

    正例

    let urlString: URLString
    let userID: UserID
    

    反例

    let uRLString: UrlString
    let userId: UserId
    

    bool类型变量命名时,建议以is作为前缀

    正例

    var isMine: Bool = false
    

    代理

    在创建自定义代理方法时,第一个未命名的参数应该是代理源

    正例

    func namePickerView(_ namePickerView: NamePickerView, didSelectName name: String)
    func namePickerViewShouldReload(_ namePickerView: NamePickerView) -> Bool
    

    反例

    func didSelectName(namePicker: NamePickerViewController, name: String)
    func namePickerShouldReload() -> Bool
    

    使用类型推断的上下文

    推荐使用编译器推断的上下文来编写更加简短清晰的代码。不作为规范强制。

    正例

    let selector = #selector(viewDidLoad)
    view.backgroundColor = .red
    let toView = context.view(forKey: .to)
    let view = UIView(frame: .zero)
    

    反例

    let selector = #selector(ViewController.viewDidLoad)
    view.backgroundColor = UIColor.red
    let toView = context.view(forKey: UITransitionContextViewKey.to)
    let view = UIView(frame: CGRect.zero)
    

    使用懒加载来细致地控制对象的生命周期,对于想实现延迟加载视图的UIViewController特别有用

    // MARK: - 懒加载
    private lazy var tableView: UITableView = {
    	let tableView = UITableView.init(frame: .zero, stype: .plain)
    	tableView.separatorStyle = .none
    	tableView.rowHeight = UITableViewAutomaticDimension
    	tableView.estimatedRowHeight = 200
    	tableView.dataSource = self
    	tableView.delegate = self
    	tableView.register(UINib(nibName: homeListCell, bundle: nil), forCellReuseIdentifier: homeListCell)
    	return tableView
    }()
    

    语法规范

    最少化import

    仅导入所需要的模块,例如,当导入Foundation能满足功能时不要导入UIKit;同理,当导入UIKit后,不要再导入Foundation

    正例

    import UIKit
    var view: UIView
    var deviceModels: [String]
    
    import Foundation
    var deviceModels: [String]
    

    反例

    import UIKit
    import Foundation
    var view: UIView
    var deviceModels: [String]
    
    import UIKit
    var deviceModels: [String]
    

    可选类型拆包取值时,先使用if let判断

    if let data = result.data {
    	// do someting
    }
    

    多个可选类型拆包取值时,将多个if let 判断合并

    if let name = persion.name, let age = person.age {
    	// do something
    }
    

    尽量不要使用 as! 或 try! ,对于可选类型Optional多使用as? ,?? 可以给变量设置默认值

    // 使用if let as? 判断
    if let name = person.name as? String {
    	// doSomething
    }
    // 给name变量设置默认值
    var name = person.name ?? ""
    

    数组和字典变量定义时需要标明泛型类型,并使用更简洁清晰的语法

    var names: [String] = []
    var values: [String: Int] = [:]
    var person: [String: Any] = [:]
    

    常量定义,建议尽可能定义在类型里面,避免污染全局命名空间,如果是其他地方有可能复用的可以定义在类型外面

    static let homeListCell = "HomeListCell"
    
    class HomeListCell: UITableViewCell {
    	static let kHomeCellHeight = 80.0
    }
    

    优先使用guard来确保条件判断的简短

    func login(with username: String?, password: String?) throws -> LoginError {
    	guard let username = username else {
    		throw .noUsername
    	}
    	guard let password = password else {
    		throw .noPassword
    	}
    	// doSomething
    }
    

    使用for in表达式进行遍历

    for index in (0...10) {
    	print(index)
    }
    

    当接口版本不兼容时,使用@available(iOS x.0, *)来表明接口适配的起始系统版本

    @available(iOS 8.0, *)
    func myFunction() {
    	// doSomething
    }
    

    编码格式

    语句结束使用回车符,不使用分号

    使用二元运算符(+ - * / = == > <等)的前后都需要添加空格

    let value = 1 + 2
    

    逗号后面加一个空格

    let titleArray = [1, 2, 3, 4, 5]
    

    缩进

    方法、if、switch等左大括号不要另起一行,跟随语句放在行末,前置1空格;右大括号独占一行,除非后面跟着统一语句的剩余部分(do while、if else等)

    func myFunction {
    	if ... {
    		...
    	} else {
    		...
    	}
    }
    

    判断语句不用加括号

    if typeValue == 1 {
    	...
    }
    

    尽量不适用self,除非形参与属性同名

    func setPerson(name: String, pAge: Int) {
    	self.name = name
    	age = pAge
    }
    

    访问枚举类型时,使用更简洁的点语法

    enum Direction {
    	case north
    	case south
    	case east
    	case west
    }
    let currentDerection = .east
    

    应删除未使用的代码,包括Xcode末班代码和占位符注释,只实现超类的代理方法等(如未使用的UIApplicationDelegate方法)

    正例

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    	return Database.contacts.count
    }
    

    反例

    override func didReceiveMemoryWarning() {
    	super.didReceiveMemoryWarning()
    	// Dispose of any resources that can be recreated.
    }
    
    override func numberOfSections(in tableView: UITableView) -> Int {
    	// #warning Incomplete implementation, return the number of sections
    	return 1
    }
    
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    	// #warning Incomplete implementation, return the number of rows
    	return Database.contacts.count
    }
    

    注释

    一般的,尽量通过清晰的架构逻辑,好的符号命名来提高代码可读性;需要的时候,才辅以注释说明。
    注释是为了帮助阅读者快速读懂代码,所以要从读者的角度出发,按需注释。
    注释内容要简洁、明了、无二义性,信息全面且不冗余。
    注释跟代码一样重要。
    写注释时要换位思考,用注释去表达此时读者真正需要的信息。在代码的功能、意图层次上进行注释,即注释解释代码难以表达的意图,不要重复代码信息。
    修改代码时,也要保证其相关注释的一致性。只改代码,不改注释是一种不文明行为,破坏了代码与注释的一致性,让阅读者迷惑、费解,甚至误解。

    推荐使用// MARK: - ,按功能、协议、代理等分组

    // MARK: - UITableViewDelegate
    
    // MARK: - Action
    
    // MARK: - Request
    

    文件头注释必须包含版权许可、功能说明、作者和创建日期

    函数头注释

    推荐使用Xcode注释快捷键(⌘⌥/)

    /// <#Description#>
    ///
    /// - Parameters:
    ///   - admin: <#admin description#>
    ///   - passWord: <#passWord description#>
    func registHilinkGateWay(admin : String , passWord : String) -> Void {
    }
    

    代码注释

    代码注释放于对应代码的上方或者右边

    注释符与注释内容间空1格;右置注释与前面代码空1格;代码上方的注释,应与对应代码保持一样的缩进。

    	// 单行注释
    	doSomething()
    	
    	// 多行注释
    	// 第二行
    	doSomething()
    	
    	doSomething() // 右置注释
    

    函数

    函数设计

    避免函数过长,建议函数不超过50行(去空行去注释)

    避免函数的代码块嵌套过深,建议不要超过4层

    函数的代码块嵌套深度指的是函数中的代码控制块(如:if、for、while、switch等)之间互相包含的深度。
    推荐使用卫语句来减少if相关的嵌套层级

    优化前

    func handleReceive(receive: String) {
    	if receive {
    		let type = receive.type
    		if type != UNKNOWN {
    			// doSomething
    		}
    	}
    }
    

    优化后

    func handleReceive(receive: String) {
    	if !receive { // 使用'卫语句'
    		return
    	}
    	
    	let type = receive.type
    	if type != UNKNOWN {
    		// doSomething
    	}
    }
    

    函数的参数建议不超过5个

    闭包

    为避免循环引用,闭包内使用弱引用;为避免弱引用被提前释放,多次引用前使用强引用转换。

    resource.request().onComplete {[weak self] response in
    	guard let strongSelf = self else { return } // 强引用显式地延长self的生命周期
    	let model = strongSelf.updateModel(response)
    	strongSelf.updateUI(model)
    }
    
    resource.request().onComplete {[weak self] response in
    	guard let `self` = self else { return } // 推荐使用swift语法糖``
    	let model = self.updateModel(response)
    	self.updateUI(model)
    }
    

    当闭包时函数的最后一个参数,采用尾随闭包写法

    UIView.animateWithDuration(1.0) {
    	self.myView.alpha = 0
    }
    

    当单个闭包表达式上下文清晰时,使用隐式的返回值

    arrayList.sort { a,b in
    	a > b
    }
    
    展开全文
  • 背景 WARNING ITMS-90725: "SDK Version Issue. This app was built with the iOS 11.3 SDK. Starting March 2019, all iOS apps submitted to the App Store must be built with the iOS 12.1 SDK or later, ...

     

    背景

    WARNING ITMS-90725: "SDK Version Issue. This app was built with the iOS 11.3 SDK.
    Starting March 2019, all iOS apps submitted to the App Store must be built with the iOS 12.1 SDK or later, 
    included in Xcode 10.1 or later."

    2019年3月份起,就不可以用当下的Xcode 9.4.1打包上传代码了,升级Xcode 10迫在眉睫。上周五开会立下军令状,3日内完成Swift 4.1到Swift 4.2的语言升级。今天已是周一,好在如期完成,才有时间在这闲扯几句。

     

     

    准备原料

     

    更新你的Xcode

    建议下载Xcode 10.1版本,不要直接覆盖升级,因为在对比代码差异时会用到之前的。

     

    准备最新的Cocoapods环境,你可能会用到以下几个命令:

    • 安装或升级RVM(Ruby Version Manager - Ruby版本管理器)
    $ curl -L get.rvm.io | bash -s stable 
    $ source ~/.bashrc  
    $ source ~/.bash_profile 
    $ rvm -v  

     

    • 用RVM升级Ruby
    $ rvm list known
    $ rvm install 2.4.1 
    $ ruby -v 

     

    • 升级Cocoapods版本
    $ sudo gem install -n /usr/local/bin cocoapods --pre
    $ pod --version

     

    更新本地Cocoapods库:

    可参考之前快速升级本地Cocoapods库文件的办法

    https://wangzhongyao.blog.csdn.net/article/details/84169233

    友情提示:执行上面命令的时候,不要考验自己的网速够不够快,有5块零钱去买个梯子好一些。

     

     

    实现

     

    升级Cocoapods引入的第三方库文件

    在升级语言时,如果有Pods库未升级的阻隔,那大概率编译后自己代码中的error无法显现。所以我们首先要做的是升级Pods库。

    如果引入的是OC写的库,就不必升级。

    若是Swift写的库,则需要检查是否适配了Swift 4.2语言。如下:

    #if swift(>=4.2)
    
    let curveUserInfoKey    = UIResponder.keyboardAnimationCurveUserInfoKey
    let durationUserInfoKey = UIResponder.keyboardAnimationDurationUserInfoKey
    let frameEndUserInfoKey = UIResponder.keyboardFrameEndUserInfoKey
    
    #else
    
    let curveUserInfoKey    = UIKeyboardAnimationCurveUserInfoKey
    let durationUserInfoKey = UIKeyboardAnimationDurationUserInfoKey
    let frameEndUserInfoKey = UIKeyboardFrameEndUserInfoKey
    
    #endif

    当然,在检查引入的第三方库时,可能引入该库的当前版本并没有适配。我们可升级库版本到最新,然后检查是否可用。

    在所有Pods中第三方库均检查完毕后,我们可以新建一个基于Swift 4.2的空白项目,然后将修改完毕后的Pods导入,运行如果无误,那么Pods的升级完毕。

     

    升级你自己的代码

    在Pods升级完毕后,你自己代码中的bug会一股脑儿的出来。如下图:

    error 999+?这其实只是冰山一角。具体怎么改?95%的bug可以通过提示点一点解决。4%呢需要下点儿功夫,比如说一些方法变更、枚举变更后无法通过提示一键生成。文章后面附更改方法。

    大概改了1天,刚过了情人节没几天,嗯,这个数字还不错。

    差不多第二天了,嗯,两位数纪念一下。

    当然问题大同小异,直到最后一个error,作难了。如下图:

    这种error并没有定位到哪一行,这个error在编译器提示的数量为1,也就是这么多叹号,实际上可能针对的只是一处错误。Xcode 10毕竟初来乍到,并不完善。

    经资料查证,Segmentation fault: 11 ,代表指令错误,程序在编译时无法将代码翻译成指令集。仔细检测对应位置的代码是否正确。出现情况较多的是 nil 类型没有做解包操作(注意 ?? 双可选类型),数组类型不一致等情况。

    一行一行去检查自己项目,果然在两个文件下发现了如下提示:

    可以翻译为“编译器发生内部错误。源编辑器功能有限。试图恢复...”。当然如果点了右边的小箭头,那么就跳转至Xcode官方提bug工单的平台了。当然能不能收到回复就另说了,所以建议不点。

    如果存在编译异常的文件并且和其他文件关联较少,那建议注释掉。切断与其他文件的关联后,再次编译,此方法可行。但如果编译异常的文件为基类,与其他的文件耦合度较高,那不建议这么做,只能一行一行的去检查判空、可选类型等操作。

    还好我运气不错,快速定位到了问题,如下:

    这存在于高德地图的某代理方法中,该值为OC中的NSString类型,延伸到Swift中出现了String!?类型。如果你的项目也出现了该情况,可以这样解决:

    let subtitle = (annotation.subtitle ?? "") ?? ""

    解包后可放心使用。

    最后整理一下代码,运行...

     

    附:Swift 4.1 ---> Swift 4.2 语法变更(可直接粘贴替换,十分方便)

    ### UIImagePickerControllerDelegate
    在 Swift 4.2 中 `UIImagePickerControllerReferenceURL`  `UIImagePickerControllerOriginalImage` 等由常量变为了 Struct:
    ```
    public struct InfoKey : Hashable, Equatable, RawRepresentable {
        public init(rawValue: String)
    }
    ```
    所以以下方法也需要跟着修改,如果不改是不会执行该代理方法的:
    ```
    // Swift 4.1
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any])
    ```
    改为:
    ```
    // Swift 4.2
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any])
    ```
    ### AppDelegate
    同理,还有 AppDelegate 中的方法:
    #### 1
    ```
    // Swift 4.1
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool
    ```
    修改为:
    ```
    // Swift 4.2
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool
    ```
    #### 2
    ```
    // Swift 4.1
    func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool
    ```
    修改为:
    ```
    // Swift 4.2
    func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool
    ```
    #### 3
    ```
    // Swift 4.1
    func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Void) -> Bool
    ```
    修改为:
    ```
    // Swift 4.2
    func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool
    ```
    ## 其他变更
    以下是我在升级过程中遇到的变更情况,大致整理为「通知相关,常量变更,类型变更,方法变更」四类,共大家参考:
    ## 通知相关
    #### Notification.Name.UIApplicationWillResignActive
    ```
    // Swift 4.1
    Notification.Name.UIApplicationWillResignActive
    ```
    ```
    // Swift 4.2
    UIApplication.willResignActiveNotification
    ```
    #### Notification.Name.UITextViewTextDidChange
    ```
    // Swift 4.1
    Notification.Name.UITextFieldTextDidChange
    ```
    ```
    // Swift 4.2
    UITextField.textDidChangeNotification
    ```
    #### Notification.Name.UIKeyboardWillShow
    ```
    // Swift 4.1
    Notification.Name.UIKeyboardWillShow
    ```
    ```
    // Swift 4.2
    UIResponder.keyboardWillShowNotification
    ```
    #### Notification.Name.UIKeyboardWillHide
    ```
    // Swift 4.1
    Notification.Name.UIKeyboardWillHide
    ```
    ```
    // Swift 4.2
    UIResponder.keyboardWillHideNotification
    ```
    ## 常量变更
    ##### UILayoutFittingExpandedSize
    ```
    UIKIT_EXTERN const CGSize UILayoutFittingCompressedSize NS_AVAILABLE_IOS(6_0);
    UIKIT_EXTERN const CGSize UILayoutFittingExpandedSize NS_AVAILABLE_IOS(6_0);
    ```
    UILayoutFittingExpandedSize 由常量变为了UIView 的 class 属性
    ```
    // Swift 4.1
    UILayoutFittingExpandedSize
    ```
    ```
    // Swift 4.2
    UIView.layoutFittingExpandedSize
    ```
    ```
    // Swift 4.1
    UILayoutFittingCompressedSize
    ```
    ```
    // Swift 4.2
    UIView.layoutFittingCompressedSize
    ```
    ##### AVAudioSessionRouteChangeReason
    ```
    // Swift 4.1
    AVAudioSessionRouteChangeReason
    ```
    ```
    // Swift 4.2
    AVAudioSession.RouteChangeReason
    ```
    #### UIKeyboardFrameEndUserInfoKey
    ```
    // Swift 4.1
    UIKeyboardFrameEndUserInfoKey
    ```
    ```
    // Swift 4.2
    UIResponder.keyboardFrameEndUserInfoKey
    ```
    #### UIKeyboardAnimationDurationUserInfoKey
    ```
    // Swift 4.1
    UIKeyboardAnimationDurationUserInfoKey
    ```
    ```
    // Swift 4.2
    UIResponder.keyboardAnimationDurationUserInfoKey
    ```
    #### UIKeyboardAnimationCurveUserInfoKey
    ```
    // Swift 4.1
    UIKeyboardAnimationCurveUserInfoKey
    ```
    ```
    // Swift 4.2
    UIResponder.keyboardAnimationCurveUserInfoKey
    ```
    #### kCAFillModeForwards
    ```
    // Swift 4.1
    kCAFillModeForwards
    ```
    ```
    // Swift 4.2
    CAMediaTimingFillMode.forwards
    ```
    #### kCAMediaTimingFunctionEaseInEaseOut
    ```
    // Swift 4.1
    kCAMediaTimingFunctionEaseInEaseOut
    ```
    ```
    // Swift 4.2
    CAMediaTimingFunctionName.easeInEaseOut
    ```
    #### kCALineJoinMiter
    ```
    // Swift 4.1
    kCALineJoinMiter
    ```
    ```
    // Swift 4.2
    CAShapeLayerLineJoin.miter
    ```
    ### 几种从 String 常量变为 Struct 类型
    #### UIImagePickerControllerReferenceURL
    ```
    // Swift 4.1
    UIImagePickerControllerReferenceURL
    ```
    ```
    // Swift 4.2
    UIImagePickerController.InfoKey.referenceURL
    ```
    #### UIImagePickerControllerOriginalImage
    ```
    // Swift 4.1
    UIImagePickerControllerOriginalImage
    ```
    ```
    // Swift 4.2
    UIImagePickerController.InfoKey.originalImage
    ```
    #### UIImagePickerControllerCropRect
    ```
    // Swift 4.1
    UIImagePickerControllerCropRect
    ```
    ```
    // Swift 4.2
    UIImagePickerController.InfoKey.cropRect
    ```
    #### UIImagePickerControllerMediaType
    ```
    // Swift 4.1
    UIImagePickerControllerMediaType
    ```
    ```
    // Swift 4.2
    UIImagePickerController.InfoKey.mediaType
    ```
    ## 类型变更
    ##### UITableViewCellStyle
    ```
    // Swift 4.1
    UITableViewCellStyle
    ```
    ```
    // Swift 4.2
    UITableViewCell.CellStyle
    ```
    ##### UIWindowLevelAlert
    ```
    // Swift 4.1
    UIWindowLevelAlert
    ```
    ```
    // Swift 4.2
    UIWindow.Level.alert
    ```
    ##### UIViewAnimationCurve
    ```
    // Swift 4.1
    UIViewAnimationCurve
    ```
    ```
    // Swift 4.2
    UIView.AnimationCurve
    ```
    ##### UIAlertActionStyle
    ```
    // Swift 4.1
    UIAlertActionStyle
    ```
    ```
    // Swift 4.2
    UIAlertAction.Style
    ```
    ##### UIViewContentMode
    ```
    // Swift 4.1
    UIViewContentMode
    ```
    ```
    // Swift 4.2
    UIView.ContentMode
    ```
    ##### RunLoopMode
    ```
    // Swift 4.1
    RunLoopMode
    ```
    ```
    // Swift 4.2
    RunLoop.Mode
    ```
    ##### NSAttributedStringKey
    ```
    // Swift 4.1
    NSAttributedStringKey
    ```
    ```
    // Swift 4.2
    NSAttributedString.Key
    ```
    ##### UIViewAnimationOptions
    ```
    // Swift 4.1
    UIViewAnimationOptions
    ```
    ```
    // Swift 4.2
    UIView.AnimationOptions
    ```
    ##### UITableViewAutomaticDimension
    ```
    // Swift 4.1
    UITableViewAutomaticDimension
    ```
    ```
    // Swift 4.2
    UITableView.automaticDimension
    ```
    ##### UIApplicationLaunchOptionsKey
    ```
    // Swift 4.1
    UIApplicationLaunchOptionsKey
    ```
    ```
    // Swift 4.2
    UIApplication.LaunchOptionsKey
    ```
    ##### UICollectionViewScrollPosition
    ```
    // Swift 4.1
    UICollectionViewScrollPosition
    ```
    ```
    // Swift 4.2
    UICollectionView.ScrollPosition
    ```
    ##### UIApplicationOpenURLOptionsKey
    ```
    // Swift 4.1
    UIApplicationOpenURLOptionsKey
    ```
    ```
    // Swift 4.2
    UIApplication.OpenURLOptionsKey
    ```
    ##### UIViewAutoresizing
    ```
    // Swift 4.1
    UIViewAutoresizing
    ```
    ```
    // Swift 4.2
    UIView.AutoresizingMask
    ```
    ##### AVPlayerStatus
    ```
    // Swift 4.1
    AVPlayerStatus
    ```
    ```
    // Swift 4.2
    AVPlayer.Status
    ```
    ##### NSUnderlineStyle
    NSUnderlineStyle写法更简洁了
    ```
    // Swift 4.1
    NSUnderlineStyle.styleSingle
    ```
    ```
    // Swift 4.2
    NSUnderlineStyle.single
    ```
    ##### UIButtonType
    ```
    // Swift 4.1
    UIButtonType
    ```
    ```
    // Swift 4.2
    UIButton.ButtonType
    ```
    ##### UIControlState
    ```
    // Swift 4.1
    UIControlState
    ```
    ```
    // Swift 4.2
    UIControl.State
    ```
    ##### UIControlEvents
    ```
    // Swift 4.1
    UIControlEvents
    ```
    ```
    // Swift 4.2
    UIControl.Event
    ```
    ##### UIAlertControllerStyle
    ```
    // Swift 4.1
    UIAlertControllerStyle
    ```
    ```
    // Swift 4.2
    UIAlertController.Style
    ```
    ##### UICollectionElementKindSectionHeader
    ```
    // Swift 4.1
    UICollectionElementKindSectionHeader
    ```
    ```
    // Swift 4.2
    UICollectionView.elementKindSectionHeader
    ```
    ```
    // Swift 4.1
    UICollectionElementKindSectionFooter
    ```
    ```
    // Swift 4.2
    UICollectionView.elementKindSectionFooter
    ```
    ##### UIBarButtonItemStyle
    ```
    // Swift 4.1
    UIBarButtonItemStyle
    ```
    ```
    // Swift 4.2
    UIBarButtonItem.Style
    ```
    ##### NSAttributedStringKey
    ```
    // Swift 4.1
    NSAttributedStringKey
    ```
    ```
    // Swift 4.2
    NSAttributedString.Key
    ```
    ##### UIApplicationOpenSettingsURLString
    ```
    // Swift 4.1
    UIApplicationOpenSettingsURLString
    ```
    ```
    // Swift 4.2
    UIApplication.openSettingsURLString
    ```
    ## 方法变更
    #### MKCoordinateRegionMake(CLLocationCoordinate2D centerCoordinate, MKCoordinateSpan span)
    ```
    // Swift 4.1
    MKCoordinateRegionMake(a, b)
    ```
    ```
    // Swift 4.2
    MKCoordinateRegion(center: a, span: b)
    ```
    #### MKCoordinateSpanMake(CLLocationDegrees latitudeDelta, CLLocationDegrees longitudeDelta)
    ```
    // Swift 4.1
    MKCoordinateSpanMake(0.1, 0.1)
    ```
    ```
    // Swift 4.2
    MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1)
    ```
    #### UIAccessibilityIsVoiceOverRunning()
    ```
    // Swift 4.1
    UIAccessibilityIsVoiceOverRunning()
    ```
    ```
    // Swift 4.2
    UIAccessibility.isVoiceOverRunning
    ```
    #### UIEdgeInsetsMake
    ```
    // Swift 4.1
    UIEdgeInsetsMake(10, 0, 40, 0)
    ```
    ```
    // Swift 4.2
    UIEdgeInsets(top: 10, left: 0, bottom: 40, right: 0)
    ```
    #### UIEdgeInsetsInsetRect(rect, insets)
    ```
    // Swift 4.1
    UIEdgeInsetsInsetRect(rect, insets)
    ```
    ```
    // Swift 4.2
    rect.inset(by: insets)
    ```
    #### NSStringFromCGPoint(CGPoint point);
    ```
    // Swift 4.1
    NSStringFromCGPoint(x)
    ```
    ```
    // Swift 4.2
    NSCoder.string(for: x)
    ```
    #### didMove(toParentViewController:)
    ```
    // Swift 4.1
    viewController.didMove(toParentViewController: self)
    ```
    ```
    // Swift 4.2
    viewController.didMove(toParent: self)
    ```
    #### addChildViewController()
    ```
    // Swift 4.1
    addChildViewController(viewController)
    ```
    ```
    // Swift 4.2
    addChild(viewController)
    ```
    #### removeFromParentViewController
    ```
    // Swift 4.1
    viewController.removeFromParentViewController()
    ```
    ```
    // Swift 4.2
    viewController.removeFromParent()
    ```
    ##### var childViewControllers:[UIViewController]
    ```
    // Swift 4.1
    let array = viewController.childViewControllers
    ```
    ```
    // Swift 4.2
    let array = viewController.children
    ```
    #### bringSubview(toFront:)
    ```
    // Swift 4.1
    bringSubview(toFront: view)
    ```
    ```
    // Swift 4.2
    bringSubviewToFront(view)
    ```
    #### sendSubview(toBack: headerView)
    ```
    // Swift 4.1
    sendSubview(toBack: headerView)
    ```
    ```
    // Swift 4.2
    sendSubviewToBack(headerView)
    ```
    ##### UIImageJPEGRepresentation(,)
    ```
    // Swift 4.1
    let data = UIImageJPEGRepresentation(image, 0.6)
    ```
    ```
    // Swift 4.2
    let data = image.jpegData(compressionQuality: 0.6)
    ```
    ##### UIDatePickerMode
    ```
    // Swift 4.1
    UIDatePickerMode
    ```
    ```
    // Swift 4.2
    UIDatePicker.Mode
    ```
    ##### AVAudioSession.RouteChangeReason
    ```
    // Swift 4.1
    UIScrollViewDecelerationRateFast
    ```
    ```
    // Swift 4.2
    UIScrollView.DecelerationRate.fast
    ```
    ##### UITableViewCellEditingStyle
    ```
    // Swift 4.1
    UITableViewCellEditingStyle
    ```
    ```
    // Swift 4.2
    UITableViewCell.EditingStyle
    ```
    ##### AVAudioSessionInterruptionType
    ```
    typedef NS_ENUM(NSUInteger, AVAudioSessionInterruptionType)
    {
    AVAudioSessionInterruptionTypeBegan = 1,  /* the system has interrupted your audio session */
    AVAudioSessionInterruptionTypeEnded = 0,  /* the interruption has ended */
    };
    ```
    ```
    // Swift 4.1
    AVAudioSessionInterruptionType
    ```
    ```
    // Swift 4.2
    AVAudioSession.InterruptionType
    ```
    ##### CMTimeMake
    ```
    // Swift 4.1
    CMTimeMake(0, 1)
    ```
    ```
    // Swift 4.2
    CMTimeMake(value: 0, timescale: 1)
    ```
    #### AVAudioSession setCategory
    AVAudioSession的 setCategory不能像之前版本不填写 mode了,新版写法:
    ```
    try? AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.ambient, mode: .default)
    ```

     

    展开全文
  • Swift 4.2 新特性(译)

    2018-11-09 17:53:17
    Swift 4.2 新特性(译) 一、概述二、准备三、语言演进3.1 随机数生成3.2 动态成员查找3.3 枚举实例集合3.4 新的序列方法3.5 检测序列元素3.6 条件遵守更新3.7 Hashable 增强3.8 集合中移除元素3.9 更改布尔值3.10 ...

    一、概述

    Swift 4.2 在 Xcode 10 beta 版上可以使用了,在 Swift 4.1 的基础上更新了很多语言特性,为 Swift 5 中 ABI 稳定做好准备。

    这篇文章包含了 Swift 4.2 中的重大的改变。因为 Swift 4.2 需要 Xcode 10,所以请下载安装最新的 Xcode 测试版本。

    二、准备

    Swift 4.2 和 Swift 4.1 源码兼容,但是和其他发布版本的二进制不兼容。Swift 4.2 是 Swift 5 实现 ABI 稳定(不同的 Swift 版本编译的应用程序和库之间实现兼容)的一个中间阶段。ABI 的特性在集成进最终的 ABI 之前会接收社区的大量反馈。

    三、语言演进

    在这个版本中有很多新的语言特性。例如,随机数生成,动态成员查找等等

    3.1 随机数生成

    3.1.1 随机数生成
    arc4random_uniform(_: ) 返回一个 0 - 9 之间的随机数字。这种实现方式有两个问题:

    • 需要引入 Foundation 框架,在 Linux 下无法工作。
    • Linux上的随机数生成会产生模偏差(有取模的过程,更容易随机到小的数)。
    // Swift 4.1
    let digit = Int(arc4random_uniform(10))
    

    Swift 4.2 在标准库中添加了随机数的 API SE-0202

    /// Swift 4.2
    // 1  
    let digit = Int.random(in: 0..<10)// 2
    if let anotherDigit = (0..<10).randomElement() {
     print(anotherDigit)
    } else {
     print("Empty range.")
    }// 3
    let double = Double.random(in: 0..<1)
    let float = Float.random(in: 0..<1)
    let cgFloat = CGFloat.random(in: 0..<1)
    let bool = Bool.random()
    

    注:randomElement() 如果 range 是空,返回 nil

    3.1.2 数组随机

    Swift 4.1 数组随机也是采用 C 函数的形式,这种方式会存在上面提到的问题,而且会存在 Int 和 Int32 转换的问题。

    /// swift 4.1
    let playlist = ["Nothing Else Matters", "Stairway to Heaven", "I Want to Break Free", "Yesterday"]
    let index = Int(arc4random_uniform(UInt32(playlist.count)))
    let song = playlist[index]
    

    Swift 4.2 采用了更加简单直接的方式。

    /// swift 4.2
    if let song = playlist.randomElement() {
     print(song)
    } else {
     print("Empty playlist.")
    }
    

    3.1.3 洗牌算法
    Swift 4.1 不包含任何集合的洗牌算法,所以要采用比较曲折的方式来实现。

    // 1
    let shuffledPlaylist = playlist.sorted{ _, _ in arc4random_uniform(2) == 0 }
    
    // 2
    var names = ["Cosmin", "Oana", "Sclip", "Nori"]
    names.sort { _, _ in arc4random_uniform(2) == 0 }
    

    Swift 4.2 提供了更加高效更加优雅的实现 Shuffling Algorithms

    let shuffledPlaylist = playlist.shuffled()
    names.shuffle()
    

    注:使用 shuffled() 来创建一个洗牌后的数组。使用 shuffle() 来将数组洗牌。

    3.2 动态成员查找

    Swift 4.1 使用下面的方式实现自定义下标操作。

    /// swift 4.1
    class Person {
      let name: String
      let age: Int
      private let details: [String: String]
      
      init(name: String, age: Int, details: [String: String]) {
        self.name = name
        self.age = age
        self.details = details
      }
      
      subscript(key: String) -> String {
        switch key {
          case "info":
            return "\(name) is \(age) years old."
          default:
            return details[key] ?? ""
        }
      }
    }
    
    let details = ["title": "Author", "instrument": "Guitar"]
    let me = Person(name: "Cosmin", age: 32, details: details)
    me["info"]   // "Cosmin is 32 years old."
    me["title"]  // "Author"
    

    Swift 4.2 使用动态成员查找来提供点语法来实现下标调用 Dynamic Member Lookup

    /// swift 4.2
    // 1
    @dynamicMemberLookup
    class Person {
      let name: String
      let age: Int
      private let details: [String: String]
      
      init(name: String, age: Int, details: [String: String]) {
        self.name = name
        self.age = age
        self.details = details
      }
      
      // 2
      subscript(dynamicMember key: String) -> String {
        switch key {
          case "info":
            return "\(name) is \(age) years old."
          default:
            return details[key] ?? ""
        }
      }
    }
    
    // 3
    me.info   // "Cosmin is 32 years old." 
    me.title  // "Author"
    

    使用步骤:

    • 标记 Person 为 @dynamicMemberLookup 使下标可以使用点语法
    • 遵守 @dynamicMemberLookup 实现 subscript(dynamicMember:) 方法
    • 使用点语法调用之前定义的下标

    注:编译器会在运行时动态评估下标的调用,这样就可以写出像 Python 或者 Ruby 等脚本语言一样类型安全的代码。

    动态成员查找不会和类的属性混淆。

    me.name // "Cosmin"
    me.age // 32
    

    可以使用点语法而非下标来调用 name 和 age。而且派生类可以继承基类的动态成员查找。

    @dynamicMemberLookup
    class Vehicle {
      let brand: String
      let year: Int
      
      init(brand: String, year: Int) {
        self.brand = brand
        self.year = year
      }
      
      subscript(dynamicMember key: String) -> String {
        return "\(brand) made in \(year)."
      }
    }
    
    class Car: Vehicle {}
    
    let car = Car(brand: "BMW", year: 2018)
    car.info  // "BMW made in 2018."
    

    可以通过协议拓展给已有类型添加动态成员查找

    // 1
    @dynamicMemberLookup
    protocol Random {}
    
    // 2
    extension Random {
      subscript(dynamicMember key: String) -> Int {
        return Int.random(in: 0..<10)
      }
    }
    
    // 3
    extension Int: Random {}
    
    // 4
    let number = 10
    let randomDigit = String(number.digit)
    let noRandomDigit = String(number).filter { String($0) != randomDigit }
    

    3.3 枚举实例集合

    Swift 4.1 默认没有提供访问枚举实例集合的方式,所以实现方式不是很优雅。

    enum Seasons: String {
      case spring = "Spring", summer = "Summer", autumn = "Autumn", winter = "Winter"
    }
    
    enum SeasonType {
      case equinox
      case solstice
    }
    
    let seasons = [Seasons.spring, .summer, .autumn, .winter]
    for (index, season) in seasons.enumerated() {
      let seasonType = index % 2 == 0 ? SeasonType.equinox : .solstice
      print("\(season.rawValue) \(seasonType).")
    }
    

    为了解决这个问题,Swift 4.2 给枚举类型添加了实例数组。

    // 1
    enum Seasons: String, CaseIterable {
      case spring = "Spring", summer = "Summer", autumn = "Autumn", winter = "Winter"
    }
    
    enum SeasonType {
      case equinox
      case solstice
    }
    
    // 2
    for (index, season) in Seasons.allCases.enumerated() {
      let seasonType = index % 2 == 0 ? SeasonType.equinox : .solstice
      print("\(season.rawValue) \(seasonType).")
    }
    

    如果枚举中包含 unavailable,需要将availablecase手动维护协议中的 allCases

    enum Days: CaseIterable {
      case monday, tuesday, wednesday, thursday, friday
      
      @available(*, unavailable)
      case saturday, sunday
      
      static var allCases: [Days] {
        return [.monday, .tuesday, .wednesday, .thursday, .friday]
      }
    }
    

    allCases 中只能添加 weekdays,因为 saturdaysunday 被标记为各个平台不可用。

    枚举实例数组中也可以添加有关联值的实例。

    enum BlogPost: CaseIterable {
      case article
      case tutorial(updated: Bool)
      
      static var allCases: [BlogPost] {
        return [.article, .tutorial(updated: true), .tutorial(updated: false)]
      }
    }
    

    3.4 新的序列方法

    Swift 4.1 中的 Sequence 定义了查找指定元素的第一个索引位置或者满足指定条件的第一个元素的方法。

    /// swift 4.1
    let ages = ["ten", "twelve", "thirteen", "nineteen", "eighteen", "seventeen", "fourteen",  "eighteen", "fifteen", "sixteen", "eleven"]
    
    if let firstTeen = ages.first(where: { $0.hasSuffix("teen") }), 
       let firstIndex = ages.index(where: { $0.hasSuffix("teen") }), 
       let firstMajorIndex = ages.index(of: "eighteen") {
      print("Teenager number \(firstIndex + 1) is \(firstTeen) years old.")
      print("Teenager number \(firstMajorIndex + 1) isn't a minor anymore.")
    } else {
      print("No teenagers around here.")
    }
    

    Swift 4.2 为了实现一致性重构了方法名

    /// swift 4.2
    if let firstTeen = ages.first(where: { $0.hasSuffix("teen") }), 
       let firstIndex = ages.firstIndex(where: { $0.hasSuffix("teen") }), 
       let firstMajorIndex = ages.firstIndex(of:  "eighteen") {
      print("Teenager number \(firstIndex + 1) is \(firstTeen) years old.")
      print("Teenager number \(firstMajorIndex + 1) isn't a minor anymore.")
    } else {
      print("No teenagers around here.")
    }
    

    Swift 4.1 也没有定义查找指定元素的最后一个索引的位置和满足指定条件的的最后一个元素等方法。在 Swift 4.1 中我们可能采用下面的方法来处理。

    /// swift 4.1
    // 1
    let reversedAges = ages.reversed()
    
    // 2
    if let lastTeen = reversedAges.first(where: { $0.hasSuffix("teen") }), 
       let lastIndex = reversedAges.index(where: { $0.hasSuffix("teen") })?.base, 
       let lastMajorIndex = reversedAges.index(of: "eighteen")?.base {
      print("Teenager number \(lastIndex) is \(lastTeen) years old.")
      print("Teenager number \(lastMajorIndex) isn't a minor anymore.")
    } else {
      print("No teenagers around here.")
    }
    
    

    Swift 4.2 添加了相应的方法,使用方式如下

    if let lastTeen = ages.last(where: { $0.hasSuffix("teen") }), 
       let lastIndex = ages.lastIndex(where: { $0.hasSuffix("teen") }), 
       let lastMajorIndex = ages.lastIndex(of: "eighteen") {
      print("Teenager number \(lastIndex + 1) is \(lastTeen) years old.")
      print("Teenager number \(lastMajorIndex + 1) isn't a minor anymore.")
    } else {
      print("No teenagers around here.")
    }
    
    

    3.5 检测序列元素

    Swift 4.1 中没有检查序列中所有元素是否满足某个指定条件的方法。不过你可以实现你自己的方法,例如下面检测集合中的元素是否都是偶数。

    let values = [10, 8, 12, 20]
    let allEven = !values.contains { $0 % 2 == 1 }
    

    Swift 4.2 添加了新的方法,很好的简化了代码,提升了可读性。

    let allEven = values.allSatisfy { $0 % 2 == 0 }
    

    3.6 条件遵守更新

    Swift 4.2 给拓展和标准库中添加一些条件遵守方面的改进。

    3.6.1 拓展中的条件遵守

    Swift 4.1 不能在拓展中自动合成 Equatable 的协议实现。例子如下:

    // 1
    struct Tutorial : Equatable {
      let title: String
      let author: String
    }
    
    // 2
    struct Screencast<Tutorial> {
      let author: String
      let tutorial: Tutorial
    }
    
    // 3 
    extension Screencast: Equatable where Tutorial: Equatable {
      // 必须自己实现 == 方法,Swift 4.1 不会自动合成
      static func ==(lhs: Screencast, rhs: Screencast) -> Bool {
        return lhs.author == rhs.author && lhs.tutorial == rhs.tutorial
      }
    }
    
    // 4
    let swift41Tutorial = Tutorial(title: "What's New in Swift 4.1?", author: "Cosmin Pupăză")
    let swift42Tutorial = Tutorial(title: "What's New In Swift 4.2?", author: "Cosmin Pupăză")
    let swift41Screencast = Screencast(author: "Jessy Catterwaul", tutorial: swift41Tutorial)
    let swift42Screencast = Screencast(author: "Jessy Catterwaul", tutorial: swift42Tutorial)
    let sameScreencast = swift41Screencast == swift42Screencast
    

    Swift 4.2 只需要遵守协议,不需要实现。因为编译器会添加一个默认的Equatable协议实现。

    extension Screencast: Equatable where Tutorial: Equatable {}
    

    这个特性也同样支持 HashableCodable

    // 1
    struct Tutorial: Hashable, Codable {
      let title: String
      let author: String
    }
    
    struct Screencast<Tutorial> {
      let author: String
      let tutorial: Tutorial
    }
    
    // 2
    extension Screencast: Hashable where Tutorial: Hashable {}
    extension Screencast: Codable where Tutorial: Codable {}
    
    // 3
    let screencastsSet: Set = [swift41Screencast, swift42Screencast]
    let screencastsDictionary = [swift41Screencast: "Swift 4.1", swift42Screencast: "Swift 4.2"]
    
    let screencasts = [swift41Screencast, swift42Screencast]
    let encoder = JSONEncoder()
    do {
      try encoder.encode(screencasts)
    } catch {
      print("\(error)")
    }
    

    3.6.2 条件遵守运行时查询

    Swift 4.2 实现条件遵守的动态查询。可以从下面的例子看出。

    // 1
    class Instrument {
      let brand: String
      
      init(brand: String = "") {
        self.brand = brand
      }
    }
    
    // 2
    protocol Tuneable {
      func tune()
    }
    
    // 3
    class Keyboard: Instrument, Tuneable {
      func tune() {
        print("\(brand) keyboard tuning.")
      }
    }
    
    // 4
    extension Array: Tuneable where Element: Tuneable {
      func tune() {
        forEach { $0.tune() }
      }
    }
    
    // 5
    let instrument = Instrument()
    let keyboard = Keyboard(brand: "Roland")
    let instruments = [instrument, keyboard]
    
    // 6
    if let keyboards = instruments as? Tuneable {
      keyboards.tune()
    } else {
      print("Can't tune instrument.")
    }
    

    注:上面在条件遵循的运行时检测中,会输出 "Can't tune instrument.",因为Instrument类型不遵守 Tuneable 协议,如果是两个 Keyboard 类型就可以。

    3.6.3 Hashable 在标准库中条件遵守增强

    在 Swift 4.2 中可选值、数组、字典和区间当他们的元素是 Hashable 的话,他们也是 Hashable

    struct Chord: Hashable {
      let name: String
      let description: String?
      let notes: [String]
      let signature: [String: [String]?]
      let frequency: CountableClosedRange<Int>
    }
    
    let cMajor = Chord(name: "C", description: "C major", notes: ["C", "E",  "G"], 
                       signature: ["sharp": nil,  "flat": nil], frequency: 432...446)
    let aMinor = Chord(name: "Am", description: "A minor", notes: ["A", "C", "E"], 
                       signature: ["sharp": nil, "flat": nil], frequency: 440...446)
    let chords: Set = [cMajor, aMinor]
    let versions = [cMajor: "major", aMinor: "minor"]
    

    3.7 Hashable 增强

    Swift 4.1 中一般会像下面这样实现自定义哈希函数:

    class Country: Hashable {
      let name: String
      let capital: String
      
      init(name: String, capital: String) {
        self.name = name
        self.capital = capital
      }
      
      static func ==(lhs: Country, rhs: Country) -> Bool {
        return lhs.name == rhs.name && lhs.capital == rhs.capital
      }
      
      var hashValue: Int {
        return name.hashValue ^ capital.hashValue &* 16777619
      }
    }
    
    let france = Country(name: "France", capital: "Paris")
    let germany = Country(name: "Germany", capital: "Berlin")
    let countries: Set = [france, germany]
    let countryGreetings = [france: "Bonjour", germany: "Guten Tag"]
    

    因为countriesHashable,所以可以添加到集合或者字典中。但是 hashValue 的实现很难理解并且也不高效。Swift 4.2 通过定义了一个通用的哈希函数来解决这个问题。

    class Country: Hashable {
      let name: String
      let capital: String
      
      init(name: String, capital: String) {
        self.name = name
        self.capital = capital
      }
      
      static func ==(lhs: Country, rhs: Country) -> Bool {
        return lhs.name == rhs.name && lhs.capital == rhs.capital
      }
    
      func hash(into hasher: inout Hasher) {
        hasher.combine(name)
        hasher.combine(capital)
      }
    }
    

    Country 中使用hash(into:) 来替代 hashValue。这个函数使用 combine() 将属性注入到 hasher中。

    注:现在实现上很容易,并且性能要比之前的版本高。

    3.8 集合中移除元素

    在 Swift 4.1 中,想要从集合中移除一个指定的元素,通常会使用filter(_:)的实现方式,在 Swift 4.2 添加了 removeAll(_:)

    // Swift 4.1
    var greetings = ["Hello", "Hi", "Goodbye", "Bye"]
    greetings = greetings.filter { $0.count <= 3 }
    // Swift 4.2
    greetings.removeAll { $0.count > 3 }
    

    3.9 更改布尔值

    在 Swift 4.1 中,我们通常会这样实现

    extension Bool {
      mutating func toggle() {
        self = !self
      }
    }
    
    var isOn = true
    isOn.toggle()
    

    Swift 4.2 给 Bool增加了 toggle()方法

    3.10 新的编译器指令

    Swift 4.2 定义了表述代码问题的编译器指令

    // 1
    #warning("There are shorter implementations out there.")
    
    let numbers = [1, 2, 3, 4, 5]
    var sum = 0
    for number in numbers {
      sum += number
    }
    print(sum)
    
    // 2
    #error("Please fill in your credentials.")
    
    let username = ""
    let password = ""
    switch (username.filter { $0 != " " }, password.filter { $0 != " " }) {
      case ("", ""):
        print("Invalid username and password.")
      case ("", _):
        print("Invalid username.")
      case (_, ""):
        print("Invalid password.")
      case (_, _):
        print("Logged in succesfully.")
    }
    

    #warning 用来输出警告信息,表示实现未完全完成
    #error 强制其他开发者填入 usernamepassword

    3.11 新的指针函数

    withUnsafeBytes(of:_:)withUnsafePointer(to:_:)在 Swift 4.1 中只能用于变量,所以必须拷贝一份。Swift 4.2 中该函数支持常量,不需要再保存值。

    // Swift 4.1
    let value = 10
    var copy = value
    withUnsafeBytes(of: &copy) { pointer in print(pointer.count) }
    withUnsafePointer(to: &copy) { pointer in print(pointer.hashValue) }
    // Swift 4.2
    withUnsafeBytes(of: value) { pointer in print(pointer.count) }
    withUnsafePointer(to: value) { pointer in print(pointer.hashValue) }
    

    3.12 Memory Layout 更新

    Swift 4.2 使用 keypath 查找存储属性的内存布局 [SE-0210],具体做法如下:

    // 1
    struct Point {
      var x, y: Double
    }
    
    // 2
    struct Circle {
      var center: Point
      var radius: Double
      
      var circumference: Double {
        return 2 * .pi * radius
      }
      
      var area: Double {
        return .pi * radius * radius
      }
    }
    
    // 3
    if let xOffset = MemoryLayout.offset(of: \Circle.center.x), 
       let yOffset = MemoryLayout.offset(of: \Circle.center.y), 
       let radiusOffset = MemoryLayout.offset(of: \Circle.radius) {
      print("\(xOffset) \(yOffset) \(radiusOffset)")
    } else {
      print("Nil offset values.")
    }
    
    // 4
    if let circumferenceOffset = MemoryLayout.offset(of: \Circle.circumference), 
       let areaOffset = MemoryLayout.offset(of: \Circle.area) {
      print("\(circumferenceOffset) \(areaOffset)")
    } else {
      print("Nil offset values.")
    

    注:可以通过 keypath 返回存储属性的内存偏移。计算属性返回 nil,因为没有存储关联。

    3.13 模块中的内联函数

    在 Swift 4.1 中,不允许在自己的模块中定义内联函数。依次选择 ViewNavigatorsShow Project Navigator, 右键单击Sources and 选择 New File。重命名文件为 FactorialKit.swift 并且替换为下面代码块中的代码。

    public class CustomFactorial {
      private let customDecrement: Bool
      
      public init(_ customDecrement: Bool = false) {
        self.customDecrement = customDecrement
      }
      
      private var randomDecrement: Int {
        return arc4random_uniform(2) == 0 ? 2 : 3
      }
      
      public func factorial(_ n: Int) -> Int {
        guard n > 1 else {
          return 1
        }
        let decrement = customDecrement ? randomDecrement : 1
        return n * factorial(n - decrement)
      }
    }
    

    在 Swift 4.2 中定义为内联的函数会更加高效,所以将 FactorialKit.swift 的代码替换如下。

    public class CustomFactorial {
      @usableFromInline let customDecrement: Bool
      
      public init(_ customDecrement: Bool = false) {
        self.customDecrement = customDecrement
      }
      
      @usableFromInline var randomDecrement: Int {
        return Bool.random() ? 2 : 3
      }
      
      @inlinable public func factorial(_ n: Int) -> Int {
        guard n > 1 else {
          return 1
        }
        let decrement = customDecrement ? randomDecrement : 1
        return n * factorial(n - decrement)
      }
    }
    

    四、其他更新

    下面是 Swift 4.2 中的一些其他改变

    4.1 Swift Package Manager 更新

    4.1.1 定义 Package 的 Swift 版本

    Swift 4.1 在 Package.swift 中定义了swiftLanguageVersions,所以可以在 packages 中定义主版本。

    let package = Package(name: "Package", swiftLanguageVersions: [4])
    

    Swift 4.2 中也能通过SwiftVersion定义小版本 [SE-0209]

    let package = Package(name: "Package", swiftLanguageVersions: [.v4_2])
    

    能够通过.version(_:)定义之后的版本

    let package = Package(name: "Package", swiftLanguageVersions: [.version("5")])
    

    4.1.2 Packages 定义本地版本

    在 Swift 4.1 中,可以使用仓库链接为 Package 定义依赖。如果有相互关联的 Package,就会产生额外的问题,所以 Swift 4.2 中引入了本地路径而提案[SE-0201]。

    4.1.3 给 Package 添加系统库 Target

    Swift 4.1 中系统模块包需要分仓库,这样包管理很难用 ,所以 Swift 4.2 使用系统库 Target 来实现 [SE-0208]

    4.2 移除隐式解包可选值

    在 Swift 4.1 中,你可以在嵌套类型中使用隐式解包可选值。

    let favoriteNumbers: [Int!] = [10, nil, 7, nil]
    let favoriteSongs: [String: [String]!] = ["Cosmin": ["Nothing Else Matters", "Stairway to Heaven"], "Oana": nil] 
    let credentials: (usermame: String!, password: String!) = ("Cosmin", nil)
    

    Swift 4.2 从数组、字典和元祖中移除了隐式解包可选值[ SE-0054]

    let favoriteNumbers: [Int?] = [10, nil, 7, nil]
    let favoriteSongs: [String: [String]?] = ["Cosmin": ["Nothing Else Matters", "Stairway to Heaven"], "Oana": nil] 
    let credentials: (usermame: String?, password: String?) = ("Cosmin", nil)
    

    感谢原作者 Cosmin Pupăză

    原文地址:https://www.raywenderlich.com/5357-what-s-new-in-swift-4-2

    附:大话Swift 4.1 epub 文件下载地址

    https://download.csdn.net/download/u010037928/10724897

    展开全文
  • \\\随着Swift 4.1进入beta阶段,Swift团队开始将注意力聚焦在新版本Swift 4.2的开发上。新版本除了bug修复和编译性能方面的改进,还会继续增强Application Binary Interface(ABI)稳定性。\\Swift 4.2的主要目标是...
  • Swift 4.2Swift 4的第二次小更新,随之带来了很多很棒的改进-这使得今年将成为Swift的重要一年,并且更加确认这个社区驱动的Swift演变进程正在让一个伟大的语言变得更好。这次我们获得了一些特性比如enum case ...
  • swift4.2标准库更改

    2018-09-25 16:41:20
    swift4.2发布了 1、RangeReplaceableCollection 集合添加根据条件删除方法(官方原文) protocol RangeReplaceableCollection { /// Removes every element satisfying the given predicate from the collection. ...
  • 经过大约一个月的时间的适配,项目正式使用XCode10(以下简称为10 or XC10)大部分库都升级为Swift4.2(以下简称为 4.2 or S4.2),下面是适配过程中遇到的一些坑。 1. Swift4、Swift4.2混编 如果你对项目是小的独立...
  • 可以说是最用心的关于swift4.2的中文翻译版本了;但下载的PDF都是分散的一个个的文件,现在把它整理到单一的文件中,方便了学习。请开心不用切换干扰下学习吧~~~
  • Swift 4.2正式发布

    2019-04-09 09:40:27
    Swift 4发布了一年之后,Swift 4.2版本也正式推出。这一版本带来了语言和标准库方面的改进,包括更好的泛型、Hashable协议以及随机数生成。另外,Swift语言维护者Ted Kremenek写道,Swift 4能提供更快的编译速度,...
  • Swift 提供了三种基本数据类型,包括数组、集合、字典,都可用于存储值集合。数组是值的有序集合。集合是无序值的唯一值集合。字典是关键值关联的无序集合。   Swift 中的数组、集合、字典对它们可以存储的值和...
  • 本文记录了将工作项目由Swift4版本升级到Swift4.2中修改的内容,需要修改876处错误: ‘UIImageOrientation’ has been renamed to ‘UIImage.Orientation’ ‘UIApplicationWillResignActive’ has been ...
  • Get started with push notifications on iOS! Currently updated to: Platform: iOS12; Language: Swift4.2; Editor: Xcode10
  • swift里字符串进行了一些扩展,基本就是重写了oc sub方法 1.从头切,切到哪里. str.bdSubString(to: 6) 2.从哪里切,切到头 str.bdSubString(from: 2) 3从哪里切到哪里 str.bdSubString(from: 0, to: 6) 4 ...
  • Core Data By Tutorials Fifth Edition IOS 12 and Swift 4.2 edittion Ray Wenderlich Download PDF
  • Swift 4.2 新特性更新

    2019-02-27 00:20:05
    Swift 4.2Swift 4.0发布以来的第二次小更新, 继上次Xcode 9.3和Swift 4.1发布以来也有俩月有余 上个版本Swift 4.1 的新特性中介绍了条件一致性和哈希索引等相关更新 随着Xcode Bate 10的发布, Swift 4.2也发布了...
  • swift4.2 之AVFoundation 调用摄像头 override func viewDidLoad() { super.viewDidLoad() setupScanSession() startScan() } //MARK: 方法 func setupScanSession(){ do{ //设置捕捉设备 ...
1 2 3 4 5 ... 20
收藏数 3,440
精华内容 1,376
关键字:

swift4.2