OC转Swift指南(一) 基础部分

简介

这篇文章主要用于记录自己学习Swift的过程, 也为自己之后回顾时做个参考.
文章是摘录自SwiftGG小组翻译的官方文档, 仅对文档内容中与Objective-C有差异的部分或比较重要的地方进行记录, 所以推荐具有Objective-C一年以上实际开发经验的阅读.

基础部分

Swift 包含了 C 和 Objective-C 上的所有基础数据类型.
Int表示整形.DoubleFloat表示浮点值.Bool是布尔型值.String的文本数据类型. 还有三个集合类型Array, Set, Dictionary.

声明常量和变量

常量声明: let number = 10
变量声明: var name = "Lilei", var address: String
也可以在一行中声明多个常量或者变量, 用,号隔开:
var x = 0.0, y = 1.0, z = 2.0

类型标注

var message: String: 表示给message变量添加了类型标注,message变量可以存储String类型的值.
你也可以在一行中定义多个同样的变量. 用逗号分隔, 并在最后一个变量名之后添加类型标注:
var red, green ,blue: Double

常量和变量的命名

在 Swift 中, 你可以使用任何你喜欢的字符作为常量和变量名, 包括Unicode字符:

let π = 3.14159
let 哇偶 = "哈哈"
let 🐮🐶 = "奶牛小狗"

除此之外 Swift 还支持使用 Swift 保留的关键字做变量名:

let `class` = 5

但是非常不推荐这么使用.

注意:
以数字开头的命名仍然是不可取的.

输出常量和变量

Swift 中使用 print() 作为输出函数.

let name = "Lilei"
print(name) // 输出 Lilei

let age = 10
print("age == \(age)") // 输出 age == 10. 将其他类型转成String类只需要用 \( ) 包裹就可以.

print("The boy's name is " + name + "," + "he is " + "\(age)" + " years old");
 //输出 The boy's name is Lilei,he is 10 years old. 可以使用 + 号拼接字符串.

类型安全和类型推断

由于 Swift 是类型安全的,所以它会在编译你的代码时进行类型检查(type checks), 并把不匹配的类型标记为错误。这可以让你在开发的时候尽早发现并修复错误.
当你在声明常量或者变量的时候赋给它们一个字面量(literal value 或 literal)即可触发类型推断

let meaningOfLife = 42 // meaningOfLife 会被推测为 Int 类型

let anotherPi = 3 + 0.14159 // anotherPi 会被推测为 Double 类型

数值型字面量

一个十进制数,没有前缀
一个二进制数,前缀是0b
一个八进制数,前缀是0o
一个十六进制数,前缀是0x

let decimalInteger = 17
let binaryInteger = 0b10001       // 二进制的17
let octalInteger = 0o21           // 八进制的17
let hexadecimalInteger = 0x11     // 十六进制的17

数值类字面量可以包括额外的格式来增强可读性。整数和浮点数都可以添加额外的零并且包含下划线,并不会影响字面量:

let paddedDouble = 000123.456
let oneMillion = 1_000_000
let justOverOneMillion = 1_000_000.000_000_1

整数转换

let cannotBeNegative: UInt8 = -1
// UInt8 类型不能存储负数,所以会报错
let tooBig: Int8 = Int8.max + 1
// Int8 类型不能存储超过最大值的数,所以会报错

要将一种数字类型转换成另一种,你要用当前值来初始化一个期望类型的新数字,这个数字的类型就是你的目标类型。

let ten: Int = 10;
let half: Double = 0.5;
let tenHalf = Double(ten) + half; // 将Int型的ten转换成Double类型 10.5;

SomeType(ofInitialValue)是调用 Swift 构造器并传入一个初始值的默认方法. 你并不能传入任意类型的值,只能传入 SomeType 内部有对应构造器的值. 所以, 之所以我们可以使用 Double(IntValue)Int 类型的常量转换成 Double, 是因为 Double 内部有该构造器. 你还可以扩展现有的类型来让它可以接收其他类型的值(包括自定义类型).

类型别名

类型别名(type aliases)就是给现有类型定义另一个名字。你可以使用typealias关键字来定义类型别名.
typealias AudioSample = UInt16

Bool值

Swift 有一个基本的布尔(Boolean)类型,叫做Bool。布尔值指逻辑上的值,因为它们只能是真或者假。Swift 有两个布尔常量,truefalse.

如果你在需要使用 Bool 类型的地方使用了非布尔值,Swift 的类型安全机制会报错。下面的例子会报告一个编译时错误:

let i = 1
if i {
    // 这个例子不会通过编译,会报错
}

下面这个是合法的:

let i = 1
if i == 1 {
    // 这个例子会编译成功
}

元祖

元组(tuples)把多个值组合成一个复合值。元组内的值可以是任意类型,并不要求是相同类型.

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

