swiftswift

Swift高阶-从Swift3.3到Swift4.1

2018-07-02  本文已影响403人  6ed2651eb5b0

最近把公司的项目从Swift3.3升级到了4.1,相对于Swift2.x到Swift3而言,这次升级所做工作量并不是特别大,但仍旧是碰到了些问题。当我们在XCode里把Swift语言版本选择到4.1然后编译时,错误数总共是999+,那一刻,我和同事的心情是崩溃的,接下来我们就开始了填坑工作。所以,在此记录下我们项目中为了适配Swift4.1改动过的地方。

一.去OC化:减少隐式@objc自动推断

问题1.1: dynamic var xxx must also be @objc,insert @objc

知识点去OC化是Swift4.0以来最大的变化,这也标志着Swift开始摆脱OC,朝着自己的方向去发展。 在Swift4.0之前,凡是继承了NSObject的类,编译器在编译时会自动在该类所有的属性,方法前加上@objc,供OC代码进行调用。但是在Swift4.0及之后,即使继承于NSObject的类编译器也不会自动的给该类所有的属性,方法前添加@objc。所以,对于我们想让OC代码访问的属性、方法,我们需要手动的在其前面加@objc。显然,dynamic是桥接OC的东西,编译器不自动添加@objc,只有我们手动的去添加了。如果项目中不再使用dynamic,可以直接将其删除。那么,问题又来了:苹果为什么这样做?为什么不让编译器自动的加@objc呢?

在Swfit4.0之前Swift中凡是继承了NSObject的类,会自动为该类的属性、方法前加上@objc,private修饰的除外,而这样也就可能导致大量不需要暴露给OC代码的属性、方法前加上@objc,大量的@objc也会导致最终二进制文件包增大。在这个OC向Swift过渡已差不多的时刻,哪里需要在哪里加,也显得更为合理。

问题1.2: Argument of '#selector' refers to instance method 'xxx()' that is not exposed to Objective-C Add '@objc' to expose this instance method to Objective-C

问题1.3:Cannot override a non-dynamic class declaration from an extension

问题1.4: 方法的调用使用NSSelecrotFromString("")导致项目崩溃

问题1.5: 在Swift4下,MJExtension这个字典转模型框架不好使了,转出的模型的属性值都是空的。

二.新增Substring类型

三.实现协议中的属性类型必须完全匹配

是否有看出这段代码的问题。Model遵守了myProtocol协议,并且实现了协议中的属性params。但是Model里params的类型是[String : String]?,而协议里myProtocol的类型却是[String : Any]? ,就这么一个类型之差,直接导致我们项目中请求接口不能成功。那么为什么呢?在swift4.0之前,在model里的params处会给出警告,警告信息是说你与协议中属性类型不一致,你到底是要新定义个属性还是实现协议里的属性啊,在swift4之前,编译器会默认认为是实现协议里的属性,虽然类型不一样,仅是给你个警告。但是在swift4之后,一向擅长变脸的苹果爸爸变了,在swift4中如果你这样写,编译器会默认为你定义了个新的属性,与协议中的属性无关。注意:协议中的属性是可选的,遵守了协议可以选择不实现这个属性。那么,这种不一致会导致什么问题呢?看Demo

 override func viewDidLoad() {
      super.viewDidLoad()
       let m = Model()
      m.params = ["t1":"123"]
      test(m)
  }

func test(_ model:myProtocol) {
    if let _ = model.params {
        print("++")
        return
    }
     print("--")
}

下面这段代码为对模型Model的使用。运行结果,在swift4之前,打印++,在swift之后,打印--。即在swift4之前,model.params不为nil,在swift4之后,model.params为nil。至于原因就是,在swift4之前,model.params取的是model里的值,而在swift4之后,model.params取的是myProtocol里的值,因为编译器不认为model实现了协议里的属性。(可能有些绕,我是彻底理解了,大家可以多读几遍)

四.在Swift4下,带一个Void参数的闭包类型,调用时可能报错Missing argument for parameter #1 in call

上述代码在swift4下编译,在success1!()处会报错,提示让传入一个参数。解决办法就是这样调用 success1!(()),或者按照success2的方式来定义success1。而success1!()这样调用在swift4之前是没事的。原因就是在swift4之后,苹果比之前更加严格的对待元组。(Void)->Void 代表一种闭包类型,这个闭包有一个Void类型的参数,并且返回值是Void,代表没有返回值。这也就是报Missing argument for parameter #1 in call错的原因,我需要一个参数,但是你没给传参数啊,所以编译器就报错了。好吧,我给你传个Void类型的参数,所以传(),因为空的元组()是Void的唯一实例,所以解决办法就是success1!(())。注意:(Void)->Void和()->Void是两种不同的类型,如果想定义一个不带参数的闭包类型,更推荐后一种方式。参考地址:https://stackoverflow.com/questions/45183961/compile-error-in-swift-4-on-parameter-passing

五.Swift4其他部分特性

5.1 NSAttributedStringKey

5.2 extension中可以访问类里的private属性

5.3 initialize方法在Swift4.0中报错

override class func initialize() {
    // some code
}

以上代码在Swift3中报警告,在swift4中直接报错:Method 'initialize()' defines Objective-C class method 'initialize', which is not permitted by Swift

总结

上述所有问题除了5.2,5.3在我们项目中没有碰到外,其余的问题均在我们项目中遇到。我们在解决问题的同时,也在思考苹果为什么要这么变。有时解决问题就是点击下鼠标的事,但是思考原因就没那么easy了。而且,自我觉得只有针对问题去解决问题,才是学习的最好的方式。如果仅仅是干巴巴的学习Swift4新特性,效果不如碰到问题解决问题更好。希望这篇文章能够帮助到做Swift4适配的小伙伴。

上一篇下一篇

猜你喜欢

热点阅读