Swift 基础部分

Swift 包含了C和Objective-C上所有基础数据类型,Int表示整型值;DoubleFloat表示浮点型值;Bool是布尔型值;String是文本型数据。 Swift 还提供了三个基本的集合类型ArraySetDictionary
除了我们熟悉的类型,Swift还增加了Objective-C中没有的高阶数据类型比如元组(Tuple)。
Swift还增加了可选(Optional)类型,用于处理值缺失的情况。

Swift是一门类型安全的语言,这意味着Swift可以让你清楚地知道值的类型。

常量和变量

常量的值一旦设定就不能改变,而变量的值可以随意更改。

声明常量和变量

let来声明常量,用var来声明变量

//一行中声明多个
var x = 0.0, y = 0.0, z = 0.0

注意:如果你的代码中有不需要改变的值,请使用 let 关键字将它声明为常量。只将需要改变的值声明为变量。

类型标注

声明常量或者变量的时候可以加上类型标注,在常量或者变量名后面加上一个冒号和空格,然后加上类型名称。(声明时如果有初始值,Swift可以推断出这个常量或者变量的类型)

//可以在一行中定义多个同样类型的变量,用逗号分割,并在最后一个变量名之后添加类型标注:
var red, green, blue: Double

常量和变量的命名

常量与变量名不能包含数学符号,箭头,保留的(或者非法的)Unicode 码位,连线与制表符。也不能以数字开头,但是可以在常量与变量名的其他地方包含数字。

