【Swift 3.1】01 - 基础知识 (The Basics)

基础知识 (The Basics)

自从苹果2014年发布Swift,到现在已经两年多了,而Swift也来到了3.1版本。去年利用工作之余,共花了两个多月的时间把官方的Swift编程指南看完。现在整理一下笔记,回顾一下以前的知识。有需要的同学可以去看官方文档>>


常量和变量 (Constants and Variables)

声明常量和变量 (Declaring Constants and Variables)

使用let来声明常量,用var来声明变量。例如下面的常量和变量用来跟踪用户的输入的密码次数:

let maximumNumberOfLoginAttemps = 10
var currentLoginAttemps = 0

当然,我们还可以用一行代码中声明多个常量或者变量,并且用逗号隔开:

var x = 0.0, y = 0.0, z = 0.0

注意:如果在代码中,你存储的一个值永远不会变,请一定使用let来声明;后续需要改变的值用var声明。

类型注释 (Type Annotations)

当我们定义一个常量或者变量的时候,可以给一个具体的类型。例如:

var welcomeMessage: String
welcomeMessage = "Hello"

同样,我们可以用一行代码注释多个变量的类型:

var red, green, blue: Double

在大多数情况下,我们不必注释变量的类型,因为Swift能根据常量或者变量的值来推断类型。

命名常量和变量 (Naming Constants and Variables)

常量和变量名几乎可以包含任意字符,包括Unicode:

let π = 3.14159
let 你好 = "你好世界"
let 🐶🐮 = "dogcow"

常量和变量的命名规则和OC一样,不能包括空格、数学符号、箭头、私有(或非法)的Unicode代码、线条和方块图字符;也不能以数字开头,但数字可以出现在后面的其他位置。

打印常量和变量 (Printing Constants and Variables)

直接使用print(_:separator:terminator:)方法来打印:

print(welcomeMessage)
// Prints "Hello"

print(_:separator:terminator:)方法是一个全局方法,separatorterminator参数有默认值,所以在使用这个方法时可以省略。

当我们要拼接一个变量到字符串,并且打印出来,可以这样写:

print("\(welcomeMessage), Lebron James!")
// Prints "Hello, Lebron James!"

注释 (Comments)

单行注释
// This is a comment.
多行注释
/* This is also a comment
 but is written over multiple lines. */
内嵌多行注释
/* This is the start of the first multiline comment.
 /* This is the second, nested multiline comment. */
 This is the end of the first multiline comment. */

分号 (Semicolons)

不同于其他语言,Swift在每行代码后面不需要加分号。

整数 (Integers)

Swift提供了8、16、32和64位的整数。这些整数的命名和C语言类似,例如8位无符号整数是UInt8,32位有符号整数是Int32。向其他Swift类型一样,这些整数类型的名称都是大写字母开头的。

整数边界 (Integer Bounds)

我们可以使用minmax属性来访问各个整数类型的最小值和最大值。

let minValue = UInt8.min  // UInt8的最小值是0
let maxValue = UInt8.max  // UInt8的最大值是255
Int

在多数情况下,我们不必在代码中指定整数的取值范围。Swift提供了另外一个整数类型Int,它的取值范围取决于当前运行平台:

  • 在32-bit的平台上,Int相当于Int32
  • 在64-bit的平台上,Int相当于Int64

注意:当你真的特别需要无符号的整数类型时才使用Unit,否则一般情况下是推荐使用Int,即使你知道你要存储的值是非负数。一致使用Int能增强代码的互动性,可以避免不同数字类型的转换。

UInt

Swift也提供了不带符号的整型,UInt,他的取值范围也取决于当前运行平台:

  • 在32-bit的平台上,UInt相当于UInt32
  • 在64-bit的平台上,UInt相当于UInt64

浮点型 (Floating-Point Numbers)

浮点型的变量存储的值范围比整数型更广。Swift提供了两种有符号的浮点型类型:

  • Double:代表64-bit浮点型
  • Float:代表32-bit浮点型

注意:Double的精度可以达到15为小数,而Float只能达到6位小数。选用哪一种类型取决于你的实际情况。一般情况下,我们直接使用Double