你可以将一个元组的内容分解(decompose)成单独的常量和变量,然后你就可以正常使用它们了:

let (statusCode, statusMessage) = http404Error
print("The status code is \(statusCode)")
// 输出 "The status code is 404"
print("The status message is \(statusMessage)")
// 输出 "The status message is Not Found"

如果你只需要一部分元组值,分解的时候可以把要忽略的部分用下划线_标记:

let (justTheStatusCode, _) = http404Error
print("The status code is \(justTheStatusCode)")
// 输出 "The status code is 404"

此外,你还可以通过下标来访问元组中的单个元素,下标从零开始:

print("The status code is \(http404Error.0)")
// 输出 "The status code is 404"
print("The status message is \(http404Error.1)")
// 输出 "The status message is Not Found"

你可以在定义元组的时候给单个元素命名:

let http200Status = (statusCode: 200, description: "OK")
//元组中的元素命名后,你可以通过名字来获取这些元素的值:
print("The status code is \(http200Status.statusCode)")
// 输出 "The status code is 200"
print("The status message is \(http200Status.description)")
// 输出 "The status message is OK"

注意:
元组在临时组织值的时候很有用,但是并不适合创建复杂的数据结构。如果你的数据结构并不是临时使用,请使用类或者结构体而不是元组。

可选值

CObjective-C 中并没有可选类型这个概念。最接近的是 Objective-C 中的一个特性,一个方法要不返回一个对象要不返回nilnil表示"缺少一个合法的对象"。然而,这只对对象起作用——对于结构体,基本的 C 类型或者枚举类型不起作用。对于这些类型,Objective-C 方法一般会返回一个特殊值(比如NSNotFound)来暗示值缺失。这种方法假设方法的调用者知道并记得对特殊值进行判断.然而,Swift 的可选类型可以让你暗示任意类型的值缺失,并不需要一个特殊值.

一个可选的 Int 被写作 Int? 而不是 Int.问号暗示包含的值是可选类型,也就是说可能包含 Int 值也可能不包含值.

nil

nil不能用于非可选的常量和变量。如果你的代码中有常量或者变量需要处理值缺失的情况,请把它们声明成对应的可选类型。

//如果你声明一个可选常量或者变量但是没有赋值,它们会自动被设置为 nil:

var surveyAnswer: String?
// surveyAnswer 被自动设置为 nil

注意: SwiftnilObjective-C 中的 nil 并不一样。在 Objective-C 中,nil 是一个指向不存在对象的指针。在 Swift 中,nil 不是指针——它是一个确定的值,用来表示值缺失。任何类型的可选状态都可以被设置为 nil,不只是对象类型.

if 语句以及强制解析

if判断不等:

    if some != nil {
        print("some is not nil")
    }

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

    if some != nil {
        print("some is not nil, is \(some!)")
    }

注意: 使用 ! 来获取一个不存在的可选值会导致运行时错误。使用 ! 来强制解析值之前,一定要确定可选包含一个非 nil 的值.

可选绑定

可选绑定可以用在 ifwhile 语句中,这条语句不仅可以用来判断可选类型中是否有值,同时可以将可选类型中的值赋给一个常量或者变量.

if let actualNumber = Int(possibleNumber) {
    print("\'\(possibleNumber)\' has an integer value of \(actualNumber)")
} else {
    print("\'\(possibleNumber)\' could not be converted to an integer")
}

这段代码可以被理解为:
“如果 Int(possibleNumber) 返回的可选 Int 包含一个值,创建一个叫做 actualNumber 的新常量并将可选包含的值赋给它。”

如果转换成功,actualNumber 常量可以在 if 语句的第一个分支中使用。它已经被可选类型 包含的 值初始化过,所以不需要再使用 ! 后缀来获取它的值。在这个例子中,actualNumber 只被用来输出转换结果。

你可以包含多个可选绑定或多个布尔条件在一个 if 语句中,只要使用逗号分开就行。只要有任意一个可选绑定的值为nil,或者任意一个布尔条件为false,则整个if条件判断为false,这时你就需要使用嵌套 if 条件语句来处理,如下所示:

 if let firstNumber = Int("4"), let secondNumber = Int("42"), firstNumber < secondNumber && secondNumber < 100 {
        print("\(firstNumber) < \(secondNumber) < 100")
    }
    // 输出 "4 < 42 < 100"

    if let firstNumber = Int("4") {
        if let secondNumber = Int("42") {
            if firstNumber < secondNumber && secondNumber < 100 {
                print("\(firstNumber) < \(secondNumber) < 100")
            }
        }
    }
    // 输出 "4 < 42 < 100"

