很久就计划开始学习Swift,一直未付出行动。原因有两点,一个是人惰性,二是因为担心写不好。挣扎了好几天,终于鼓起勇气与动力,写第一篇关于Swift的知识点。如果有不足之处,望请致电。 开篇讨论下Swift中的可变性或不可变性的区别。
Swift中变量的不可变性
Swift对于不可变性可控制通过let和var关键字来控制。下面我们来看看这两个关键字的区别:
let x: Int = 10
var y: Int = 20
当我们使用let和var申明了变量时,它们最大区别是:
- let申明的变量称为不可变变量,不可再赋予新值,如果赋予新值时会报编译错误“Cannot assign to value: '' is a 'let' constant”.
- var申明的变量称为可变变量,可以随意赋予新值,前提只要类型一样则可以。
这样实现机制,最大的好处是,使用let可以减少更多的可变因子,我们可以将更多的精力放在那些可变变量。当我们看到一个变量是let申明的时候,我们只需要关心它的初始值,不用担心它在后续的使用中值会发生更改。
虽然在Objective-C中,为了实现类似功能,分别设计了可变类型和不可变类型,例如NSArray和NSMutableArray、NSString和NSMutableString、NSDictionary和NSMutableDicionary、NSSet和NSMutableSet等等。虽然如此,但是在使用是需要使用不同类型,相对繁琐。即使是一个不可变对象,但是也不是真正意义上的不可变,我们可以在后续中修改它的引用指向从而修改它的值。例如: src = "123"。还可以通过performSelector:withObject:等方法修改对象的值,例如:
NSString *src = [NSMutableString stringWithString:@"我是可变字符串"];
NSString *suffix = @"123";
[src performSelector: **@selector**(appendString:) withObject:suffix]; // src的值是"我是可变字符串123"
虽然一般逻辑下没有人会这么操作,但这段代码可告诉我们Objective-C的不可变性不是那么值的信任。如果在将src传入到SDK的函数且看不到源码,刚好通过NSString和NSMutableString来做某些逻辑的话,那么你会在很诧异src在那里修改的值,让你头疼很长一段时间。
Swift的值类型和引用类型
在Swift中不是只有变量才具有不可变性和可变性,类型也分为值类型和引用类型。值类型包含结构体、枚举、Int、String、Double、Bool等,引用类型则是使用class标记的类型。为了讨论值类型和引用类型的区别,我这里使用结构体和类来做例子。 我们先来定义一个结构体和类:
struct PointStruct {
var x: Int
var y: Int
}
class PointClass {
var x: Int
var y: Int
init(x: Int, y: Int) {
self.x = x
self.y = y
}
}
接下来我们按如下代码:
var point1 = PointStruct(x: 1, y: 1)
var point2 = point1
point2.x = 2
var point3 = PointClass(x: 3, y: 3)
var point4 = point3
point4.x = 10
执行上面的代码后:
- 结构体对象的 point1 = (1, 1), point2 = (2, 1), point1的值没有改变,而类对象的 point3 = point4 = (10, 3)。这就是值类型和类类型的最大区别,当值类型在赋值给一个新的变量或作为参数传递时,值类型会被复制。所以在给point2的x赋值时,point1的值不会受到影响,正是在point2 = point1赋值时发生了值赋值,可以用OC中的深拷贝来理解。
- 而类对象point4.x = 10在赋值后,point3的x值也更改了。因为point4 = point3赋值时是指针引用,point4指向的还是point3这个变量,像我们生活中的别名、外号,可以用OC中的浅拷贝来理解。
Struct中let 和 var的区别:
let pointClass = PointClass(x: 4, y: 4)
pointClass.x = 8
let pointStruct = PointStruct(x: 2, y: 2)
pointStruct.x = 4
我们看到Struct申明为let时修改其var属性会报错:Cannot assign to property: 'pointStruct' is a 'let' constant,意思是不能将pointStruct设置为let不可变。
因为我在后续修改了pointStruct的值: pointStruct.x = 4。但是引用类型却不会。
这就是值类型不可变性与引用类型的最大区别: 当值类型为不可变时,其所有属性都为不可变(即使在声明为var);但是引用类型的var属性却不受其对象是否声明为let或其他的。
理解值类型和类类型的区别极为重要,这样我们可以预测赋值行为将会如何修改数据,同时可以确定那一部分代码会受到影响。值类型的赋值,不会影响原来的数据;类类型赋值时指向同一个变量地址,会相互影响。当我们不希望修改原值时,使用值类型,否则使用类类型。