类型安全与类型推断 (Type Safety and Type Inference)

细心的同学可以发现,在上面定义的一些变量,我们无需在最前面写变量的类型,而是直接用let或者var。因为Swift可以直接根据你赋给那个变量的值来推断变量的类型。例如:

let meaningOfLife = 42 // meaningOfLife被推断为Int类型,很明显,我们从42字面上就能看出是一个整数
let pi = 3.14159 // pi被推断为Double类型

在推断浮点型数字的时候,Swift会选择推断为Double,而不是Float。

如果一个整数和一个小数相加,以算式的方式复制给一个变量,这个变量会被推断为Double类型:

let anotherPi = 3 + 0.14159 // anotherPi是Double类型

数值类型的字面值 (Numeric Literals)

整数类型的字面值应该这样写:

  • 十进制数,没有前缀
  • 二进制数,需要加0b前缀
  • 八进制数,需要加0o前缀
  • 十六进制数,需要加0x前缀

按照上面的规则,十进制数17可以写成:

let decimalInteger = 17
let binaryInteger = 0b10001       // 17 in binary notation
let octalInteger = 0o21           // 17 in octal notation
let hexadecimalInteger = 0x11     // 17 in hexadecimal notation

浮点型的字面值也可以写成10进制(没有前缀),或者16进制(0o前缀)。10进制的浮点型还需要一个可选的指数e;16进制的也需要一个指数p

十进制的浮点型有一个指数e,这个浮点型的值就等于基数乘以10e

  • 1.25e2就是 1.25 x 102,或者125.0。
  • 1.25e-2就是 1.25 x 10-2,或者0.0125。

十六进制的浮点型有一个指数p,这个浮点型的值就等于基数乘以2p

  • 0xFp2 就是 15 x 22,或者是60.0。
  • 0xFp-2 就是 15 x 2-2,或者是3.75。

下面这些变量的字面值都代表着十进制的12.1875

let decimalDouble = 12.1875
let exponentDouble = 1.21875e1
let hexadecimalDouble = 0xC.3p0

数值类型的字面值可以包含其他格式化的字符使得这个数更易读。整型和浮点型都可以加上额外的0和包含_来增强可读性。这些额外增加的字符不会改变这个数的值。

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

数字类型转换 (Numeric Type Conversion)

整型转换 (Integer Conversion)

不同整数类型的取值范围是不同的。例如Int8只能存储-128127的数值;而`UInt8`只能存储0225。如果一个数超过了这个整数类型的范围,将会报错:

let cannotBeNegative: UInt8 = -1
// UInt8 cannot store negative numbers, and so this will report an error
let tooBig: Int8 = Int8.max + 1
// Int8 cannot store a number larger than its maximum value,
// and so this will also report an error

类型转换的格式方法如下:

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

因为oneUInt8类型,而twoThousandUInt16类型,所以他们不能直接相加,需要把one转换成UInt16类型,然后才可以相加。

整型和浮点类型转换 (Integer and Floating-Point Conversion)

整型和浮点型之间的转换必须显示转换:

let three = 3
let pointOneFourOneFiveNine: 0.14159
let pi = Double(three) + pointOneFourFiveNine
// pi 等于 3.14159, 并且被推断为Double类型

浮点型转为整型也必须是显示的:

let integerPi = Int(pi)
// integerPi 等于 3, 并且被推断为Int类型

当浮点型转为整型时,浮点型的小数部分总是被省略掉,只取整数部分。也就是说4.75会被转为4,-3.9被转为-3。

类型别名 (Type Aliases)

使用typealias关键字来定义一个别名。当我们想用一个更清晰更适合的类型来表达一个变量的时候,使用别名是非常有用的。例如我们从外部资源操作一个特定大小的数据时:

typealias AudioSample = UInt16

定义好这个别名之后,你就可以像使用UInt16一样地使用AudioSample

var maxAmplitudeFound = AudioSample.min
// maxAmplitudeFound is now 0

因为AudioSample是一个别名,所以AudioSample.min实际上就是UInt16.min

