Swift初见

Swift基本类型

Swift的类型是在C和OC的基础上发展而来的,Int是整型;Double和Float是浮点型;Bool是布尔型;String是字符串。类似OC,Swift也提出了三个集合类型:Array、Set、Dictionary;

除了上述比较熟悉的类型,Swift还增加了OC中没有的类型,比如元组(Tuple);Swift还增加了可选(Optional)类型。

Swift是一个类型安全的语言(甚至以此著称),可选(Optional)就是一个很好的例子。Swift可以让你清除地知道值的类型。如果你的代码期望得到一个String,你却不小心传入一个Int,Swift的类型安全机制会在开发阶段发现这个问题,避免在runtime出错。

常量和变量

在OC以及C中,一个值类型或引用类型数据是常量还是变量由关键字const决定。在Swift中,在没有const这一说喽。

let和var

Swift使用let来声明常量,使用var来声明变量;

声明

Swift在声明一个变量或者常量可以携带类型信息,也可以不携带。

P.S:声明变量/常量时,不指明类型而让Swift编译器隐式处理的这种行为被称为『类型推测』(Type Inference),后文有更详细介绍。

赋值

本来常量和变量是编程语言里非常基础的概念,没啥好讲的,但Swift世界里,有些许不同,在赋值过程中,需要注意:

  1. 无论是常量还是变量,在定义时都可以没有右值(即没有初始化值,譬如var a: Intlet b: NSObject),在其他语言中,系统往往会在这种情况下为它们分配一个默认值,譬如OC会设置默认值0或者nil;但这在Swift的世界里是不可能的,Swift不会为它们设置任何默认值,无论是在什么时候;
  2. 无论是常量还是变量,在第一次使用(访问)之前必须要保证有与类型匹配的值(譬如,var a:Int; print("a = \(a)")是不合法的);
  3. 和其他语言一样,常量只能进行一次赋值,变量可以进行任意多次赋值;但无论何时,赋值时右值都必须与其定义时的类型匹配,Swift不会帮你做任何转换;

第3点意味着:

  • let aInt: Int = 0; aInt = 5不合法,常量只能进行一次初始化;
  • var aObject: NSObject; aObject = nil不合法,右值nil与NSObject类型不匹配;
  • var aInt: Int; aInt = 0.0不合法,右值0.0与Int类型不匹配;

说明

  • 常量/变量使用之前没有初始化/赋值在Xcode的编译错误通常是:『Variable variableName used before being initialized』;
  • 常量/变量赋值时类型不对的编译错误是:『Cannot assign a value of type WrongType to a value of type CorrectType』;

总之,一定要深刻理解到Swift是一门类型安全的语言,《The Swift Programming Language》是这么说的:

Swift is a type safe language. A type safe language encourages you to be clear about the types of values your code can work with. If part of your code expects a String, you can not pass it an Int by mistake.

类型推测(Type Inference)

显然,『类型推测』在处理引用类型时非常便利,这会使得代码更简洁;但是在处理值类型时可能会有些许麻烦,譬如1.0推断为float还收double?在这种记忆力不是特别牢靠(不太确认)的情况下最好还是显式写出变量/常量的类型吧。

静态局部变量、静态全局变量、全局变量

「静态局部变量」、「静态全局变量」、「全局变量」这些是C语言里的概念,在Swift似乎不存在。

可选类型(Options)

正如上文所阐述的,Swift是一门『类型安全』的语言,在对对象赋值时,左值类型和右值类型必须匹配,否则无法通过编译;但在OC中,有非常非常多的机会与nil指针打交道,譬如:

  • 某个方法/函数返回nil指针,表示返回的对象不存在;
  • 某个方法/函数的某个传入参数为nil,表示传入空值;
  • 某个strong类型指针赋值nil,表示不再持有原对象;

Swift的创建是基于Objective-C的,何况还得保证与之兼容,必然考虑到了这个问题,Swift中能够赋值nil的类型叫「可选类型」(optional)。

理解optionals

C和OC中并没有可选这个概念。在OC的对象世界里,一个指针要么对应这一个对象,要么对应着nil;很多时候我们不知道这个指针到底是空指针,还是有一个有效的对象与之对应,所以我们在OC中经常会写大量大量的if语句判断某个指针是否为空。而在Swift中,不需要这么麻烦,因为它是一门类型安全的语言,NSObject类型变量在运行时一定有一个NSObject对象与之对应,不可能是nil值。想想这个就激动,想象一下,以前在OC中调用具有返回值的方法时经常需要对返回值进行判断,因为必须要考虑它可能返回nil啊,譬如:

NSMutableArray *aArray = [NSMutableArray array];
NSString *aStr = [self aFuckMethod]; // aFuckMethod的实现代码不可见,不知道一定会返回一个NSString
if (aStr) {
    [aArray addObject:aStr];
}

如果在Swift中,aFuckMethod的原型为func aFuckMethod() -> String,则清晰表明了aFuckMethod方法一定会返回一个非String类型对象,那么上面一段代码就可以省掉那个if语句了;对比Swift,确实感觉到了OC是一个门太落后的语言,太罗嗦了!

回过头来阐述Swift的optional。简而言之:

You use optionals in situations where a value may be absent. An optional says:

 * There is a value, and it equals x 

or
 * There is not value at all

