Swift学习之路五(构造过程Initialization)

写在前面

喜欢我们的内容,可以订阅我们的官方微信公众账号:乐Coding。我们的iOS高级开发QQ群:386572485.

构造过程(Initialization),Swift中的构造器和Objective-C中的构造函数还是有很大不同的。即使Swift中类的构造器和值类型 (枚举和结构体)的构造器也有所不同。 下面我们慢慢看到底有那些不同。

1. 构造器,构造过程包括为实例中的每个属性设置初始值和为其执行必要的准备和初始化任务;

与 Objective-C 中的构造器不同,Swift 的构造器无需返回值;

构造器以init命名。

注意:当你为存储型属性设置默认值或者在构造器中为其赋值时,它们的值是被直接设置的,不会触发任何属性观测器(property observers)


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17


struct Dog {

var name:String

//定义一个无参数的构造器

init(){

name="狗狗"

}

}

var aDog=Dog()  //如果实力生命为let类型,则属性name不可以更改,因为结构体是值类型

aDog.name="毛毛"

2.自定义构造器

你不希望为构造器的某个参数提供外部名字,你可以使用下划线_来显示描述它的外部名,以此覆盖上面所说的默认行为


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39


struct Person {

var name:String=""

var age:Int=0

//拥有一个参数的构造函数,外部参数名ofName

init(ofName nameP:String){

name=nameP

}

//ageP即作为内部参数,又作为外部参数

init(ageP :Int){

age=ageP

}

//拥有两个参数的构造函数

init(ofName nameP:String , andAge ageP:Int){

name=nameP

age=ageP

}

}

var person1=Person(ofName: "Lves")

var person2=Person(ageP: 23)

var person3=Person(ofName: "乐Coding", andAge: 1)

3. 如果你定制的类型中包含一个可以取值为空的存储类型,你可以把她定义为可选类型optional type,可选类型将自动初始化为空nil

4. 构造过程中常量属性的修改

对某个类实例来说,它的常量属性只能在定义它的类的构造过程中修改;不能在子类中修改。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30


class Programmer {

let skill:String

var name:String

//可选性类型

var content:String?

init (name:String ,skill:String){

self.name=name

self.skill=skill

}

func getName()->String{

return name

}

}

let pro=Programmer(name: "Lves",skill:"swift")

var name=pro.getName()

pro.skill

Swift 将为所有属性已提供默认值的且自身没有定义任何构造器的结构体或基类,提供一个默认的构造器。这个默认构造器将简单的创建一个所有属性值都设置为默认值的实例。

如果结构体对所有存储型属性提供了默认值且自身没有提供定制的构造器,它们能自动获得一个逐一成员构造器

5.构造器代理

构造器代理:构造器通过调用其它构造器来完成实例的部分构造过程,目的是为了减少代码重复;

构造器代理的实现规则和形式在值类型和类类型中有所不同,先说一下值类型(枚举和结构体)的

1)对于值类型,你可以使用self.init在自定义的构造器中引用其它的属于相同值类型的构造器。并且你只能在构造器内部调用self.init。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45


struct Size {

var w=0.0,h=0.0

}

struct Point {

var x=0.0,y=0.0

}

struct Rect {

var origin=Point()

var size=Size()

//默认构造函数,不重新定义如果自定义构造函数后将无法调用默认构造函数

init(){}

init(origin:Point ,size:Size){

self.origin=origin

self.size=size

}

init(cent:Point ,size:Size){

let originX=cent.x-size.w/2

let originY=cent.y-size.h/2

//调用默认构造函数

self.init(origin:Point(x: originX, y: originY),size:size)

}

}