如果你需要使用与Swift保留关键字相同的名称作为常量或者变量名,你可以使用反引号(`)将关键字包围的方式将其作为名字使用。(不推荐)

输出常量和变量

print(someValue, separator:, terminator:) 函数来输出当前常量或变量的值:
separator用来设置各个值间的拼接字符串
terminator用来设置输出结果结束后的结束字符,默认为“\n”换行

print("111", "222", separator:"--", terminator:"结束符 ")
print("111", "222", separator:"--", terminator:"结束符 ")
//输出结果(并没有换行)
//111--222结束符 111--222结束符

Swift字符串插值(string interpolation)的方式把常量名或者变量名当做占位符加入到长字符串中,Swift会用当前常量或变量的值替换这些占位符。将常量或变量名放入圆括号中,并在开括号前使用反斜杠将其转义:

/*
    若只是要print一个变量或者常量可以直接print,但是如果要将这个拼接到字符串中需要这
    样做
*/
print("The current value of friendlyWelcome is \(friendlyWelcome)")
// 输出 "The current value of friendlyWelcome is Bonjour!

print(_:separator:terminator:)这个函数第一个参数为_即并没有设置标签,其他参数设置了标签并且有默认值因此是可选的

注释

单行注释用双正斜杠( // )作为起始标记:
多行注释用:

/* 这是第一个多行注释的开头
/* 这是第二个被嵌套的多行注释 */
这是第一个多行注释的结尾 */

多行注释可以去嵌套注释,我们可以方便的注释掉大段代码 即使代码中已经包含注释(oc中是没有多行注释的)

分号

Swift并不需要在末尾添加分号,但是如果单行内写多条语句时,必须用分号分开;

整数

Swift 提供了8,16,32和64位的有符号和无符号整数类型(即分别为1、2、4、8字节)。
命名:比如8位无符号整数类型是 UInt8 ,32位有符号整数类型是 Int32 。

范围

我们可以用不同整数类型的minmax属性获取对应类型的最小值和最大值:

//返回的类型正是其对应类型
let minValue = UInt8.min // minValue 为 0,是 UInt8 类型
let maxValue = UInt8.max // maxValue 为 255,是 UInt8 类型

Swift提供了整数类型IntUInt其长度和当前平台原生字长相同

补充:
尽量不要使用UInt ,除非你真的需要存储一个和当前平台原生字长相同的无符号整数。除了这种情况,最好使用Int,即使你要存储的值已知是非负的。统一使用Int可以提高代码的可复用性,避免不同类型数字之间的转换,并且匹配数字的类型推断

浮点数

Swift提供了两种有符号浮点数类型:

  • Double 表示64位浮点数。当你需要存储很大或者很高精度的浮点数时请使用此类型。
  • Float 表示32位浮点数。精度要求不高的话可以使用此类型。

Double精确度很高,至少有15位数字,而Float只有6位数字。选择哪个类型取决于你的代码需要处理的值的范围,在两种类型都匹配的情况下,将优先选择Double

类型安全和类型推断

Swift是一个类型安全(type safe)的语言。因此它会在编译时进行类型检查,将不匹配的类型标记为错误。但是也不需要每个都显示的指定类型,编译器可以在编译代码的时候自动推断出表达式的类型。

//当推断浮点数的类型时,Swift 总是会选择 Double 而不是 Float 
let pi = 3.14159
// pi 会被推测为 Double 类型

如果表达式中同时出现了整数和浮点数,会被推断为 Double 类型

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

数值型字面量

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

对于浮点数用10进制或者16进制表示,可以用指数来表示,10进制的指数为e(可为大写或小写),16进制使用大写或者小写的p

1.25e2 表示 1.25 × 10^2,等于 125.0 
1.25e-2 表示 1.25 × 10^-2,等于 0.0125
0xFp2 表示 15 × 2^2,等于 60.0
0xFp-2 表示 15 × 2^-2,等于 3.75

补充:
为了可读性:整数和浮点数都可以添加额外的零并且包含下划线,并不会影响字面量:

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

数值型类型转换

前面说过,即使知道整数常量或者变量为非负也推荐使用Int类型,这样可以保证我们的的变量或常量可以直接被复用并且可以匹配整数类字面量的类型推断

我们通常使用其他整数类型是为了处理外部明确长度的数据或者优化性能、内存。这样直接使用显式指定长度的类型可以及时发现值溢出并且可以暗示正在处理特殊数据。

整数转换

Swift任何时候都不会隐式进行类型转换,所以我们应该根据情况进行显式的进行类型装换,这样代码的转换意图也更清晰

let twoThousand: UInt16 = 2_000
let one: UInt8 = 1
let twoThousandAndOne = twoThousand + UInt16(one)

SomeType(ofInitialValue) 是调用Swift构造器并传入一个初始值的默认方法(但是也不能接受任意类型的值,只能传入SomeType内部有对应构造器的值)

整数和浮点数转换

整数和浮点数之间可以互相转换

let pi = Double(3);
let integerPi = Int(3.1415)
//这样转换的话会向下取整
//即浮点值会被截断

类型别名

给现有类型定义另外一个名字,使用typealias关键字来定义类型别名
typealias AudioSample = UInt16
定义了一个类型别名之后,你可以在任何使用原始名的地方使用别名

布尔值

Swift提供基本布尔类型Bool,有两个布尔常量:turefalse
Swift中的Bool是非常严格的必须为ture或者false,而不是OC中的非0为真

元组

元组将多个值组合为一个复合值,其中可以为任意类型

元组只适用于我们临时组织数据,不适合创建复杂的数据结构,如果不是临时使用请使用类或者结构体

//定义元组
let http404Error = (404, "Not Found")
//元组解析 也可以将 (statusCode, statusMessage) 看为定义的元组名称
let (statusCode, statusMessage) = (404, "Not Found")
//元组解析 利用 _ 忽略其他的部分
let (justTheStatusCode, _) = http404Error
print((statusCode, statusMessage).0, (statusMessage), http404Error.1)


let (status200Code, status200Message) = (statusCode: 200, description: "OK")
status200Code
(status200Code, status200Message).1
//这种方式报错,因为用(status200Code, status200Message) 已经将元组内容部分解析,定义的名称已经被覆盖了
//(status200Code, status200Message).status200Code 
let status400 : (statusCode: Int, description: String) = (404, "Not Found")
status400.statusCode

可选类型

可选类型用来处理值可能缺失的情况

OC中并没有这个类型,在oc中为了表达这个意思我们通常让方法返回一个特殊值(比如 NSNotFound),来让我们对特殊值进行判断,Swift提供了一个可选类型可以按时任意类型值得缺失很方便.

可选类型的值: 要不有值为X 要不为没有值

//我们将String强转为Int 因为并不是所有字符串都能转为整数,因此返回可选类型
let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
// convertedNumber 被推测为类型 "Int?", 或者类型 "optional Int"

问号表示是可选类型Int,就是有可能包含Int也有可能不包含值

nil

Swift中的nil和OC中并不同。 在OC中nil是一个指向不存在的对象的指针。而Swift中nil不是一个指针,是一个确定的值来表示值得缺失,任何可选状态都可被置为nil,不仅是对象类型。nil只能设置可选类型的值,如果想设置一个常量或者变量,需要先将其设置为可选类型

var serverResponseCode: Int? = 404
serverResponseCode = nil
//对于未赋值的可选类型 默认值为 nil
var surveyAnswer: String?

if语句以及强行解析

对于一个可选类型值,可以通过 在其后加上!强制解析来获取其值,但是对于不存在的值加!会导致错误,因此在强制解析前需要判断非空

if convertedNumber != nil {
print("convertedNumber has an integer value of \(convertedNumber!).")
}

可选绑定

上面强行解析后这个值还是可选类型,为了避免这种情况我们可以使用可选绑定,用着ifwhile语句中,通过判断可选类型是否有值,有值得话将其赋值给一个确定类型的变量或者常量,这样就可以使用这个变量或者常量来避免使用可选类型的值避免使用强行解析

缺点是这样创建的常量或者变量只能在这个if语句中使用

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)不是nil就可创建一个常量actualNumber指向这个值

隐式解析可选类型

有时在可选类型第一次被赋值后,就可以确定其总是有值,这时候我们可以声明其为隐式解析可选类型来避免每次都判断解析
通过将可选类型后的 ? 替换为!来声明隐式解析可选类型

可以将隐式解析可选类型当做一个可以自动解析的可选类型
使用隐式解析必须确保这个值不会为空 否则和对空值进行强行解析一样会出错

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

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

错误处理

Swift允许我们在函数声明中通过添加 throw类型来判处错误消息
当我们调用一个可能抛出错误消息的函数时,应该在表达式前置try关键字

这里只是简单了解

func makeASandwich() throws {
// ...
}
do {
    try makeASandwich()
    eatASandwich()//没有错误抛出会执行这个函数
} catch SandwichError.outOfCleanDishes {
    washDishes()
} catch SandwichError.missingIngredients(let ingredients) {
    buyGroceries(ingredients)//匹配的错误抛出 函数被调用并且会
}

断言

有时我们需要在代码中判断某些条件是否满足条件来决定是否继续执行,这时候我们需要断言

使用断言进行调试

断言会在运行时判断一个逻辑条件是否为ture。如果条件判断为true,代码运行会继续进
行;如果条件判断为false ,代码执行结束,你的应用被终止。

我们在调试环境中触发一个断言,可以检查断言被触发时你的应用的状态。此外,断言允许你附加一条调试信息。

你可以使用全局assert(_:_:file:line:)函数来写一个断言.
向这个函数传入一个结果为 true或者false的表达式以及一条信息,当表达式的结果为 false 的时候这条信息会被显示:

let age = -3
assert(age >= 0, "A person's age cannot be less than zero")
// 因为 age < 0,所以断言会触发

当代码使用优化编译的时候,断言将会被禁用,例如在Xcode中,使用默认的target Release配置选项来build时,断言会被禁用。

何时使用断言

当条件可能为假时使用断言,但是最终一定要保证条件为真,这样你的代码才能继续运行。

断言可能导致你的应用终止运行,所以你应当仔细设计你的代码来让非法条件不会出现。然而,在你的应用发布之前,有时候非法条件可能出现,这时使用断言可以快速发现问题。

断言的适用情景:

  • 整数类型的下标索引被传入一个自定义下标实现,但是下标索引值可能太小或者太大。
  • 需要给函数传入一个值,但是非法的值可能导致函数不能正常执行。
  • 一个可选值现在是 nil ,但是后面的代码运行需要一个非 nil 值。