其实别名可以这么理解,就是为代码的可读性更高,更贴合项目实际,例如我们经常见的TimeInterval,实际上是一个Double类型。

布尔值 (Booleans)

Swift提供了两个布尔类型的常量:truefalse

let orangesAreOrange = true
let turnipsAreDelicious = false

orangesAreOrangeturnipsAreDelicious被推断为Bool类型。

Swift是不允许其他不是布尔类型的值替代Bool的。例如下面这个例子就会报错:

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

如果改为下面这个例子就可以编译通过:

let i = 1
if i == 1 {
    
}

i == 1 的结果是Bool类型,所以第二个例子是正确的。

多元组 (Tuples)

一个多元组包含了多个元素,多元组里面的元素可以是任何类型,并且可以是不一样的类型。例如:

let http404Error = (404, "Not Found")

http404Error这个多元组描述了HTTP的一个状态码,里面包含了一个IntString,所以这个多元组的类型是(Int, String)

我们可以拆分多元组里面的元素,例如:

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

如果你只需要访问其中的一个元素,可以使用_来忽略其他元素:

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

另外,我们还可以使用下标来访问里面的元素:

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

给多元组的元素命名:

let http200Status = (statusCode: 200, description: "OK")

一旦里面的元素有了名字,就可以使用名字来访问:

print("The status code is \(http200Status.statusCode)")
// Prints "The status code is 200"
print("The status message is \(http200Status.description)")
// Prints "The status message is OK"

多元组作为方法的返回值是特别有用的,我们在方法里可以返回更多的信息。

可选类型 (Optionals)

可选类型是Swift新增的一个类型,在OC或者C中是没有的。当一个值有可能为空时,就是用可选类型。一个可选类型代表着两种可能:1)有一个特定的值,并且可以把这个可选值解包得到他真正的值;2)没有值,也就是nil

下面举个例子,把String转换为Int:

let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
convertedNumber被推断为Int?类型,或者说是 optional Int

因为Int(possibleNumber)在转换的时候,假如possibleNumber的字面值不是数字,那就会转换不成功,最后返回nilconvertedNumber。所以convertedNumber的值有两种可能,要么就转成功,返回具体值,要么就转换不成功,返回nil。所以convertedNumber是Int?类型。这个?的意思是变量的值可能会是nil

nil

我们可以把nil赋值给一个可选类型:

var serverResponseCode: Int? = 404
// serverResponseCode contains an actual Int value of 404
serverResponseCode = nil
// serverResponseCode now contains no value

注意:nil不能赋值给非可选类型的常量和变量。所以如果在代码中,如果变量有可能为nil,尽量声明为可选类型。

如果在声明可选类型变量的时候,没有给一个默认值,那么这个变量默认为nil

var surveyAnswer: Stirng?
// surveyAnswer 自动设置为nil

注意:Swift的nil和OC中的nil是不同的。在OC中,nil是一个指针,指向一个不存在的对象;而在Swift中,nil不是指针,只是表示一个特定类型的值是空的。任何可选类型都可以设置为nil,不仅仅是对象类型。

If语句和强制解包 (If Statements and Forced Unwrapping)

使用==或者!=来判断一个可选类型的变量是否等于nil

if covnertedNumber != nil {
    print("convertedNumber contains some integer value.")
}

当我们能确定可选类型变量确实包含了一个值,可以使用!来解包这个可选类型变量:

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

注意:在使用!解包时,一定要保证可选类型的变量有值,不能为nil,否则在运行的时候会报错。

可选绑定 (Optional Binding)

使用可选绑定来确认一个可选类型是否有值,如果有值,那么就赋值给一个局部的变量或者常量。可选绑定可以配合ifwhile语句来检查一个可选类型变量是否有值:

if let constantName = someOptional {
    // do something...
}

我们可以使用可选绑定重写之前的这个例子:

// 之前的例子
if convertedNumber != nil {
    print("convertedNumber has an integer value of \(convertedNumber!).")
}

// 使用可选绑定重写,改为
if let actualNumber = Int(possibleNumber) {
    print("\"\(possibleNumber)\" has an integer value of \(actualNumber)")
}
else {
    print("\"\(possibleNumber)\" could not be converted to an integer")
}
// Prints ""123" has an integer value of 123"

