喵の窝

swift中坑爹的构造函数

面向对象编程中构造函数用于解决类(成员)的初始化问题.而自古以来,构造函数的调用顺序都是从基类的构造函数到子类的构造函数.举个栗子,比如A是B的子类,而B又是C的子类.即A->B->C(箭头表示继承于)这种结构.那么构造A对象的时候会先构造C,然后接着构造B,最后再构造A.
但是!事情到swift这里就蛋疼了起来!

在swift中,构造分两步:物理构造和逻辑构造.构造一个类的顺序为子类的物理构造->基类的构造->子类的逻辑构造.以上面A->B->C的例子来说,构造顺序就是:A的物理构造->B的物理构造->C的物理构造->C的逻辑构造->B的逻辑构造->A的逻辑构造.

在其中,物理构造指主要保证类实例的内存得到的正确的分配.在物理构造完成以后,非可选成员变量肯定不为空,并且可以正确访问.可选成员变量的值要么是nil,要么可以正确访问.不存在’成员变量指向一个非法地址’的情况.

而逻辑初始化则是让所有的成员变量的值’看起来合理’.比如某个类中有一个表示性别的成员变量m_sex,那么在逻辑初始化完成后,m_sex只能是m(男) 或者是s(攻) 咳咳..不对..或者是f(女).而不可能是abc这种没有意义的字符.

对应到代码中,物理构造写在super.init以前,逻辑构造写在super.init以后.同时,在物理构造中不允许通过self访问基类的成员.

好了.科普完毕,下面开始吐槽.

上面这段代码中,类TyLayer中有一个构造函数需要传入frame: CGRect当成参数.

在TyView类中,有一个TyLayer的成员变量.下面问题来了,要如何初始化tyLayer这个变量呢?

一开始,我是这样写的:


required init?(coder aDecoder: NSCoder) {

super.init(coder: aDecoder)

self.tyLayer = TyLayer(frame: self.bounds)

setupLayer()

}


但是编译器报错:self.tyLayer没有在调用super.init()之前初始化.

于是.我把self.tyLayer放到super.init()之前.代码变成了这样:


required init?(coder aDecoder: NSCoder) {

self.tyLayer = TyLayer(frame: self.bounds)

super.init(coder: aDecoder)

setupLayer()

}


注意红色两行的位置.这样就保证了在调用super.init()的时候,self.tyLayer被正确的初始化.但是,编译器又提示在调用super.init()之前访问了self.bounds.(物理初始化完成之前无法访问self)

于是,我又把tyLayer改成了可选类型.并且附上初值nil,然后在逻辑初始化中给tyLayer附上真正的值.如下图,注意红框处.

但是这样做的问题就是在其他成员函数中使用tyLayer就变得很麻烦,要么加上guard的监控,要么加上感叹号(‘!’)强制解引用.总归不是很优雅.

于是我又想,在物理构造的时候给TyLayer默认构造,在逻辑构造的时候再给frame赋值.

代码如下:

结果编译器提示没有合适的构造函数………………………………………………..

………………………………………………..

………………………………………………..

………………………………………………..

………………………………………………..

………………………………………………..

我!屮!艸!芔!茻!

你TM的一个构造函数搞得这么蛋疼你这是要上天吗?其他语言无论是静态的C++还是动态的OBJ-C都没你这么蛋疼啊.你TM的凭啥构造父类的时候要保证自己先’物理构造完成’.人家C++和OBJ-C都是先构造父类,然后再构造自己不也活得好好地?(估计设计swift构造函数的时候设计师的脑袋被某东西夹了-_-)

………………………………吐槽了这么多…我想出来的最终解决方案如下…

虽然还是很丑..但是也没办法了-_-

如果有同学想到更加优雅的解决方案请务必留言告诉我..

如果想不到………………………….也欢迎留言吐槽-_-