swift正确的init姿势

源于一个segmentFault提问,为什么在init方法里,self.xxx可以在super.init()之前,而print(self)不可以。

期初猜想是self的内存分配和super的是分离的,self.xxx可以是因为这个xxx是当前类的属性,而print(self)实际会访问self所有的内存,也就包括super的部分,而这时super是没有初始化的。

然后找文档验证,虽然没有很吻合,但找到了init方法的一些原则,也算是相关。主要没有说内存方面为什么这么干,只是说swift规定了就是这么干。

文档在InitializationTwo-Phase Initialization部分。把主要的部分说一下。

  • 啥叫 Two-Phase Initialization

Class initialization in Swift is a two-phase process. In the first phase, each stored property is assigned an initial value by the class that introduced it. Once the initial state for every stored property has been determined, the second phase begins, and each class is given the opportunity to customize its stored properties further before the new instance is considered ready for use.

就是初始化分两个阶段,第一个阶段,把每一个stored property赋值,然后customize its stored properties,说实话这个我不知道具体是干啥,猜测是优化一下各个属性的内存分配之类的。关键不是这个,关键是最后的 before the new instance is considered ready for use.也就是这个对象被使用之前,必须先做好第一阶段的赋值工作。

然后是4个检测:

  • 第一个

A designated initializer must ensure that all of the properties introduced by its class are initialized before it delegates up to a superclass initializer.

简单说,调用super.init()之前必须把所有(存储)属性赋值好,这里没有提stored词修饰,实际计算属性是不用管这些的(尼玛计算属性连初始值都不需要,还管这个)。

这一条就对应了问题里的self.xxx可以在super.init()之前调用,其实不是可以而是必须!当然赋值还有其他渠道:

  1. 本身是optional类型,默认值就是nil,那不需要再赋值了。
  2. var name = "123",在声明属性时就给了默认值了,也不需要在init里搞了。

那为啥super.init()之后也可以self.xxx = xxx赋值呢?这时已经初始化结束了,就跟在其他函数里使用了一样,其实已经不属于Initialization的行为范畴了。

  • 第二个

A designated initializer must delegate up to a superclass initializer before assigning a value to an inherited property. If it doesn’t, the new value the designated initializer assigns will be overwritten by the superclass as part of its own initialization.

必须先调用super.init(),然后才可以对继承过来的属性赋值,举例:

class test1 {
    var name:String?
    init() {
        name = "1"
        print(self);
    }
}

class test2: test1 {
    override init() {
        self.name = "234"  //这里报错
        super.init()

    }
}
复制代码

注意这时候name是optional了,也会报错,跟第一点稍有不同。

  • 第三点

A convenience initializer must delegate to another initializer before assigning a value to any property (including properties defined by the same class). If it doesn’t, the new value the convenience initializer assigns will be overwritten by its own class’s designated initializer.

init方法分designated initializer和convenience initializer,后者要先调用同一个类里面的前者,在调用之后才能给自身或父类的属性赋值。举例:

convenience init(name:String, age:Int){
      self.age = 10  //这里报错
      self.init()
  }
复制代码
  • 第四点

An initializer cannot call any instance methods, read the values of any instance properties, or refer to self as a value until after the first phase of initialization is complete.

在init方法里,不能调用实例方法、读取对象属性值页不能把self当做值来使用,比如print(self),除非第一阶段已经完成。

第一阶段上面已经说了,更具体的文档里有,简单说下过程:调用init方法 --> 分配当前类的内存 --> 确认所有存储变量赋值 -->调用super.init(),沿着继承链向上,保证所有父类都完成“存储变量赋值”这一步,至此第一阶段完成。

总结下,就是当前类和它所有父类的存储变量都被分配好,然后你才能使用新建的这个对象。

所以这就是为啥print(self)在super.init()之前调用会报错。

  • 总结下正确顺序:
    1. 先给当前类的存储变量赋值
    2. 调用super.init(),如果是convenience initializer,则调用自身的designated initializer,如self.init()
    3. 给继承的属性赋值
    4. 使用新建对象
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值