Khala-Wan`

Swift4.0:尝鲜以及第一次翻车纪念

• Swift4.0

昨天凌晨WWDC2017刚发布了Xcode9 Beta包含了Swift4.0,正好最近在做MacOS的项目,因为项目是个人的,所以果断用来尝鲜。但是最后还是翻车了。所以写个博客纪念我在Swift4.0翻的第一次车。😂

初体验

4.0这一次更新的兼容程度相比之前2.3到3.0那次更新真的可以说是很良心了。在修改了差不多30行代码,花费5分钟左右时间之后,项目已经彻底兼容了Swift4.0.这个结果真的让我惊讶,还以为需要花费点功夫呢。至于Swift4.0的优化点,iOS的还没来得及看,MacOS上最让我开心的是针对各种NSButton的Status的改变。 3.1时期使用NSCheckBox的时候在StoryBoard中查看它的相关属性可以看到一个叫做Status的属性,有三个值:

  • On
  • Off
  • Mixed

OK,大家都能看懂,然后拖线使用时,获取sender:NSButton的Status就成了Int类型的值。

0 -> Off ; 1 -> On

虽然不影响使用,但是从代码的阅读性上来讲真的是槽点太多。 但是本次更新之后我欣喜的发现,这个status变成了枚举✌️。

open var state: NSControl.StateValue

诸如此类还有很多,比如:NSOpenPanel Begin之后闭包里面的result也从Int换成了枚举。

从这点优化来看,Apple是不是终于记起来优化Cocoa框架复杂又恶心的API了?具体还有什么变化我就不废话了。之后网上肯定有铺天盖地各种博客给大家看。

翻车现场

言归正传,我们来讲讲这次翻车的过程。我为了从XIB中实例化我的NSWindow,给NSWinow写了一个扩展方法如下:

extension NSWindow{
    class func loadWithNibName(_ name:String , _ loadClass:AnyClass) -> NSWindow {
        let nib:NSNib = NSNib.init(nibNamed: NSNib.Name(rawValue: name), bundle: nil)!
        var tempObjects = NSArray()
        if (!nib.instantiate(withOwner: nil, topLevelObjects: &tempObjects)){
            return NSWindow()
        }
        for object in tempObjects!{

            if (object as AnyObject).classForCoder == loadClass {
                return object as! NSWindow
            }
        }
        return NSWindow()
    }
}

当时切换到Swift4.0的时候instantiate(withOwner: nil, topLevelObjects: &tempObjects)这个API报错,说&tempObjects这个参数的类型不对。于是跳进去看定义,尴尬了,之前这里让传UnsafePoint,也就是指针,现在需要传进去一个AutoreleasingUnsafeMutablePointer比之前多了个Autoreleasing 话不多说,Xcode让怎么办就怎么办呗,于是修改如下:


!nib.instantiate(withOwner: nil, topLevelObjects: AutoreleasingUnsafeMutablePointer.init(&tempObjects))

恩。。。编译通过,看着没什么问题了。运行起来!结果你也猜到了,崩溃了。崩溃直接进了AppDelegate。lldb没有任何提示,于是果断全局断点伺候,无效。还是没抓到。于是仔细看下代码报错原因: 

EXC_BADACCESS:这是访问了一个已经被release了的对象。

于是我猜测应该是这个TempObjects数组被提前Release了。打开Scheme的编辑窗,在Diagnostics中勾选Malloc StackZombie Objects这两个选项。然后来让我们看一下是不是TempObjects被提前释放了。 结果如下:

果然是它被提前释放了。对比之前的代码可以发现,使用了AutoreleasingUnsafeMutablePointer这个类型之后才出现这个问题,因为TempObjects是一个局部变量,在函数执行完之后就会被释放,在这之后AutoreleasingUnsafeMutablePointer又试图去Release它,所以造成了EXC_BADACCESS. 于是修改代码如下:

extension NSWindow{
    class func loadWithNibName(_ name:String , _ loadClass:AnyClass) -> NSWindow {
        let nib:NSNib = NSNib.init(nibNamed: NSNib.Name(rawValue: name), bundle: nil)!
        var tempObjects:NSArray? = NSArray()
        if (!nib.instantiate(withOwner: nil, topLevelObjects: &tempObjects)){
            return NSWindow()
        }
        for object in tempObjects!{

            if (object as AnyObject).classForCoder == loadClass {
                return object as! NSWindow
            }
        }
        return NSWindow()
    }
}

将TempObjects的类型声明成Optional即可。看来不能全部听信Xcode的自动修复。😂

##最后 这就是我Swift4.0的初体验和第一次翻车过程。Swift4.0和Xcode9还有其他新特性和好玩的东西,相信过不了多久大家都会将它们发掘出来的。就说这么多,我要去玩玩这两个新玩具了。哈哈。