6. 类的继承和构造过程 (翻译自 ios8 programming language,感谢 http://numbbbbb.gitbooks.io/)

类里边包括从父类继承来的存储性属性,都要在构造过程中初始化。swift为类提供了两种:指定构造器和便利构造器。

引用:

指定构造器是类中最主要的构造器。一个指定构造器将初始化类中提供的所有属性,并根据父类链往上调用父类的构造器来实现父类的初始化。

每一个类都必须拥有至少一个指定构造器。在某些情况下,许多类通过继承了父类中的指定构造器而满足了这个条件。具体内容请参考后续章节自动构造器的继承。

便利构造器是类中比较次要的、辅助型的构造器。你可以定义便利构造器来调用同一个类中的指定构造器,并为其参数提供默认值。你也可以定义便利构造器来创建一个特殊用途或特定输入的实例。

你应当只在必要的时候为类提供便利构造器,比方说某种情况下通过使用便利构造器来快捷调用某个指定构造器,能够节省更多开发时间并让类的构造过程更清晰明了。

规则 1

指定构造器必须调用其直接父类的的指定构造器。

规则 2

便利构造器必须调用同一类中定义的其它构造器。

规则 3

便利构造器必须最终以调用一个指定构造器结束。

一个更方便记忆的方法是:

指定构造器必须总是向上代理

便利构造器必须总是横向代理

7. 两段式构造过程 (翻译自 ios8 programming language)

Swift 中类的构造过程包含两个阶段。第一个阶段,每个存储型属性通过引入它们的类的构造器来设置初始值。当每一个存储型属性值被确定后,第二阶段开始,它给每个类一次机会在新实例准备使用之前进一步定制它们的存储型属性。

Swift 编译器将执行 4 种有效的安全检查,以确保两段式构造过程能顺利完成:

安全检查 1

指定构造器必须保证它所在类引入的所有属性都必须先初始化完成,之后才能将其它构造任务向上代理给父类中的构造器。

如上所述,一个对象的内存只有在其所有存储型属性确定之后才能完全初始化。为了满足这一规则,指定构造器必须保证它所在类引入的属性在它往上代理之前先完成初始化。

安全检查 2

指定构造器必须先向上代理调用父类构造器,然后再为继承的属性设置新值。如果没这么做,指定构造器赋予的新值将被父类中的构造器所覆盖。

安全检查 3

便利构造器必须先代理调用同一类中的其它构造器,然后再为任意属性赋新值。如果没这么做,便利构造器赋予的新值将被同一类中其它指定构造器所覆盖。

安全检查 4

构造器在第一阶段构造完成之前,不能调用任何实例方法、不能读取任何实例属性的值,self的值不能被引用。

以下是两段式构造过程中基于上述安全检查的构造流程展示:

阶段 1

某个指定构造器或便利构造器被调用;

完成新实例内存的分配,但此时内存还没有被初始化;

指定构造器确保其所在类引入的所有存储型属性都已赋初值。存储型属性所属的内存完成初始化;

指定构造器将调用父类的构造器,完成父类属性的初始化;

这个调用父类构造器的过程沿着构造器链一直往上执行,直到到达构造器链的最顶部;

当到达了构造器链最顶部,且已确保所有实例包含的存储型属性都已经赋值,这个实例的内存被认为已经完全初始化。此时阶段1完成。

阶段 2

从顶部构造器链一直往下,每个构造器链中类的指定构造器都有机会进一步定制实例。构造器此时可以访问self、修改它的属性并调用实例方法等等。

最终,任意构造器链中的便利构造器可以有机会定制实例和使用self。

感觉构造器的执行过程有点像递归构造(小编个人猜测)

8.构造器的继承和重载:Swift 中的子类不会默认继承父类的构造器

如果你重载的构造器是一个指定构造器,你可以在子类里重载它的实现,并在自定义版本的构造器中调用父类版本的构造器。

如果你重载的构造器是一个便利构造器,你的重载过程必须通过调用同一类中提供的其它指定构造器来实现。

与方法、属性和下标不同,在重载构造器时你没有必要使用关键字override;

假设要为子类中引入的任意新属性提供默认值,请遵守以下2个规则:

规则 1

如果子类没有定义任何指定构造器,它将自动继承所有父类的指定构造器。

规则 2

如果子类提供了所有父类指定构造器的实现–不管是通过规则1继承过来的,还是通过自定义实现的–它将自动继承所有父类的便利构造器。

即使你在子类中添加了更多的便利构造器,这两条规则仍然适用。

语法:

指定构造器的写法和结构体的构造器一样


1

2


init(p){

}

便利构造器也采用相同样式的写法,但需要在init关键字之前放置convenience关键字,并使用空格将它们俩分开:


1

2

3


convenience init(parameters) {

statements

}

定义一个基类,人


1

2

3

4

5

6

7

8

9

10

11

12

13

14


class Human{

var name:String

//构造函数,指定构造器

init(name:String){

self.name=name

}

//便利构造器

convenience init(){

//调用本类的指定构造器

self.init(name:"noname")

}

}

let aMan=Human(name: "LvesLi")

init(name:String) 被当做指定构造器是因为它确保所有新Human实例中的存储性属性都能被初始化;

Human类没有父类,所以init(name: String)构造器不需要调用super.init()来完成构造

定义第二个类是Human的子类Student。Student拥有自己的属性Grade:Int

在Student的指定构造器中,先初始化自己的新属性,然后代理给父类的指定构造器.这个过程满足两段式构造过程中的安全检查1

便利构造器让实力创建更方便快捷,它调用了本类的指定构造器。便利构造器只是简单的将任务代理给了同一类里提供的指定构造器。

因为这个便利构造器重写父类的指定构造器init(name: String),必须在前面使用使用override标识。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18


class Student :Human {

var grade:Int

//指定构造器

init(name:String,grade:Int){

self.grade=grade

super.init(name: name)

}

//便利构造器

override convenience init(name: String) {

self.init(name:name,grade:1)

}

}

//创建实例,三个构造器都可以

let st1=Student()

let st2=Student(name: "Lves")

let st3=Student(name: "Lves", grade: 4)

定义第三个类 UniversityStudent(大学生),多了一个属性heterosexual bool,是否有女/男朋友

下面的类没有定义构造器是因为他的新属性有默认值,将自动继承所有父类中的指定构造器和便利构造器。


1

2

3

4

5

6

7

8

9

10

11

12

13

14


class UniversityStudent: Student {

var heterosexual:Bool=false

var desciption:String{

return "\(name) is \(grade) grade,and if has a heterosexual:\(heterosexual) "

}

}

let st4=UniversityStudent()

let st5=UniversityStudent(name: "Lves")

let st6=UniversityStudent(name: "Lves", grade: 4)

st6.desciption //Lves is 4 grade,and if has a heterosexual:false

9. 通过闭包和函数来设置属性的默认值

如果某个存储型属性的默认值需要特别的定制或准备,你就可以使用闭包或全局函数来为其属性提供定制的默认值。每当某个属性所属的新类型实例创建时,对应的闭包或函数会被调用,而它们的返回值会当做默认值赋值给这个属性。

这种类型的闭包或函数一般会创建一个跟属性类型相同的临时变量,然后修改它的值以满足预期的初始状态,最后将这个临时变量的值作为属性的默认值进行返回。

下面列举了闭包如何提供默认值的代码概要:


1

2

3

4

5

6

7

8

9

10

11

12

13


class SomeClass {

let someProperty: SomeType = {

// 在这个闭包中给 someProperty 创建一个默认值

// someValue 必须和 SomeType 类型相同

return someValue

}()

}

注意:

1.闭包结尾的大括号后面接了一对空的小括号。这是用来告诉 Swift 需要立刻执行此闭包。如果你忽略了这对括号,相当于是将闭包本身作为值赋值给了属性,而不是将闭包的返回值赋值给属性。

2.如果你使用闭包来初始化属性的值,请记住在闭包执行时,实例的其它部分都还没有初始化。这意味着你不能够在闭包里访问其它的属性,就算这个属性有默认值也不允许。同样,你也不能使用隐式的self属性,或者调用其它的实例方法。

未完待续.......继续阅读请到我们的博客:http://www.lvesli.com 。有更多iOS开发的资源,你也可以订阅我们的官方微信账号:乐Coding.或者扫描下方

时间: 2024-12-15 07:13:07

Swift学习之路五(构造过程Initialization)的相关文章

Swift 的类、结构体、枚举等的构造过程Initialization(上)

构造过程是为了使用某个类.结构体或枚举类型的实例而进行的准备过程.这个过程包含了为实例中的每个属性设置初始值和为其执行必要的准备和初始化任务. 构造过程是通过定义构造器(Initializers)来实现的,这些构造器可以看做是用来创建特定类型实例的特殊方法.与 Objective-C 中的构造器不同,Swift 的构造器无需返回值,它们的主要任务是保证新实例在第一次使用前完成正确的初始化. 类实例也可以通过定义析构器(deinitializer)在类实例释放之前执行特定的清除工作.想了解更多关于

Swift 学习笔记十五:扩展

扩展就是向一个已有的类.结构体或枚举类型添加新功能(functionality).扩展和 Objective-C 中的分类(categories)类似.(不过与Objective-C不同的是,Swift 的扩展没有名字.) Swift 中的扩展可以: 1.添加计算型属性和计算静态属性 2.定义实例方法和类型方法 3.提供新的构造器 4.定义下标 5.定义和使用新的嵌套类型 6.使一个已有类型符合某个协议 一.扩展属性,构造器,方法 class Human{ var name:String? va

swift学习笔记(五)构造过程

构造过程是为了使用某个类.结构体或枚举类型的实例而进行的准备过程.在构造过程中,对每一个属性进行了初始值预设和其它必要的准备和初始化工作. 与OC相比,swift的构造函数.不须要返回值.同一时候,在类和结构体的构造过程中,必须对全部的存储类型属性,包括继承自父类的属性.赋予合适的初始值.存储类型值不能处于一个未知状态. 在对属性进行初始化过程中,有两种方法,第一:使用构造方法,第二:在定义属性时,直接赋予默认值. 当使用构造方法对属性赋值时,不会触发不论什么的属性观測器. 当一个属性总是使用同

Swift 的类、结构体、枚举等的构造过程Initialization(下)

类的继承和构造过程 类里面的全部存储型属性--包含全部继承自父类的属性--都必须在构造过程中设置初始值. Swift 提供了两种类型的类构造器来确保全部类实例中存储型属性都能获得初始值,它们各自是指定构造器和便利构造器. 指定构造器和便利构造器 指定构造器是类中最基本的构造器.一个指定构造器将初始化类中提供的全部属性,并依据父类链往上调用父类的构造器来实现父类的初始化. 每个类都必须拥有至少一个指定构造器.在某些情况下,很多类通过继承了父类中的指定构造器而满足了这个条件.详细内容请參考兴许章节自

SICP学习笔记及题解—构造过程抽象(三)

主要内容 高阶过程:以过程为参数和/或返回值的过程 lambda 表达式 let 表达式 用过程作为解决问题的通用方法 求函数的 0 点 求函数的不动点 返回过程值 过程是语言里的一等公民 (first-class object) 1.3.1高阶过程 过程是抽象,一个过程描述了一种对数据的复合操作,如求立方过程:(define (cube x) (* x x x)) 换个方式,也可以总直接写组合式:(* 3 3 3), (* x x x), 不定义过程,总基于系统操作描述,不能提高描述的层次,

SICP学习笔记及题解---构造过程抽象(一)

有段时间没看这本书了. 而且在做笔记的时候产生了一些疑问,觉得这样照着书做笔记没什么意义.于是乎,改变了一下做法.改成先提出疑问,记下重点,然后结合实际案例学习相关东西,最后附上题解, ok,下面就是第一次的笔记.(依旧是旧套路的) 本节内容 l  讨论基本的Scheme语法规则 l  过程的定义 l  代换模型 l  条件表达式和谓词 l  过程抽象 l  与C语言比较 程序设计的基本元素 所有的高级的语言都会在把简单的认知组合起来形成复杂认识的方法上有独到之处.而且每个强有力的语言都为此提供

开始Swift学习之路

Swift出来好几个月了,除了同事分享点知识外,对swift还真没有去关心过.GitHub上整理的学习Swift资料还是很不错的,目前也推出了电子书和PDF格式. Swift的语法和我们平常开发的语言语法还是有很大不同的,Swift不需要写;结束符,变量声明的时候可以不用指明类型.在Playground下面可以立即看到写的代码效果,还是蛮酷的. 由于每天时间有限,只能一点点的慢慢了解. var和let 这两个都是声明变量,但是let声明的变量只能赋值一次,类似我们的const. if else

swift学习第十五天:闭包

闭包 闭包的介绍 闭包和OC中的block非常相似 OC中的block是匿名的函数 Swift中的闭包是一个特殊的函数 block和闭包都经常用于回调 注意:闭包和block一样,第一次使用时可能不习惯它的语法,可以先按照使用简单的闭包,随着学习的深入,慢慢掌握其灵活的运用方法. 闭包的使用 block的用法回顾 定义网络请求的类 @interface HttpTool : NSObject - (void)loadRequest:(void (^)())callBackBlock; @end

Swift学习笔记(五)——在Background中启用控制台Console Output模式

在之前的介绍中可以知道,使用Xcode中的background来学习Swift是非常强大的,可以在右侧的界面中实时预览代码中的所有常量或者变量的值,非常方便.但是现在我们学习的Swift应该作为一种控制台程序出现,就像初学C,OC一样.所有代码的打印结果都可以在Console控制台中显示,以区别之前的所有参数值都显示在一起的情况.但是默认打开Background是没有所谓的控制台的.那怎么办呢?下面简述打开Background的方法. (1)打开Xcode,选择View-->Assistant