注意: 在 if 条件语句中使用常量和变量来创建一个可选绑定,仅在 if 语句的句中(body)中才能获取到值。相反,在 guard 语句中使用常量和变量来创建一个可选绑定,仅在 guard 语句外且在语句后才能获取到值.

隐式解析可选类型

如上所述,可选类型暗示了常量或者变量可以“没有值”。可选可以通过 if 语句来判断是否有值,如果有值的话可以通过可选绑定来解析值。

有时候在程序架构中,第一次被赋值之后,可以确定一个可选类型_总会_有值。在这种情况下,每次都要判断和解析可选值是非常低效的,因为可以确定它总会有值。

这种类型的可选状态被定义为隐式解析可选类型(implicitly unwrapped optionals)。把想要用作可选的类型的后面的问号(String?)改成感叹号(String!)来声明一个隐式解析可选类型。

当可选类型被第一次赋值之后就可以确定之后一直有值的时候,隐式解析可选类型非常有用。隐式解析可选类型主要被用在 Swift 中类的构造过程中,请参考无主引用以及隐式解析可选属性

一个隐式解析可选类型其实就是一个普通的可选类型,但是可以被当做非可选类型来使用,并不需要每次都使用解析来获取可选值。下面的例子展示了可选类型 String 和隐式解析可选类型 String 之间的区别:

let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // 需要感叹号来获取值

let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString  // 不需要感叹号

你可以把隐式解析可选类型当做一个可以自动解析的可选类型。你要做的只是声明的时候把感叹号放到类型的结尾,而不是每次取值的可选名字的结尾。

注意:
如果你在隐式解析可选类型没有值的时候尝试取值,会触发运行时错误。和你在没有值的普通可选类型后面加一个惊叹号一样。
如果一个变量之后可能变成nil的话请不要使用隐式解析可选类型。如果你需要在变量的生命周期中判断是否是nil的话,请使用普通可选类型。

### 错误处理
使用throws来标注函数会抛出异常.

func canThrowAnError() throws {
    // 这个函数有可能抛出错误
}

当你的函数能抛出错误消息时, 你应该在表达式中前置try关键词。

do {
    try canThrowAnError()
    // 没有错误消息抛出
} catch {
    // 有一个错误消息抛出
}

这里有一个错误处理如何用来应对不同错误条件的例子。

func makeASandwich() throws {
    // ...
}

do {
    try makeASandwich()
    eatASandwich()
} catch SandwichError.outOfCleanDishes {
    washDishes()
} catch SandwichError.missingIngredients(let ingredients) {
    buyGroceries(ingredients)
}

在此例中,makeASandwich()函数会抛出一个错误消息,如果没有干净的盘子或者某个原料缺失。因为 makeASandwich() 抛出错误,函数调用被包裹在 try 表达式中。将函数包裹在一个 do 语句中,任何被抛出的错误会被传播到提供的 catch 从句中。

如果没有错误被抛出,eatASandwich() 函数会被调用。如果一个匹配 SandwichError.outOfCleanDishes 的错误被抛出,washDishes() 函数会被调用。如果一个匹配 SandwichError.missingIngredients 的错误被抛出,buyGroceries(_:) 函数会被调用,并且使用 catch 所捕捉到的关联值 [String] 作为参数。

基本预算符

除了Objective-C原有的基本运算符之外,Swift又新增了如下几个运算符

空合运算符(Nil Coalescing Operator)

空合运算符(a ?? b)将对可选类型 a 进行空判断,如果 a 包含一个值就进行解封,否则就返回一个默认值 b。表达式 a 必须是 Optional 类型。默认值 b 的类型必须要和 a 存储值的类型保持一致。

a ?? b 就相当于 a != nil ? a! : b

区间运算符(Range Operators)

Swift 提供了两个方便表达一个区间的值的区间运算符。

闭区间运算符

闭区间运算符(a...b)定义一个包含从 ab(包括 ab)的所有值的区间。a 的值不能超过 b

for index in 1...5 {
    print("\(index) * 5 = \(index * 5)")
}
// 1 * 5 = 5
// 2 * 5 = 10
// 3 * 5 = 15
// 4 * 5 = 20
// 5 * 5 = 25

半开区间运算符

半开区间运算符(a..<b)定义一个从 ab 但不包括 b 的区间。 之所以称为半开区间,是因为该区间包含第一个值而不包括最后的值。

let names = ["Anna", "Alex", "Brian", "Jack"]
let count = names.count
for i in 0..<count {
    print("第 \(i + 1) 个人叫 \(names[i])")
}
// 第 1 个人叫 Anna
// 第 2 个人叫 Alex
// 第 3 个人叫 Brian
// 第 4 个人叫 Jack
订阅评论
提醒
guest
0 评论
内联反馈
查看所有评论
0
希望看到您的想法,请您发表评论x