相对于OC,Swift的optionals对值类型(譬如整型、结构体)和引用类型(类对象)的处理更具一致性。在OC中,一个方法要不返回一个对象要不返回nil,后者表示『缺乏一个合法的对象』;然而,这只对对象起作用;对于结构体、基本的C类型或者枚举都不起作用,处理这些类型,OC方法一般会返回一个特殊值(譬如NSNotFound)来暗示值缺失,这种方法会假设方法的调用者知道并有意识对特殊值进行判断。然而,Swift的optionals可以让你暗示任意类型的缺失,并不需要一个特殊值。

P.S:稍微体会一下,就会意识到Swift的optional高明多了。

使用optionals

Optional类型默认值

在OC中,若声明一个对象(指针)但不赋值,系统会默认将之设置为nil;在Swift中也类似,对于一个optional,若不对其赋值,则默认为nil,这可不是我瞎说,文档有说明:

If you define an optional variable without providing a default value, the variable is automatically set to nil for you.

P.S:似乎这是Swift唯一为变量/常量设置默认值的时机。

简而言之,你可以对一个optional变量赋值nil,也可以赋值相应的对象。

在OC中,我们经常需要对一个类类型指针进行判断,判断它是否为nil;在Swift中使用optional也不省心,也常常需要写if语句判断,只是Swift中提供了更多丰富的处理方式。

强制解析(Foreced Unwrapping)符!

You can use an if statement to find out whether an optional contains a value by comparing the optional against nil. You perform this comparison with the == or !=.

当你确定可选包确实含值之后,你可以在可选的名字后面加一个感叹号!来获取值。这个惊叹号表示『我知道这个可选有值,请使用它』,这被称为可选值的强制解析(forced unwrapping),如下:

let strNumber = "42"
var convertNumber: Int?
convertNumber = strNumber.toInt()
if convertNumber != nil {
    println("转换后的结果为:\(convertNumber!)")
}

值得注意的是,使用!来获取一个不存在的可选值会导致runtime error。因此使用!强制解析前,一定要确定optional包含一个非nil的值。

总之,一定要记住,惊叹号!对于optional而言是『强制解析符』,而不是仅仅是『解析符』,这可不是咬文嚼字,虽然只有两字之差,但意思差远了。

可选绑定(Optional Binding)

使用可选绑定(optional binding)来判断optional是否包含值,如果包含就把值赋给一个临时常量或者变量。可选绑定可以用在ifwhile语句中来对可选的值进行判断并把值赋给一个常量或者变量,如下:

let strNumber = "42"
if let actualNumber = strNumber.toInt() {
    println("转换后的结果为:\(actualNumber)")
}

这段代码可以理解为:
如果strNumber.toInt()返回的optional包含一个Int值,则创建一个叫actualNumber的新常量(actualNumber为Int类型)并将可选包含的值赋给它。

这段『可选绑定』的代码显然比前一段『强制解析』的代码要简短一些,可是,我总觉得Swift的这一部分设计不好,晦涩难懂。首先,
下意识里,let actualNumber = strNumber.toInt()等价两句代码:let temp = strNumber.toInt()let actualNumber = temp,得到的actualNumber应该是一个optional啊,怎么文档给出的解释暗示actualNumber是一个Int类型变量呢?Fuck!

Implicitly Unwrapped Optionals

『隐式解析可选』作为右值?

『隐式解析可选』似乎比普通optional更安全,既然这么好,为什么不放弃前面的普通optional呢?似乎是不行的,文档这么说:

Do not use an implicitly unwrapped optional when there is a possibility of a variable becoming nil at a later point. Always use a normal optional type if you need to check for a nil value during the lifetime of a variable.

元组(Tuple)

元组概述

元组(tuples)早已经是很多高级语言(譬如Python)的中重要组成成分了,它用来把多个值组合成一个复合值,而这些值可以是任意类型(无所谓变量还是常量,无所谓值类型还是引用类型,无所谓optional还是non-optional),并不要求是相同类型。

使用元组

分解元组(Decompose Tuple)

Swift中的元组分解(decompose)和Python中差不多,你可以使用如下语法将一个元组分解成单独的常量和变量:

let http404Error = (404, "Not Found")
// http404Error的类型是(Int, String),值是(404, "Not Found")
 
var (status, statusMessage) = http404Error

在decompose时,如果只需要一部分元组信息,使用_来标记要忽略的部分。

Tuple支持下标操作

可以使用下标访问一个元组中指定的元素,譬如http404Error.0表示访问上述元组http404Error的第一个元素(值为404的Int型)。

给Tuple元素命名

使用下标访问Tuple元素难免不够完美,毕竟数字太冰冷了;Swift的Tuple还支持给Tuple元素命名,譬如:

let http404Error = (status: 404, message:"Not Found")
println("状态码:\(http404Error.status), 信息:\(http404Error.message)")

理解元组

根据我的理解,Tuple并不算一种类型(事实上Swift中也不存在所谓的Tuple类型),它更应该看成是Swift所支持的一种语言特性(语法)。Tuple看起来像数组,但显然不同于数组,数组可是一种数据类型,数组包括大量的功能方法,「元组」是不存在所谓的方法的。也正因为如此,元组的使用场景比较单一,一般用来临时组织数据(譬如组织函数/方法的返回值和参数)。

posted @ 2015-06-29 16:05  脸大皮厚歌  阅读(283)  评论(0编辑  收藏  举报