这一话首先来讲写关于init的东西。
首先初始化并不会经常被用到,这是因为类和结构体中的大部分属性都会通过赋值被初始化,或者有些属性是Optional的,这样即使是nil也没关系,可以在之后再给它们赋值,就好比StoryBoard中的outlet,又或者可以使用闭包来初始化,或者使用lazy来避开init,所以有很多方法来避免init,除非你确实需要一个init的时候,那么该怎么做呢?
在一些情况下会自动生成init,其中一种情况是当类中的所有属性都有初始值的时候,你会自动得到一个没有参数的初始化方法。结构体的话它会默认得到一种将所有属性作为参数的初始化方法,仅当结构体中没有初始化方法的时候,如示例中可以通过圆括号中的赋值来初始化一个MyStruct结构体。
那么初始化方法可以做什么呢。首先我们可以在初始化方法中重置默认值,比如默认值是3,而初始化方法中把它赋值为4,那么它的值就会变成4。甚至如果你的属性是一个let定义的常量,依旧可以在init方法中为它赋值。在你的类和结构体中你可以调用其他的初始化方法,使用self.init,这样你可以调用有不同参数的其他初始化方法。当然在类中你可以调用super.init,当然调用父类的init方法时有很多规则。
那么在init的时候有什么是你必须要做的么?首先在任何的init方法完成时你必须保证所有的属性都被初始化了,注意如果有一个Optional它的值是nil,也算它有值。
在类中Swift提供两种方法来初始化,注意不是在结构体中而是在类中,一种是Convenience Initializer,其他的都是另外一种init,叫做Designated Initializers,Designated Initializers是默认的初始化方法。一个Designated Initializer只能调用它父类中的Designated Initializer,这是一个非常重要的规则,如果有一个Designated Initializer,它的前面没有convenience这个单词,必须在init中调用父类的init而不能调用自身的其他init,并且父类中的init也必须是Designated类型的。
另外,你必须在调用父类的初始化方法时,首先初始化你自己的所有属性!
你必须先让父类给它的属性赋值然后你才能给它们赋值!
Convenience Initializer有不同的特性,它必须而且只能调用本类中的designated initializer ,它不能调用任何父类的初始化方法,它可以通过其他Convenience Initializer来间接调用designated initializer。
Convenience Initializer必须直接或者间接调用designated initializer之后才能访问其他值。
最后,调用类中的方法和属性必须在初始化完成之后才能进行。
下面来聊聊继承式初始化
如果你没有在你的类中实现任何designated initializer,那么你将继承你父类中的所有designated initializers,否则你将不继承你父类中的任何designated initializer。
如果你重写了所有的designated initializer,那么你将继承父类中所有的Convenience Initializer。
如果你在初始化方法前加上required关键字的话,这个类的子类就必须实现它的这个初始化方法。
那么failable init呢?有一些初始化方法允许失败并且返回nil,它的定义中init后面跟着一个?,看了之前的几话大家对?应该是很熟悉了,示例中展示了一个UIImage,它可以通过图片名称从项目中获得一个图片,当然如果项目中没有这个图片的话,它就会返回nil。通常面对这种failable init,我们使用if let结构,如果初始化成功我们就执行动作,否则就做其他工作。在Swift中我们很少使用java中常用的try和catch,可选型的出现让try-catch变得不是必须的。
讲了这么多关于初始化的知识,那么该如何新建一个对象呢?做法是在想要创建的对象名称后面加一对圆括号,括号内是初始化方法的参数。但是并不是一直使用这种方法,有时候你会使用一个类方法或者类型方法去创建。如示例中所示,我们创建一个按钮的时候使用了UIButton这种类型的一个方法。
还有一种比较少用的初始化方法,有些时候你的一些对象会帮助你创建另一些对象,例如String中有一个非常酷的方法叫做join,join接受一个有字符串组成的数组并且用join的方式隔开,所以上面的示例会返回给你一个由“,”隔开的字符串,把myArray分割成两部分,这明显是创建了一个新的数组。