1. Swift的类和结构体的异同:
1) Swift非常重视结构体的作用,并提供了结构体非常强大的面向对象支持;
2) 结构体和类的共有属性:
i) 都可以定义数据成员(这是必然的);
ii) 都可以定义属性(计算属性和存储属性);
iii) 都可以定义附属脚本(下标访问,特别是基本类型中的数组和字典都都是用结构体定义的,并且支持下标访问,Swift的下标访问实质上使用附属脚本定义的);
iv) 都可以定义方法;
v) 都有构造器(注意,Swift中的构造器不等于方法,之后在概念上要作区分);
vi) 都支持扩展和协议;
3) 只有类具备但结构体不具备的更强大的功能:
i) 支持继承;
ii) 支持运行时强制类型转换;
iii) 支持析构;
iv) 支持ARC引用计数(只有类是引用类型的,结构体是值类型的,因此无法引用,也就提不上引用计数管理了);
2. 类和结构体的定义:
1) 和Java定义类的方式相似,都要求所有类和结构体的内容都定义在类体和结构体内部,而不是像C++和OC那样将实现和声明分文件编写;
2) 一个简单地定义类和结构体的例子:
class Employee
{
var no: Int = 0
var name: String = ""
var job: String? // 可以无业
var salary: Double = 0.0
var dept: Department? // 无业就没有所属部门
}
struct Department // 这里不像C++,Department必须定义在Employee前面
{ // Swift会先扫描整个文件并记录所有出现的符号,因此不需要前向引用声明
var no: Int = 0
var name: String = ""
}
class A
{
}
struct B // 当然也可以在结构体中添加类对象成员
{
var a: A
}
注意!可以在类中添加结构体成员,也可以在结构体中添加类成员,这是随意的;
同时和Java一样,没有前向引用声明的概念,一个类中使用另一个类则另一个类不必非得定义在该类之前,Swift会扫描整个文件并记录所有出现的符号;
3. 实例化和访问成员:
1) 利用结构体和类的构造器实例化即可(接上例的代码):
var emp1 = Employee()
var emp2: Employee = Employee()
var dept1 = Department()
var dept2: Department = Department()
注意!这里调用的是它们的无参构造器(构造器之后会讲);
2) Swift摒弃了Java中非常繁琐的每次实例化对象都需要用new操作符的习惯,Swift也是将引用类型的对象都放在堆中管理,因此这点需要程序员记在心里即可,不必使用new之类的操作符来强调这个问题;
3) 实例化和实例是一个广义上的概念:枚举、函数类型和闭包开辟内存的过程也可以成为实例化,而这些开辟的结果只能称作实例,只有类类型开辟的结果才可以成为对象(当然也可以成为实例);
2) 访问成员的方式和传统语言一样,都是使用成员运算符.进行访问的,比如emp1.no = 10111;
4. 值类型和引用类型之间的区别强调:
1) Swift中只有类类型是引用类型,其余的一切类型都是值类型的,值类型和引用类型最大的区别就是值类型的变量或常量代表的就是数据本身,而类类型的变量或常量只是一个引用(就相当于C++中的指针),该引用指向堆中真正的类的对象实体,引用之间的赋值可以使多个引用指向同一个实体,但值类型之间的赋值就是完完全全拷贝其中的内容;
其实最重要的地方是在函数传参阶段,值类型数据传参就是完全拷贝一份而引用类型的传参仅仅是拷贝一份引用,而对象实体本身就那么一份而不会拷贝,引用的复制仅仅就是多了一个函数参数引用该对象的实体罢了,因此在函数中通过引用参数来修改对象中的内容会直接影响函数外指向该对象的那个引用(其实外面的应用没变,只不过通过该引用访问的对象的内容会由于函数内的修改而发生改变),但是在函数内对值类型数据修改就不会影响函数外的那个值类型数据,因为函数内的仅仅是一个拷贝,和函数外的那份完全不相干;
!因此,通常结构体不宜定义地过大(一般定义成一个较小的数据包),因为传参的时候是要拷贝的,如果体积庞大则拷贝非常消耗资源!
2) 两者之间的区别演示:
class A
{
var a: Int = 1
var b: Int = 2
}
var a = A()
var aa = a
aa.a = 100
aa.b = 100
println("a.a = \(a.a), a.b = \(a.b)") // 100, 100
func changeA(ra: A) // 默认为let的,在let情况下只有通过引用才能访问成员,因此是引用类型
{
ra.a = 5
ra.b = 5
}
changeA(a)
println("a.a = \(a.a), a.b = \(a.b)") // 5, 5
struct B
{
var a: Int = 1
var b: Int = 2
}
var b = B()
var bb = b
bb.a = 100
bb.b = 100
println("b.a = \(b.a), b.b = \(b.b)") // 1, 2,没变,因此bb只是一份拷贝而已
//func changeB(eb: B) // Bad! 默认情况是let,let的值类型是不能改变其成员!因此是struct是值类型的
//{
// eb.a = 300
// eb.b = 300
//}
func changeB(var eb: B) // 修改成var或inout就可以了
{
eb.a = 300
eb.b = 300
}
changeB(b)
println("b.a = \(b.a), b.b = \(b.b)") // 1, 2,还是没变,传入函数的只是拷贝
func changeBByRef(inout rb: B) // 输入输出参数即强行引用传递
{
rb.a = 300
rb.b = 300
}
changeBByRef(&b)
println("b.a = \(b.a), b.b = \(b.b)") // 300, 300
3) 也演示一下枚举类型(也是一种值类型):
enum Dir: String
{
case East = "East"
case South = "South"
case West = "West"
case North = "North"
}
var d1 = Dir.East
var d2 = d1
d2 = Dir.West
println(d1.rawValue) // East
4) 引用之间的比较:
i) ===和!==用于比较两个引用是否相等(即指针的指向是否相同),即是否引用了同一个对象);
ii) ==和!=用于比较两个引用指向的对象的内容是否相同,但前提是必须先对类的==运算符进行重载否则会直接编译报错!
5. 类型嵌套:
功能和Java的内部类相似,只不过Swift的更加灵活,枚举、结构体、类的内部之间可以随意相互定义:
class A {
class B {}
enum C { case k }
struct D {}
}
enum B {
class A {}
enum C { case k }
struct D {}
}
struct C {
class A {}
enum B { case b }
struct D {
class K {}
enum H {
case v
class Q {}
}
}
}
反正就是支持各种类型之间相互嵌套,并且可以嵌套多层,但是和Java一致的是内部类型的作用域在内部,可以和外部类型重名,只不过在内部屏蔽了;