首页 » iOS编程基础:Swift、Xcode和Cocoa入门指南 » iOS编程基础:Swift、Xcode和Cocoa入门指南全文在线阅读

《iOS编程基础:Swift、Xcode和Cocoa入门指南》3.2 变量声明

关灯直达底部

正如第1章所述,变量是通过let或var声明的:

·let声明的变量是常量,其值在首次赋值(初始化)后就不会再变化了。

·var声明的变量才是真正的变量,其值可以被后续的赋值所改变。

不过,变量的类型是绝对不会改变的。使用var声明的变量可以被赋予不同的值,但该值必须要符合变量的类型。这样,在声明变量时,需要为其指定好类型,然后变量就会一直具有该类型。你可以显式或隐式地指定变量的类型:

显式变量类型声明

在声明中的变量名后面,添加一个冒号和类型名:


var x : Int  

通过初始化创建隐式变量类型

如果将变量初始化作为声明的一部分,并且没有提供显式的类型,那么Swift就会根据初始化值推断其类型:


var x = 1 // and now x is an Int  

完全可以显式声明变量的类型,然后为其赋予一个初始值,并将这些工作在一步中完成:


var x : Int = 1  

在该示例中,显式类型声明是多余的,因为类型(Int)可以通过初始值推断出来。但有时,提供显式类型的同时又赋予一个初始值并不多余。比如,下面列出的各种情况:

Swift的推断可能是错误的

我遇到的一个常见情况就是当我提供初始值作为数值字面值时,Swift会将其推断为Int或Double,这取决于字面值是否包含了小数点。不过还有很多其他的数值类型!如果遇到这种情况,我会显式提供类型,如下代码所示:


let separator : CGFloat = 2.0  

Swift无法推断出类型

在这种情况下,显式变量类型可以让Swift推断出初始值的类型。选项集合就是一种非常常见的情况(第4章将会介绍)。如下代码无法编译通过:


var opts = [.Autoreverse, .Repeat] // compile error  

问题在于名字.Autoreverse与.Repeat分别是UIViewAnimationOptions.Autoreverse与UIViewAnimationOptions.Repeat的简写,不过除非我们告诉Swift,否则它是不知道这一点的:


let opts : UIViewAnimationOptions = [.Autoreverse, .Repeat]  

程序员无法推断出类型

我常常会加上多余的显式类型声明来提醒我自己。如下示例来自于我所编写的代码:


let duration : CMTime = track.timeRange.duration  

在上述代码中,track的类型是AVAssetTrack。Swift非常清楚AVAssetTrack的timeRange属性的duration属性是个CMTime。不过,我不知道!为了提醒自己,我显式添加了类型。

由于可以使用显式变量类型,因此变量在声明时无需初始化。下面这样写是合法的:


let x : Int  

现在,x是个空盒子,一个没有初始值的Int变量。不过,如果可以避免,我强烈建议你不要对局部变量采取这种做法。这么做并非灾难,因为Swift编译器会阻止你使用从未赋过值的变量,只不过不是一个好习惯而已。

能够证明该规则的一个例外情况是条件初始化。有时,我们只有在执行了某些条件测试后才知道某个变量的初始值是什么。不过,变量本身只能声明一次,因此它必须要提前声明,然后根据条件进行初始化。下面这么做是合理的(不过还有更好的写法):


let timed : Boolif val == 1 {    timed = true} else {    timed = false}  

在将变量的地址作为实参传递给函数时,变量必须要提前声明并初始化,即便初始值是假的亦如此。回忆一下第2章的示例:


var arrow = CGRectZerovar body = CGRectZeroCGRectDivide(rect, &arrow, &body, Arrow.ARHEIGHT, .MinYEdge)  

代码运行后,两个CGRectZero值将被替换掉;它们仅仅是占位符而已,为了满足编译器的要求。

有时,你希望调用的Cocoa方法会立刻返回一个值,然后在传递给相同方法的函数中使用该值。比如,Cocoa有一个UIApplication实例方法,其声明如下所示:


func beginBackgroundTaskWithExpirationHandler(handler: ( -> Void)?)    -> UIBackgroundTaskIdentifier  

该函数会返回一个数字(UIBackgroundTaskIdentifier就是个Int),然后再调用传递给它的函数(handler),该函数会使用一开始返回的数字。Swift的安全规则不允许你使用一行代码声明持有该数字的变量,然后在匿名函数中使用它:


let bti = UIApplication.sharedApplication    .beginBackgroundTaskWithExpirationHandler({        UIApplication.sharedApplication.endBackgroundTask(bti)    }) // error: variable used within its own initial value  

因此,你需要提前声明好变量;不过,Swift还会提示另一个错误:


var bti : UIBackgroundTaskIdentifierbti = UIApplication.sharedApplication    .beginBackgroundTaskWithExpirationHandler({        UIApplication.sharedApplication.endBackgroundTask(bti)    }) // error: variable captured by a closure before being initialized 

解决办法就是提前声明好变量,然后为其赋予一个假的初始值作为占位符:


var bti : UIBackgroundTaskIdentifier = 0bti = UIApplication.sharedApplication    .beginBackgroundTaskWithExpirationHandler({        UIApplication.sharedApplication.endBackgroundTask(bti)    })  

对象的实例属性(在枚举、结构体或类声明的顶层)可以在对象的初始化器函数中进行初始化,而不必在声明中赋值。对于常量实例属性(let)与变量实例属性(var),具有显式类型而不直接赋予初始值是合法且常见的。第4章将会深入介绍这一点。