协议
协议是对实例行为的一种约束,和ObjC类似,在Swift中可以定义属性和方法(ObjC中之所以能定义属性是因为@property的本质就是setter、getter方法)。和其他语言不同的是Swift中的协议不仅限于类的实现,它同样可以应用于枚举、结构体(如果只想将一个协议应用于类,可以在定义协议时在后面添加class关键字来限制其应用范围)。
|
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
|
protocol Named{ //定义一个实例属性 var name:String { get set } //定义一个类型属性 static var className:String { get } //定义构造方法 init(name:String) //定义一个实例方法 func showName() //定义一个类型方法 static func showClassName()}protocol Scored{ var score:Double { get set }}//Person遵循了Named协议class Person:Named { //注意从Named协议中并不知道name是存储属性还是计算属性,这里将其作为存储属性实现 var name:String var age:Int = 0 static var className:String{ return "Person" } //协议中规定的构造方法,必须使用required关键字声明,除非类使用final修饰 required init(name:String){ self.name=name } //遵循showName方法 func showName()
{ println("name=\(name)") } //遵循showClassName方法 static func showClassName()
{ println("Class
name is \"Person\"") }}//Student继承于Person并且实现了Scored协议class Student: Person,Scored { var score:Double=0.0 init(name:String, score:Double){ self.score = score super.init(name: name) } //由于上面自定义了构造方法则必须实现协议中规定的构造方法 required init(name: String)
{ super.init(name: name) } func test(){ println("\(self.name)
is testing.") }}var p=Person(name: "Kenshin
Cui")p.showName() //结果:name=Kenshin
Cuiprintln("className=\(Person.className)") //结果:className=PersonPerson.showClassName() //结果:Class
name is "Person"p.age=28var s:Named=Student(name: "Kaoru",score:100.0) //尽管这里将s声明为Named类型,但是运行时仍然可以正确的解析(多态),但是注意此时编译器并不知道s有test方法,所以此时调用test()会报错s.showName()//在下面的函数中要求参数stu必须实现两个协议func showMessage(stu:protocol<Named,Scored>){ println("name=\(stu.name),score=\(stu.score)")}var s2=Student(name: "Tom",score:99.0)showMessage(s2) //结果:name=Tom,age=99.0//检测协议let b1 = s is Scored //判断p是否遵循了Scored协议if b1 { println("s
has score property.")}//类型转化if let s3 = s as? Scored { //如果s转化成了Scored类型则返回实例,否则为nil println("s3'
score is \(s3.score)") //结果:s3'
score is 100.0}let s4 = s as! Scored //强制转换,如果转化失败则报错println("s4'
score is \(s4.score)") //结果:s4'
score is 100.0 |
- 协议中虽然可以指定属性的读写,但即使协议中规定属性是只读的但在使用时也可以将其实现成可读写的;
- Swift的协议中可以约定属性是实例属性还是类型属性、是读写属性还是只读属性,但是不能约束其是存储属性还是计算属性;
- 协议中的类型属性和类型方法使用static修饰而不是class(尽管对于类的实现中类型属性、类型方法使用class修饰);
- 协议中约定的方法支持可变参数,但是不支持默认参数;
- 协议中约定的构造方法,在实现时如果不是final类则必须使用require修饰(以保证子类如果需要自定义构造方法则必须覆盖父类实现的协议构造方法,如果子类不需要自定义构造方法则不必);
- 一个协议可以继承于另外一个或多个协议,一个类只能继承于一个类但可以实现多个协议;
- 协议本身就是一种类型,这也体现除了面向对象的多态特征,可以使用多个协议的合成来约束一个实例参数必须实现某几个协议;
扩展
Swift中的扩展就类似于ObjC中的分类(事实上在其他高级语言中更多的称之为扩展而非分类),但是它要比分类强大的多,它不仅可以扩展类还可以扩展协议、枚举、结构体,另外扩展也不局限于扩展方法(实例方法或者类型方法),还可以扩展便利构造方法、计算属性、下标脚本、
|
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
46
47
48
49
50
51
52
53
54
|
class Person { var firstName:String,lastName:String var age:Int=0 var fullName:String{ get{ return firstName+"
"+lastName } } init(firstName:String,lastName:String){ self.firstName=firstName self.lastName=lastName } func showMessage(){ println("name=\(fullName),age=\(age)") }}extension Person{ //只能扩展便利构造方法,不能扩展指定构造方法 convenience init(){ self.init(firstName:"",lastName:"") } //只能扩展计算属性,无法扩展存储属性 var personInfo:String{ return "firstName=\(firstName),lastName=\(lastName),age=\(age)"; } //扩展实例方法 func sayHello(){ println("hello
world.") } //嵌套类型 enum SkinColor{ case Yellow,White,Black } //扩展类型方法 static func skin()->[SkinColor]{ return [.Yellow,.White,.Black] }}var p=Person()p.firstName="Kenshin"p.lastName="Cui"p.age=28println(p.personInfo) //结果:firstName=Kenshin,lastName=Cui,age=28p.sayHello() //结果:hello
world.Person.skin() |
结构体
结构体和类是构造复杂数据类型时常用的构造体,在其他高级语言中结构体相比于类要简单的多(在结构体内部仅仅能定义一些简单成员),但是在Swift中结构体和类的关系要紧密的多,这也是为什么将结构体放到后面来说的原因。Swift中的结构体可以定义属性、方法、下标脚本、构造方法,支持扩展,可以实现协议等等,很多类可以实现的功能结构体都能实现,但是结构体和类有着本质区别:类是引用类型,结构体是值类型。
|
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
|
struct Person { var firstName:String var lastName:String var fullName:String{ return firstName + "
" + lastName } var age:Int=0 //构造函数,如果定义了构造方法则不会再自动生成默认构造函数//
init(firstName:String,lastName:String){//
self.firstName=firstName//
self.lastName=lastName//
} func showMessage(){ println("firstName=\(firstName),lastName=\(lastName),age=\(age)") } //注意对于类中声明类型方法使用关键字class修饰,但结构体里使用static修饰 static func showStructName(){ println("Struct
name is \"Person\"") }}//注意所有结构体默认生成一个全员逐一构造函数,一旦自定义构造方法,这个默认构造方法将不会自动生成var p=Person(firstName: "Kenshin", lastName: "Cui", age: 28)println(p.fullName) //结果:Kenshin
Cuip.showMessage() //结果:firstName
"Kenshin", lastName "Cui", age 28Person.showStructName() //结果:Struct
name is "Person"//由于结构体(包括枚举)是值类型所以赋值、参数传递时值会被拷贝(所以下面的实例中p2修改后p并未修改,但是如果是类则情况不同)var p2 = pp2.firstName = "Tom"println(p2.fullName) //结果:Tom
Cuiprintln(p.fullName) //结果:Kenshin
Cui |
- 默认情况下如果不自定义构造函数那么将自动生成一个无参构造函数和一个全员的逐一构造函数;
- 由于结构体是值类型,所以它虽然有构造函数但是没有析构函数,内存释放系统自动管理不需要开发人员过多关注;
- 类的类型方法使用class修饰(以便子类可以重写),而结构体、枚举的类型方法使用static修饰(补充:类方法也可以使用static修饰,但是不是类型方法而是静态方法;另外类的存储属性如果是类型属性使用static修饰,而类中的计算属性如果是类型属性使用class修饰以便可以被子类重写;换句话说class作为“类型范围作用域”来理解时只有在类中定义类型方法或者类型计算属性时使用,其他情况使用static修饰[包括结构体、枚举、协议和类型存储属性]);
类的实例通常称之为“对象”,而在Swift中结构体也可以有实例,因此对于很多二者都可以实现的功能,在文中称之为实例而没有使用对象的概念。
枚举
在其他语言中枚举本质就是一个整形,只是将这组相关的值组织起来并指定一个有意义的名称。但是在Swift中枚举不强调一个枚举成员必须对应一个整形值(当然如果有必要仍然可以指定),并且枚举类型的可以是整形、浮点型、字符、字符串。首先看一下枚举的基本使用:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
//注意Swift中的枚举默认并没有对应的整形值,case用来定义一行新的成员,也可以将多个值定义到同一行使用逗号分隔,例如:case
Spring,Summer,Autumn,Winterenum Season{ case Spring case Summer case Autumn case Winter}var s=Season.Spring//一旦确定了枚举类型,赋值时可以去掉类型实现简写s =
.Summerswitch s {case .Spring: //由于Swift的自动推断,这里仍然可以不指明类型 println("spring")case .Summer: println("summer")case .Autumn: println("autumn")default: println("winter")} |
事实上Swift中也可以指定一个值和枚举成员对应,就像其他语言一样(通常其他语言的枚举默认就是整形),但是Swift又不局限于整形,它可以是整形、浮点型、字符串、字符,但是原始值必须是一种固定类型而不能存储多个不同的类型,同时如果原始值为整形则会像其他语言一样默认会自动递增。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
//指定原始值(这里定义成了整形)enum Season:Int{ case Spring=10 //其他值会默认递增,例如Summer默认为11,如果此处也不指定值会从0开始依次递增 case Summer case Autumn case Winter}var summer=Season.Summer//使用rawValue访问原始值println("summer=\(summer),rawValue=\(summer.rawValue)")//通过原始值创建枚举类型,但是注意它是一个可选类型var autumn=Season(rawValue: 12)//可选类型绑定if let newAutumn=autumn{ println("summer=\(newAutumn),rawValue=\(newAutumn.rawValue)")} |
如果一个枚举类型能够和一些其他类型的数据一起存储起来往往会很有用,因为这可以让你存储枚举类型之外的信息(类似于其他语言中对象的tag属性,但是又多了灵活性),这在其他语言几乎是不可能实现的,但是在Swift中却可以做到,这在Swift中称为枚举类型相关值。要注意的是相关值并不是原始值,原始值需要事先存储并且只能是同一种类型,但是相关值只有创建一个基于枚举的变量或者常量时才会指定,并且类型可以不同(原始值更像其他语言的枚举类型)。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
//相关值enum Color{ case RGB(String) //注意为了方便演示这里没有定义成三个Int类型(例如:
RGB(Int,Int,Int))而使用16进制字符串形式 case CMYK(Float,Float,Float,Float) case HSB(Int,Int,Int)}var red=Color.RGB("#FF0000")var green=Color.CMYK(0.61, 0.0, 1.0, 0.0)var blue=Color.HSB(240, 100, 100)switch red {case .RGB(let colorStr): println("colorStr=\(colorStr)")case let .CMYK(c,m,y,k): println("c=\(c),m=\(m),y=\(y),k=\(k)")case let .HSB(h,s,b): println("h=\(h),s=\(s),b=\(b)")} |
上面提到其实枚举也有一些类型和结构体的特性,例如计算属性(包括类型属性,枚举只能定义计算属性不能定义存储属性,存储属性只能应用于类和结构体)、构造方法(其实上面使用原始值创建枚举的例子就是一个构造方法)、方法(实例方法、类型方法)、下标脚本 。
|
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
enum Season:Int{ case Spring=0 ,Summer,Autumn,Winter //定义计算属性 var tag:Int{ return self.rawValue } //类型属性 static var enumName:String{ return "Season" } //
//定义构造方法,注意在枚举的构造函数中则必须保证self有值(正如类的构造方法必须保证其存储属性有值一样)//
init(prefix:String){//
switch prefix.lowercaseString {//
case "sp"://
self = .Spring//
case "su"://
self = .Summer//
case "au"://
self = .Autumn//
default://
self = .Winter//
}//
} //其实上面的构造器有些不合理,那就是default就是Winter,事实上这类构造器可能传任何参数,此时可以使用可失败构造函数来解决 //可失败构造函数返回nil(尽管Swift中构造函数是不返回值的,但是此时约定返回nil代表构造失败) init?(prefix:String){ switch prefix.lowercaseString { case "sp": self =
.Spring case "su": self =
.Summer case "au": self =
.Autumn case "wi": self =
.Winter default: return nil } } //定义实例方法 func showMessage(){ println("rowValue=\(self.rawValue)") } //定义类型方法 static func showEnumName(){ println("Enum
name is \"Season\"") }}var summer=Season.Summerprintln(summer.tag) //结果:1println(Season.enumName) //结果:SeasonSeason.showEnumName() //结果:Enum
name is "Season"summer.showMessage() //结果:rowValue=1if let spring = Season(prefix: "au")
{ //可选绑定,构造函数返回值可能为nil println(spring.tag) //结果:2} |
1041

被折叠的 条评论
为什么被折叠?



