使用构造器来实现构造过程,构造器可以看做是用来创建新实例的特殊方法,与OC的构造器不同,之前是先调用父类的init再写自己的, 但是到了Swift里面, 我们却先初始化自己, 再初始化父类, 是相反的,swift中构造器无需返回值,主要任务是保证新实例在第一次使用前完成正确的初始化
类
和结构体
创建实例时,必须为所有存储类型设置初始值//类
class Student {
var name:String
var age = 12 //默认属性值(如果一个属性总是使用相同的初始值,那么为其设置一个默认值比每次都在构造器中赋值要好。两种方法的效果是一样的,只不过使用默认值让属性的初始化和声明结合得更紧密。使用默认值能让你的构造器更简洁、更清晰,且能通过默认值自动推导出属性的类型)
init() {
name = "xiaoming" //将存储属性name的值初始化为12
}
}
//结构体
struct People {
var work:String
init() {
work = "teacher"
}
}
通过输入参数和可选类型的属性来自定义构造过程
外部名字为stuAge、stuName,内部名字为ageInt、stuAge,将参数值保存在属性name、age中
class Student: NSObject {
var age:Int
var name:String
init(stuAge ageInt:Int,stuName nameStr:String) {
age = ageInt
name = nameStr
}
}
let stu = Student.init(stuAge: 12, stuName: "xiaoming")
在定义构造器时没有提供参数的外部名字,Swift 会为构造器的每个参数自动生成一个跟内部名字相同的外部名,内部参数为ageInt、nameStr,在方法调用中可以看到外部参数也是ageInt、nameStr。
class Student: NSObject {
var age:Int
var name:String
init(ageInt:Int,nameStr:String) {
age = ageInt
name = nameStr
}
}
let stu = Student.init(ageInt: 12, nameStr: "xiaoming")
不希望为构造器的某个参数提供外部名字,你可以使用下划线(_)
来显式描述它的外部名
class Student: NSObject {
var name:String
init(_ nameStr:String) {
name = nameStr
}
}
let stu = Student.init("xiaoming")
如果定义的类型包含一个逻辑上允许取值为空的存储型属性,无论是因为它无法在初始化时赋值,还是因为它在之后某个时间点可以赋值为空,你都需要将它定义为可选类型。可选类型的属性将自动初始化为nil,表示这个属性是有意在初始化时设置为空的。
class Student: NSObject {
var name:String
var age:Int? //自动赋值为nil
init(_ nameStr:String) {
name = nameStr
}
}
let stu = Student.init("xiaoming")
stu.age = 12
print(stu.name,stu.age!)
可以在构造过程中的任意时间点给常量属性赋值,只要在构造过程结束时是一个确定的值,一旦常量属性被赋值,它将永远不可更改。
class Student: NSObject {
let age:Int
init(_ ageInt:Int) {
age = ageInt
}
}
let stu = Student.init(12)
如果结构体或类的所有属性都有默认值,同时没有自定义的构造器,那么系统会给结构体或类设置一个默认的构造器,这个默认构造器创建的实例对象,其对象的所有属性值都为其默认值
class Student: NSObject {
var age:Int = 0
var name:String?
var hobby = ""
}
let stu = Student()
//由于Student类中的所有属性都有默认值,它将自动获得一个可以为所有属性设置默认值的默认构造器(尽管代码中没有显式为name属性设置默认值,但由于name是可选字符串类型,它将默认设置为nil)。上面例子中使用默认构造器创造了一个Student类的实例,并将其赋值给常量stu。
如果结构体没有提供自定义构造器,它们将自动获得一个逐一成员构造器,即使结构体的存储属性没有默认值 逐一成员构造器通过与成员属性名相同的参数名进行传值来完成对成员属性的初始化赋值
struct Size {
var w = 0.0
var h = 0.0
}
let s = Size.init(w: 2.0, h: 2.0) //结构体Size自动获得了一个逐一成员构造器init(w:, h: )
struct Size {
var width = 0.0 // 全部有默认值, 会生成2个构造器
var height = 0.0
}
struct Point {
var x = 0.0
var y = 0.0
}
struct Rect {
var origin = Point()
var size = Size()
init(){} //在功能上和系统默认构造器是一样的
init(origin:Point,size:Size)//只是简单地将origin和size的参数值赋给对应的存储型属性
{
self.origin = origin
self.size = size
}
init(center:Point,size:Size)//先通过center和size的值计算出origin的坐标,然后再调用(或者说代理给)init(origin:size:)构造器来将新的origin和size值赋值到对应的属性中
{
let originX = center.x-(size.width / 2)
let originY = center.y-(size.height / 2)
// 构造器代理
self.init(origin: Point.init(x: originX, y: originY), size: size)
}
}
let rect1 = Rect.init()
let rect2 = Rect.init(center: Point.init(x: 1.0, y: 1.0), size: Size.init(width: 6.0, height: 6.0))
let rect3 = Rect.init(center: Point.init(x: 2.0, y: 2.0), size: Size.init(width: 6.0, height: 6.0))
class People: NSObject {
var name:String
init(name:String) {
self.name = name
}
}
class People: NSObject {
var name:String
init(name:String) {
self.name = name
}
convenience override init(){
self.init(name: "xiaoming")
}
}
初始化.png
class Student {
var age : Int
init(){
age = 10
}
convenience init(a:Int){
self.init()
age = a
print(self) // 打印两次, 第一次是Student, 第二次是Boy
}
}
class Boy : Student {
var name: String
override init(){
name = "boy"
super.init()
}
}
var stu = Student(a: 2)
var boy = Boy(a: 2)
第一阶段
• 某个指定构造器或便利构造器被调用
• 完成新实例内存的分配,但此时内存还没有被初始化
• 指定构造器确保其所在类引入的所有存储型属性都已赋初值。存储型属性所属的内存完成初始化
• 指定构造器将调用父类的构造器,完成父类属性的初始化
• 这个调用父类构造器的过程沿着构造器链一直往上执行,直到到达构造器链的最顶部
• 当到达了构造器链最顶部,且已确保所有实例包含的存储型属性都已经赋值,这个实例的内存被认为已经完全初始化
第二阶段
• 从顶部构造器链一直往下,每个构造器链中类的指定构造器都有机会进一步定制实例。构造器此时可以访问self修改它的属性并调用实例方法等等
• 最终,任意构造器链中的便利构造器可以有机会定制实例和使用self
class Student {
var type:String
init() {
type = "学生"
}
}
class Boy: Student {
//第一阶段:初始化存储属性
var age = 0
override init() {
//第一阶段:初始化父类
super.init()
//第二阶段:子类自定义
age = 10
}
}
let s = Boy()
print(s.type) //学生
print(s.age) //10
当你调用Boy()时
1.首先会调用super.init()初始化父类,这时Student类的属性在自身的指定构造器中被初始化完成
2.一旦父类初始化完成,就可以初始化子类的属性,并且可以子类定制属性,这里个性化设置age =10
class ClassA {
init(a:Int){
}
init(b:Float) {
}
convenience init(c:Int){
}
convenience init(d:Float){
}
}
class ClassB: ClassA {
override init(a: Int) {
super.init(a: a)
}
override init(b: Float) {
super.init(b: b)
}
}
ClassB 中实现了 ClassA 中所有
的指定构造器,那么在初始化 ClassB 对象的时候,选择方法如下
所有的指定构造器.png
class ClassB: ClassA {
override init(a: Int) {
super.init(a: a)
}
}
ClassB 中实现了 ClassA 中个别
的指定构造器,那么在初始化 ClassB 对象的时候,选择方法如下
个别的指定构造器.png
init?
注意:
可失败构造器的参数名和参数类型,不能与其它非可失败构造器的参数名,及其参数类型相同struct Animal {
let species:String
init?(species:String) {
if species.isEmpty{
return nil
}
self.species = species
}
}
let a = Animal.init(species: "giraffe")
print(type(of:a)) //Optional<Animal>
enum TemperatureUnit {
case Celsius,Fahrenheit
init?(symbol:Character){
switch symbol {
case "C":
self = .Celsius
case "F":
self = .Fahrenheit
default:
return nil
}
}
}
let t0 = TemperatureUnit.init(symbol: "F") //Fahrenheit
let t1 = TemperatureUnit.init(symbol: "T") //nil
class Product{
let name:String!
init?(name:String) {
self.name = name
if name.isEmpty {
return nil
}
}
}
let p0 = Product.init(name: "") //nil
let p1 = Product.init(name: "A") //Product
类,结构体,枚举的可失败构造器可以传递到其他可失败构造器,而且子类的可失败构造器也能传递到父类的可失败构造器,如果你代理到的其他可失败构造器触发构造失败,整个构造过程将立即终止,接下来的任何构造代码不会再被执行
class ClassA{
let str:String!
init?(str:String) {
if str.isEmpty{
return nil
}
self.str = str
}
}
class ClassB:ClassA{
let b:Float
init?(a:String,b:Float) {
if b<1 {
return nil
}
self.b = b
super.init(str: a)
}
}
let c = ClassB.init(a: "1", b: 0) //nil
let d = ClassB.init(a: "", b: 1) //nil
let e = ClassB.init(a: "1", b: 1) //ClassB
可以用一个不可失败的构造器重新父类可失败构造器,但是不能反过来,重写父类构造器时,返回值要强制解包
class ClassA{
var str:String
init?(str:String) {
self.str = str
if str.isEmpty{
return nil
}
}
}
class classC:ClassA{
var c:String
override init(str: String) {
c = str
super.init(str: str)!
}
}
let cc = classC.init(str:"d") //classC
init?
需要解包,init!
直接使用
不管使用哪种形式,都需要判空
如果构造器用required
来修饰,那么意味着子类必须重写该父类的构造器
class ClassA{
var str:String
required init(str:String){
self.str = str
}
}
class classC:ClassA{
var c:String
required init(str: String) {
c = str
super.init(str: c)
}
}
let cc = classC.init(str: "c") //classC
如果子类继承的构造器能满足必要构造器的要求,则无须在子类中显式提供必要构造器的实现
class ClassA{
var str:String
required init(){
str = "0"
}
}
class classC:ClassA{
var c = "1"
}
let cc = classC.init() //classC
在设置存储属性默认值时,可以用函数或者闭包来实现
class SomeClass {
let someProperty: SomeType = {
// 注意: 在这里不能用self, 更不能用其它的属性(即使它有默认值, 因为self还没准备好)或者该类的实例方法,执行这段代码时, 初始化都还没有进行
// 在这个闭包中给 someProperty 创建一个默认值
// someValue 必须和 SomeType 类型相同
return someValue
}()// 最后要加上(), 不然就被当做是个闭包, 而不是这个闭包的结果了
}
struct Checkerboard {
let boardColors: [Bool] = {
var temporaryBoard = [Bool]()
var isBlack = false
for i in 1...8 {
for j in 1...8 {
temporaryBoard.append(isBlack)
isBlack = !isBlack
}
isBlack = !isBlack
}
return temporaryBoard
}()
func squareIsBlackAtRow(row: Int, column: Int) -> Bool {
return boardColors[(row * 8) + column]
}
}
每当一个新的Checkerboard实例被创建时,赋值闭包会被执行,boardColors的默认值会被计算出来并返回。上面例子中描述的闭包将计算出棋盘中每个格子对应的颜色,并将这些值保存到一个临时数组temporaryBoard中,最后在构建完成时将此数组作为闭包返回值返回。这个返回的数组会保存到boardColors中,并可以通过工具函数squareIsBlackAtRow来查询: