Swift 中的变量、常量、类型
a good building needs a good foundation
变量、常量、类型
变量是存储特定类型值的临时容器:
var count: Int
var shouldRemind: Bool
var text: String
var list: [ChecklistItem]
-
Double,与 Float 相似,但精度更高。比如可以使用 Doubles 来存储经纬度。
-
Character,字符。String 就是字符的集合。
-
UInt,Int 变体。U 代表无符号,表示数据类型只能包含正值。之所以称为无符号,是因为数字前面不能带有负号(-)。 UInt 可以存储 0 到 18 位数之间的数字,但不能存储负数。
-
Int8,UInt8,Int16,UInt16,Int32,UInt32,Int64,UInt64。这些都是 Int 的变体。区别在于它们用于存储的字节数。字节越多,它们可以存储的值越大。实际上,我们经常使用 Int,在 64 位平台上它使用 8 个字节存储数据,并且最多可以容纳大约 19 位数字的正数和负数。
-
CGFloat,它实际上不是 Swift 类型,而是 iOS SDK 定义的类型。这是一个小数点数字,例如 Float 和 Double。由于历史原因,在整个 UIKit 中都将其用于浮点值。 (“ CG”前缀代表Core Graphics框架)
Swift 对类型校验非常严格,比其它许多语言都严格。如果变量的类型为 Int,则不能赋值给它 Float 类型。
即使两种类型都代表某种类型的数字,Swift 都不会自动在不同的数字类型之间进行转换。
eg.
var i = 10
var f: Float
f = i // error
f = Float(i) // OK
创建新变量时,我们不必总是指定类型。如果给变量一个初始值,Swift 将使用类型(type inference)推断来自动确定该变量的类型:
var i = 10 // Int
var d = 3.14 // Double
var b = true // Bool
var s = "Hello, world" // String
如果使用具有与实例变量同名的局部变量,则可能引起混淆。当你调用那个变量时,你得到的可能并不是你想要的那个值,代码一多,你自己可能都分不清哪个是哪个了:
class MyObject {
var count = 7 // an instance variable
func myMethod() {
var count = 42 // local variable “hides” instance variable
print(count) // prints 42
}
}
一些开发者喜欢在实例变量名称的前面加下划线(_),以避免出现此问题:比如实例变量写成_count 而不是 count。还有一种方法是,当访问实例变量时使用关键字 self:
func myMethod() {
var count = 42
print(self.count) // prints 7
}
常量 Constants
有时,我们将计算或方法调用的结果存储到临时容器中,此后该值将永远不会更改,在这种情况下,我们最好使用常量而不是变量:
let pi = 3.141592
let difference = abs(targetValue - currentValue)
let message = "You scored \(points) points"
let image = UIImage(named: "SayCheese")
提示:一个大牛的建议是,90%的情况下使用 let 都是正确的解决方案。如果使用 let 发现错误,Swift 编译器会警告我们正在尝试更改常量。这种情况下,我们再将 let 更改为 var。
值类型(Value types) vs 引用类型(reference types)
当使用基本类型(例如整数和字符串)时,使用 let 创建的常量一旦被赋予值就无法更改:
let pi = 3.141592
pi = 3 // not allowed
但是,对于引用类型(reference types)的对象,只有对象的变量(如下面示例中的 item)是常量。对象本身仍然可以更改:
let item = ChecklistItem()
item.text = "Do the laundry"
item.checked = false
item.dueDate = yesterday
但是 item 变量不可更改:
let anotherItem = ChecklistItem()
item = anotherItem // cannot change the reference
定义为 class 的对象是引用类型,而定义为 struct 或 enum 的对象是值类型。这意味着 iOS SDK 中的大多数对象都是引用类型,但是 Swift 语言中内置的内容(例如 Int,String 和 Array)都是值类型。
集合 Collections
数组存储对象列表,它包含的对象是按顺序排序的,我们可以通过索引检索它们。
// An array of ChecklistItem objects:
var items: Array<ChecklistItem>
// Or, using shorthand notation:
var items: [ChecklistItem]
// Making an instance of the array:
items = [ChecklistItem]()
// Accessing an object from the array:
let item = items[3]
我们可以将数组写为 Array<Type> or [Type] 。第一个是正式写法,第二个是“语法糖”(syntactic sugar),它更容易阅读。 与其它语言不同,在 Swift 中,不能写为 Type [],类型名称应放在方括号内。
词典(dictionary)存储键值对。一个对象(通常是字符串)是检索另一个对象的键。
// A dictionary that stores (String, Int) pairs, for example a
// list of people’s names and their ages:
var ages: Dictionary<String, Int>
// Or, using shorthand notation:
var ages: [String: Int]
// Making an instance of the dictionary:
ages = [String: Int]()
// Accessing an object from the dictionary:
var age = dict["Jony Ive"]
从字典中检索对象的方法与从数组中读取的表示法非常相似,它们都使用 [] 括号。为了索引数组,我们始终使用正整数作为 key ,但是对于字典,通常使用字符串。
也有其它类型的集合,但是数组和字典是最常见的集合。
泛型 Generics
数组和字典被称为泛型,这意味着它们与存储在这些集合中的对象的类型无关。
我们可以拥有一个 Int 对象数组,也可以具有一个 String 对象数组,或者实际上是任何类型的对象数组,甚至还有其他数组的数组。
因此,必须先指定要存储在数组中的对象的类型,然后才能使用它,所以,我们不能这样写:
var items: Array // error: should be Array<TypeName>
var items: [] // error: should be [TypeName]
在 [] 括号内或 Array 一词之后 <> 的括号内,应始终有一个类型声明。如果您以前开发 Objective-C,请注意 OC 中的 <> 的含义与此处完全不同。
对于字典,则需要提供两个类型名称:一个标识键的类型,一个标识值的类型。
Swift 要求所有变量和常量都有一个值。我们可以在声明变量或常量时默认指定一个值,也可以通过在 init 方法内为其分配一个值。
可选变量 Optionals
有时,使用无值的变量会很有用,在这种情况下,我们需要将其声明为可选变量:
var checklistToEdit: Checklist?
我们不能立即使用此变量,而应该首先测试它是否具有值。这称为可选解包(unwrapping the optional):
if let checklist = checklistToEdit {
// “checklist” now contains the real object
} else {
// the optional was nil
}
在使用字典中的值之前,您需要先使用 if let 将其解包:
if let age = dict["Jony Ive"] {
// use the value of age
}
如果我们确定字典中肯定包含给定的键,则还可以使用强制解包(force unwrapping)来读取相应的值:
var age = dict["Jony Ive"]!
! 告诉 Swift,“此值绝对不会为 nil” 。当然,如果为 nil ,则要 crash 了。
强制解包(force unwrapping)的一种较安全的替代方法是可选链接( optional chaining)。例如,如果navigationController 属性为nil,以下内容将使 app crash:
navigationController!.delegate = self
但是这样不会:
navigationController?.delegate = self
如果 navigationController 为 nil,则将被忽略。相当于如下写法:
if navigationController != nil {
navigationController!.delegate = self
}
也可以使用感叹号代替问号来声明可选内容:
var dataModel: DataModel!
这样的值可能是不安全的,因为可以将其用作常规变量,而不必先对其进行拆包。
使用这种方式,有时显得更方便。当我们在声明或 init() 中无法为变量提供初始值时,可以使用这种方式。
但是,一旦给变量赋了一个值,就不应该再将其设置为 nil。 如果该值可以再次变为 nil,则最好使用带问号的 optional 。
方法(Methods)和函数(functions)
函数是独立的功能,与对象无关,需要显示的传递数据。
方法与对象和类相关,依赖对象而调用,可以直接处理对象上的数据,也就是隐式传递数据。