协议
协议是对实例行为的一种约束,和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 Cui
println
(
"className=\(
Person
.
className
)"
)
//结果:className=Person
Person
.
showClassName
()
//结果:Class name is "Person"
p
.
age
=
28
var
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
=
28
println
(
p
.
personInfo
)
//结果:firstName=Kenshin,lastName=Cui,age=28
p
.
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 Cui
p
.
showMessage
()
//结果:firstName "Kenshin", lastName "Cui", age 28
Person
.
showStructName
()
//结果:Struct name is "Person"
//由于结构体(包括枚举)是值类型所以赋值、参数传递时值会被拷贝(所以下面的实例中p2修改后p并未修改,但是如果是类则情况不同)
var
p2
=
p
p2
.
firstName
=
"Tom"
println
(
p2
.
fullName
)
//结果:Tom Cui
println
(
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,Winter
enum
Season
{
case
Spring
case
Summer
case
Autumn
case
Winter
}
var
s
=
Season
.
Spring
//一旦确定了枚举类型,赋值时可以去掉类型实现简写
s
= .
Summer
switch
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
.
Summer
println
(
summer
.
tag
)
//结果:1
println
(
Season
.
enumName
)
//结果:Season
Season
.
showEnumName
()
//结果:Enum name is "Season"
summer
.
showMessage
()
//结果:rowValue=1
if
let
spring
=
Season
(
prefix
:
"au"
) {
//可选绑定,构造函数返回值可能为nil
println
(
spring
.
tag
)
//结果:2
}
|