代码可以这么解读:如果Int(possibleNumber)返回的值是一个非nil的值,那么就把这个值赋值给actualNumber,然后actualNumber就可以作为局部变量在if的第一个分支语句里使用。因为actualNumber已经确定有非nil值,所以在打印那句代码无需用!解包。

在if语句中,我们可以包含多个可选绑定和布尔条件,并且以,隔开。只要任意一个可选绑定或者任意一个布尔条件的结果为false,那么整个if语句的值将会被判断为false:

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

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

第一种写法更为简短。

隐式解包可选类型 (Implicitly Unwrapped Optionals)

有时候,我们能确定一个可选类型被第一次赋值后,总会有值。那我们就可以把这种可选类型声明为隐式解包可选类型。在要设置为可选的类型名称后面加上!,例如String!

其实隐式解包可选类型实际上也是一个正常的可选类型,但是可以像非可选类型一样使用,访问它的值时不用解包。

let possibleString: String? = "一个可选类型的字符串"
let forcedString: String = possibleString! // 需要感叹号解包

let assumedString: String! = "一个隐式解包可选类型的字符串"
let implicitString: String = assumedString // 无需感叹号解包

注意:如果一个隐式解包可选类型是nil,然后你访问他的解包后的值,将会在运行的时候报错。

像正常的可选类型一样,你可以使用if检查是否有值:

if assumedString != nil {
    print(assumedString)
}
// Prints "An implicitly unwrapped optional string."

也可以使用可选绑定:

if let definiteString = assumedString {
    print(definiteString)
}
// Prints "An implicitly unwrapped optional string."

注意:当一个变量有可能为nil时,千万不要使用隐式解包可选绑定。

错误处理 (Error Handling)

当一个方法遇到错误条件时,会抛出一个错误。方法的调用者就能抓取这个错误,然后进行处理:

func canThrowAnError() throws {
    // this function may or may not throw an error
}

在声明方法时,使用throws关键字来说明这个方法会抛出异常。当调用一个会抛出异常的方法时,使用try关键字。

do {
    try canThrowAnError()
    // no error was thrown
}
catch {
    // an error was thrown
}

可以使用多个catch来抓取不同的异常:

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

在这个例子中,如果没有干净的盘子可用或者缺少某种做三明治的原料时,makeASandwich()将会抛出异常。如果没有异常,就会执行eatASandwich()。如果抓取的错误匹配SandwichError.outOfCleanDishes,就会执行washDishes(),如果抓取的错误匹配SandwichError.missingIngredients,就会执行buyGroceries(_:)

断言 (Assertions)

调试断言 (Debugging with Assertions)

在某些情况下,我们需要让某些变量满足特定的条件之后,才让代码继续执行。如果不满足,代码会运行错误。我们可以在代码中使用断言来阻止代码执行,然后调试错误的原因。

let age = -3
assert(age >= 0, "人的年龄不能小于0")

如果age >= 0true,那么代码继续执行,如果是false,那么程序将会停止,并且打印人的年龄不能小于0

注意: 在当优化优化编译时,断言会被禁用,例如在Xcode中使用一个程序的默认发布配置建立新版本的时候。

何时使用断言 (When to Use Assertions)

当一个判断条件有可能为false时,可以使用断言。下面这些情况可以使用断言:

  • 当一个整数下标被传给一个自定义的下标实现时,但是下标的值可能会太小或太大。
  • 当一个值作为参数传给一个方法,但是一个非法的值不能让这个方法完成它的任务。
  • 一个可选类型的值目前是nil,但是后面的代码要求这个可选类型要有一个非nil的值。

第一部分完。下个部分:【Swift 3.1】02 - 基本运算符 ((Basic Operators)


如果有错误的地方,欢迎指正!谢谢!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,194评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,058评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,780评论 0 346
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,388评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,430评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,764评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,907评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,679评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,122评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,459评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,605评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,270评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,867评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,734评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,961评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,297评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,472评论 2 348

推荐阅读更多精彩内容