Swift 初学者 ( 10/12 ) 数组

简介

这是一个Swift语言教程,基于最新的iOS 9,Xcode 7.3和Swift 2.2,会为你介绍Swift编程非常基础的内容。从电脑如何工作的全程基本原理到语言结构,你会足够了解这门语言,来处理数据和管理代码的行为。

快速链接


第二部分:集合类型

到目前为止,你看到的数据主要都是单个形式。尽管元祖可以有多块数据,但你必须指定大小;带有三个字符串的元祖和带有两个字符串的元祖是完全不同的类型,他们之间的转换不是件小事。

在这个部分,你会学习有关 Swift 中集合类型(collection types)的内容。集合是灵活的“容器”,可以让你一起存储任意多个值。

Swift 中有三个集合类型:数组,字典和 sets。你会在下面三篇文章用这个顺序来学习他们:

  • 教程 10,数组
  • 教程 11,字典
  • 教程 12,sets

集合类型们有相似的外观,但非常不同的用法。当你读完三篇文章后,把差别记在脑海里,你要开始养成随时知道应该用哪种类型的那种感觉。

作为探索集合类型之间区别的一部分,我们也会考虑性能:集合执行特定操作有多快,比如向集合中添加元素或在其中搜索。

讨论性能的常用方式是用大O符号(big-O notation)。如果你还不熟悉它,读一读简短的介绍吧。

介绍大O符号

大O符号是描述运行时间(running time)的一种方式,也就是一个操作用了多长时间来完成。想法是操作用的确切时间并不重要;重要的是在规模上的相对差异。

想想你有一列名字,是某种随机的顺序,现在你需要找到列表上的第一个名字。这列只有一个名字或一百万个名字都不重要—找到最开始的那个名字总是花同样多的时间。这是一次恒量时间(constant time)操作的例子,或者是大O符号中的 O(1)

现在假设你需要找到列表中的一个特定名字。你需要扫描整个列表,看每一个单独的名字,直到你找到一个匹配或抵达终点。这次也是,我们不关心这次用的确切时间,只关心相比其他操作的相对时间。

要计算运行时间,要考虑工作的单位。你需要看每个名字,所以把这认为是每个名字的工作的一个“单位”。如果你有 100 个名字,那就是 100 个工作单位。如果你把名字的个数翻倍到 200;那怎么改变工作的量呢?他让工作的量翻倍了。相似的,如果你让名字的数量变成四倍,那也让工作的量变成四倍。

这是线性时间(linear time)操作的例子,也就是大O符号中的 O(N)。输入的大小是变量 N,表示操作花的时间的量也是 N。这里有一个直接的、线性的关系,在输入尺寸(列表里名字的数量)和搜索一个名字会用的时间之间。

你可以看到为什么恒量时间操作在 O(1) 里有数字 1。不论怎么样,他们只是一个单独的工作单位!

你可以通过搜索网络来读更多大O符号相关的内容。这个教程里你只需要恒量时间和线性时间,但还有其他的超出这个范围的复杂时间(time complexities)

大O符号在处理集合类型的时候尤其重要,因为集合可以存储很大量的数据,并且你需要知道在添加、删除和编辑值得时候的运行时间。

例如,如果集合类型 A 搜索有恒量时间,集合类型 B 搜索有线性时间,你选择使用哪个会基于你计划搜索的量级。

数组

数组(Arrays)是你会在 Swift 里遇到的最通用的集合类型。数组是类型化的,就像常规的变量和常量,存储多个值比如一个简单的列表。

在你创建第一个数组前,花一点时间考虑一下细节,数组是什么以及为什么你想要用它。

什么是数组?

数组是同类型的值的有序集合。数组里的元素是零索引(zero-indexed),表示第一个元素的索引是 0,第二个元素的索引是 1,以此类推。知道这一点,你可以算出最后一个元素的索引是数组中的值的个数减 1。

这个数组里有五个元素,索引值是 0-4。

还有,所有值都是 String 类型。你不能向一个保存字符串的数组添加非字符串类型。注意相同的值可以出现多次。

什么时候数组有用?

当你想把一些东西用一个特定顺序保存的时候,数组很有用。你想要顺序可能是因为要分类整理元素,或者需要通过索引取出元素,而不是遍历一整个数组。

例如,如果你在存储高分数据,顺序不影响。你会想要最高分出现在列表的第一个(也就是在 0 索引),第二高的分数次之,以此类推。

可修改 vs 不可修改数组

就像你之前已经读到的类型,比如 String 或 Int,当你创建数组的时候,必须声明它为常量或变量。

如果数组创建之后不需要改变,你应该用 let 声明它为常量,让它不可修改。如果你需要添加、移除或更新数组里的值,你应该声明它为变量来创建一个可修改的数组。

创建数组

在这个部分,你会练习声明数组和给他们赋值初始值。

显示声明

你可以显示声明一个数组,或利用 Swift 的类型推断特色。这是一个显示声明的例子:

let numbers: Array<Int>

尖括号里的类型定义了数组可以存储的值类型,编译器在你向数组添加元素的时候会这么强迫你。例如,如果你想添加一个字符串,编译器会返回一个错误,你的代码就无法编译了。这个强大的强迫机制叫做 generics,你会在后面学习更多。

在这个例子里,你把 numbers 声明为一个数组,只能存储 Int 值。你把这个数组定义为常量,所以它的值不能改变。

推断声明

Swift 也能从类型的初始化中推断数组的类型:

let inferredNumbers = Array<Int>()

这里 inferredNumbers 也是一个整数的数组。如果你跟着做了,把这行代码打到了 playground 里,你会注意到结果区域的 []。这是 Swift 对于空数组的表示。

另外,定义数组简短点的方式是用方括号围绕类型,像这样:

let alsoInferredNumbers = [Int]()

这是对于数组的最常用的声明形式,你会在本教程中贯穿使用。

数组字面值

当你声明数组的时候,你可能想给它初始值。你可以用数组字面值(array literals),是一个提供数组值得简洁的方式。一个数组字面值是一列值,用逗号间隔,被方括号包围:

let evenNumbers = [2, 4, 6, 8]

因为声明只包含整数,Swift 推断 evenNumbers 的类型为 Int 值的数组,也就是 [Int]。

创建一个数组,把值全部设置为一个默认值也是可能的:

let allZeros = [Int](count: 5, repeatedValue: 0)
// > [0, 0, 0, 0, 0]

因为 [Int]—包含方括号—只是一个类型,你在这个例子里调用了它的初始化。两个参数制定了个数,和将要被重复的值。

你到目前为止创建的数组都是不可修改的,因为你把他们赋值为常量。对于不会改变的数组这是一个好的习惯。例如,考虑这个数组:

let vowels = ["A", "E", "I", "O", "U"]

vowels (元音)是字符串的数组,并且它的值不会被改变。但这很好呀,因为元音的列表不会经常变!

访问元素

能够创建数组并没有什么用,除非你知道怎么从数组里获取值。在这个部分,你会学习到访问数组里元素的几种不同方式。

使用 properties 和方法

假设你在创建一个纸牌游戏,希望用一个数组存储选手的名字。选手加入或离开游戏的时候要改变这个列表,所以要声明一个可被修改的数组:

var players = ["Alice", "Bob", "Cindy", "Dan"]

这个例子里,players 是一个可变数组,因为你把它分配为变量。在游戏开始前,需要确保有足够的选手。可以用 isEmpty property 来检测是不是有至少一名选手:

print(players.isEmpty)
// > false

数组不是空的,但要开始游戏的话至少要两个选手。可以用 count property 来获取选手的数量:

if players.count < 2 {
    print("我们需要至少两名选手!")
} else {
    print("开始吧!")
}
// > 开始吧!

是时候开始游戏了!你决定游戏的顺序是根据数组里的名字排列顺序。那怎么得到第一个选手的名字呢?

数组提供了 first property 来获取数组的第一个对象:

var currentPlayer = players.first

打印 currentPlayer 的值显示了有趣的东西:

print(currentPlayer)
// > Optional("Alice")

frist property 实际上返回了一个可选值,因为如果数组是空的话,first 会返回 nil。

相似的,数组有一个 last property 返回数组的最后一个值,如果数组是空的话就返回 nil:

print(players.last)
// > Optional("Dan")

从数组获取值的另一种方式是通过调用 minElement()。这个方法返回数组里有最低的的元素——不是最低的索引!如果数组包含的是字符串,就返回按字母表顺序最低的字符串,在这个例子里是"Alice":

currentPlayer = players.minElement()
print(currentPlayer)
// > Optional("Alice")

很明显,frist 和 minElement() 不会总返回同样的值。例如:

print([2, 3, 1].first)
// > Optional(2)
print([2, 3, 1].minElement())
// > Optional(1)

你可能已经猜到了,数组也有一个 maxElement() 方法。

注意:first 和 last properties 和 minElement() 和 maxElement() 方法不只是数组有。每个集合类型都有这些 properties 和方法,在那些许许多多的属性以外。你会在读到协议(protocols)的时候学习更多有关这个行为的内容。

现在你已经搞明白怎么得到第一名选手了,你要介绍一下这名选手是谁:

if let currentPlayer = currentPlayer {
    print("\(currentPlayer) 将要开始")}
// > Alice 将要开始

你用了 if let 来拆包从 first 得到的可选值;否则,语句会打印 Optional("Alice") 将要开始,这应该不是你想要的吧。

这些 properties 和方法在你想得到第一个,最后一个,最小的或最大的元素的时候会很有帮助。但如果你想要的元素不能通过这些 properties 或方法中的任何一个观察到呢?

使用下标

访问数组里的元素最方便的方式是通过使用下标(subscript)语法。语法允许你通过使用包括索引的方括号来直接访问任意值:

var firstPlayer = players[0]
print("第一名选手是 \(firstPlayer)")
// > 第一名选手是 "Alice"

因为数组是零索引,所以用 0 索引来获取第一个对象。你可以使用一个更大的索引来获得数组里的下一个元素,但如果你尝试访问超过了数组尺寸的索引,你会得到一个运行时错误。

var player = players[4]
// > fatal error: Array index out of range(致命错误:数组索引超出范围)

为什么会得到这个错误呢?因为 players 只包含了四个字符串。索引 4 表示第五个元素,但这个数组并没有第五个元素呀。

当你使用下标的时候,不需要担心可选值,因为尝试访问一个不存在的索引不会返回 nil;直接就导致运行时错误了。

使用范围

你可以结合范围(ranges)来使用下标语法,从数组中获取不止一个单独的值。例如:如果你想得到接下来要开始的两名选手,可以这样做:

let upcomingPlayers = players[1...2]
print(upcomingPlayers)
// > ["Bob", "Cindy"]

如你所见,upcomingPlayers 常量实际上是和原始数组相同类型的数组。

你用的范围是 1...2,表示每个数组的第二和第三个项目。只要起始值小于结束值,你可以使用任何索引,它们都在数组的范围内。

检查一个元素

你可以检查数组里是否存在至少一个特定的元素,使用 contains(_:),如果找到了就返回 true,没有就 false。

可以用这个方法写一个函数,检查给定的选手在不在游戏里:

func isPlayerEliminated(playerName: String) -> Bool { 
if players.contains(playerName) {
    return false
} else {
    return true
}}

现在可以在任意你需要检查选手是否被淘汰的时候使用这个函数:

print(isPlayerEliminated("Bob"))
// > false

你甚至可以在一个特定范围里测试一个元素是否存在:

players[1...3].contains("Bob")
// > true

现在你能从数组里取数据了,是时候看看可变数组以及如何修改它们的值了。

修改元素

你可以对可变数组做所有类型的改变:添加或移除元素,更新已存在的值,和移动元素到一个不同的顺序。在这个部分,你会看到如何处理数组,让它配合你的游戏正在进行的事情。

附加元素

如果新的选手想要加入游戏,他们需要注册然后添加他们的名字到数组里。Eli 是要加入已经存在的四名选手的第一个选手。你可以使用 append(_:) 来把 Eli 加到数组的结尾:

players.append("Eli")

如果你想附加除了字符串以外的东西,编译器会显示一个错误。记住,数组只能包含相同类型的值。还有,append(_:) 只对可变数组奏效。

下一个加入游戏的选手是 Gina。你可以用另一种方式把她附加到游戏里,使用 += 操作符:

players += ["Gina"]

这个表达式右侧是只有一个单独元素的数组,字符串"Gina"。通过使用 +=,你在附加这个数组的元素到 players 上。现在这个数组看起来像这样:

print(players)
// > ["Alice", "Bob", "Cindy", "Dan", "Eli", "Gina"]

这里,你添加一个单独的元素到数组上,但你可以看到附加多个项目会多么简单,只要在 Gina 的名字后面添加更多名字就可以了。

插入元素

这个纸牌游戏的不成文规矩是选手的名字要按字母表顺序排列。如你所见,这个列表遗失了一个以字母 F 开始的选手。幸运的是,Frank 刚刚抵达。你想把他添加到列表的 Eli 和 Gina 中间。要做这件事,使用 insert(_:atIndex:):

players.insert("Frank", atIndex: 5)

atIndex 元素定义了你想添加元素的位置。记住数组是零索引的,所以索引 5 实际上是在 Eli 和 Gina 中间。

移除元素

游戏进行中,其他选手逮到了 Cindy 和 Gina 作弊。应该把他们从游戏中移除!你知道 Gina 在选手列表的最后一个,所以你可以轻松地使用 removeLast() 来移除她:

var removedPlayer = players.removeLast()
print("\(removedPlayer) 被移除了")
// > Gina 被移除了

这个方法做了两件事:它移除了最后一个元素,然后返回了它,以防你需要打印它或存储到其他什么地方去——比如一个作弊者数组!

要从游戏里移除 Cindy,你需要知道她的名字被存储的确切索引。看着选手列表,你发现她在列表的第三个,所以她的索引是 2。

removedPlayer = players.removeAtIndex(2)
print("\(removedPlayer) 被移除了")
// > Cindy 被移除了

你会怎么获得一个元素的索引呢?有一个方法就是做这个的!indexOf(_:) 返回元素的第一个索引,因为数组可能包含同样的值的多个拷贝。如果方法没有找到元素,就返回 nil。

迷你练习

使用 indexOf(_:) 来确定元素"Dan"在 players 里的位置。

更新元素

Frank 已经决定每个人从现在开始都要叫他 Franklin。你可以从数组里移除"Frank"值,然后添加"Franklin",但那对于一个简单的任务来说太多工作量了。作为替代,你应该用下标语法来更新名字:

print(players)
// > ["Alice", "Bob", "Dan", "Eli", "Frank"]
players[4] = "Franklin"
print(players)
// > ["Alice", "Bob", "Dan", "Eli", "Franklin"]

你要小心不要用一个超出数组边界的索引,不然你的 app 就会崩溃。

随着游戏继续,一些选手被淘汰了,新人加入替换他们。幸运的是,你也可以结合范围使用下标来用一行代码更新多个值:

players[0...1] = ["Donna", "Craig", "Brian", "Anna"]
print(players)
// > ["Donna", "Craig", "Brian", "Anna", "Dan", "Eli", "Franklin"]

这段代码替换了前两个选手,Alice 和 Bob,替换为新的 players 数组里的四名选手。如你所见,范围的尺寸不需要等于你在添加的数组持有的值的尺寸。

移动元素

看看这一团糟!players 数组包含名字的首字母从 A 到 F,但他们的顺序不对,这违背了游戏的规则。

你可以尝试解决这个状况,通过手动一个一个移动值到正确的位置上,像这样:

let playerAnna = players.removeAtIndex(3)
players.insert(playerAnna, atIndex: 0)
print(players)
// > ["Anna", "Donna", "Craig", "Brian", "Dan", "Eli", "Franklin"]

如果你想移动一个单独的元素,这会奏效,但如果你想排序整个数组,应该使用sort():

players = players.sort()
print(players)
// > ["Anna", "Brian", "Craig", "Dan", "Donna", "Eli", "Franklin"]

sort() 做的完全就是你预想的那样:返回一个数组排序好的拷贝。如果您想对数组本身(in place)进行排序,而不是返回一个已排序的副本,应该用 sortInPlace()。

遍历数组

现在时间越来越晚了,选手决定今晚暂停,明天继续;在此期间,你要把他们的分数保存到一个单独的数组里。你会在关于字典的教程里看到更好的方式来做这个,但现在你要继续用数组:

let scores = [2, 2, 8, 6, 1, 2]

选手离开之前,你想打印仍然在游戏里的名字。你可以使用 for-in 循环来做,你在第6篇教程里已经读到。

for playerName in players {
    print(playerName)
}
// > Anna
// > Brian
// > Craig
// > Donna
// > Eli
// > Franklin

这段代码经过了 players 的所有元素,从 0 索引知道 players.count - 1,打印了它们的值。在第一个循环里,playerName 等于数组的第一个元素;第二个循环里,等于数组的第二个元素;以此类推直到循环已经打印了数组里的所有名字。

如果你也需要每个元素的索引,你可以遍历数组的 enumerate() 方法的返回值,返回一个元祖包含数组里每个元素的索引和值。

for (index, playerName) in players.enumerate() {
    print("\(index + 1). \(playerName)")
}
// > 1. Anna
// > 2. Brian
// > 3. Craig
// > 4. Donna
// > 5. Eli
// > 6. Franklin

现在你可以这个刚刚学到的技术来写一个函数,带有一个整数的数组作为它的输入,返回它的元素的和:

func sumOfAllItems(intArray: [Int]) -> Int {
    var sum = 0
    for number in intArray {
        sum += number
    }
    return sum 
}

你可以用这个函数来计算选手的总分:

print(sumOfAllItems(scores))
// > 21

迷你练习

写一个 for-in 循环,打印选手的名字和分数。

顺序操作

你刚刚看到的 for-in 循环都有一个共同点:他们遍历了数组的所有元素,然后对每个项目实施了特定的行为。

Reduce

reduce(_:combine:) 带有一个初始值作为第一个参数,然后按顺序累计数组里的每个值,使用 combine 操作。一个例子胜过千言万语:

let sum = scores.reduce(0, combine: +)
print(sum)// > 21

这和写 let sum = 0 + scores[0] + scores[1] + ... +scores[5] 是相同的。它做了 sumOfAllValues(_:) 做的完全一样的事,但只有一行代码。

combine 参数是很高端的,它支持不止一个单独的算术操作。它是一个闭包(closure),你会在后面的教程里学习更多有关内容,“函数式编程。”

Filter

filter(:) 返回一个新数组,通过从它被调用的数组中筛选出元素。它唯一的参数是一个闭包,返回一个布尔型,它会为数组里的每个元素执行这个闭包一次。如果闭包返回 true,filter(:) 会添加元素到返回的数组中;否则它会忽略这个元素。

例如,在你的纸牌游戏里,你想打印这天的高分,你把这定义为大于 5 分的分数:

print(scores.filter({ $0 > 5 }))
// > [8, 6]

你可以使用简写的 $0 指向闭包里的第一个参数,这样你就能指向 filter(_:) 当前在操作的元素。

Map

map(_:) 也带有唯一的闭包参数。就像它名字表示的,它将一个数组中的每个值映射(map)到一个新值,使用闭包参数。

再一次,来个例子会更易懂。假设你感觉很慷慨,你想要把每个选手拥有的值翻倍。

print(scores)
// > [2, 2, 8, 6, 1, 2]
let newScores = scores.map({ $0 * 2 })
print(newScores)

只要一行代码,你已经创建了一个新数组,分数都被乘以 2。

注意:你会在后面的教程学习更多有关顺序操作,“函数式编程。”

数组操作的运行时间

数组在内容里存储为连续不断的块。意味着如果你在一个数组里有十个元素,十个值都被一个接一个存储。记在心中,这是多个数组操作消耗的表现:

访问元素:获取一个元素的消耗是 O(1)。因为所有值都是序列化的,做随机访问和获取位于特定索引的值很简单;编译器要知道的就是数组从哪里开始,以及你想要获取哪个索引。

插入元素:添加一个元素的复杂性基于你添加新元素的位置在哪:

  • 如果你在数组的开头添加,Swift 可以用 O(1) 完成。
  • 如果你添加到数组的中间,从这个索引开始的所有值都需要被平移。这样做会需要 N/2 操作,因此运行时间是 O(N)。
  • 如果你添加到数组的末尾并且有空间的话,它会使用 O(1)。如果没有空间,Swift 需要从其他什么地方找到空间,在添加之前复制整个数组过去,会占用 O(N)。但平均情况是 O(1),因为数组大部分情况下都不是满的。

删除元素:删除一个元素留下了被移除的元素本来所在的空当。就像之前提到的,数组里的所有元素需要被序列化这样空当才能关闭,通过向前平移元素。

复杂性和插入元素相似:如果你在移除开头或结尾的元素,这是一个 O(1) 操作。如果你在从中间移除,复杂性是 O(N)。

搜索一个元素:如果你在搜索的元素是数组的第一个元素,搜索会在一次操作后结束。如果这个元素不存在,你需要执行 N 次操作直到你意识到元素没有被找到。平均来看,搜索一个元素会用 N/2 操作,因为搜索有一个 O(N) 的复杂性。

当你在阅读接下来的关于数组和集合的教程的时候,你会看到他们的表现特征和数组有什么不同。这会给你一点灵感,对于特定情况应该用哪个集合类型。

关键点

  • 数组(Arrays)是相同类型的值的有序集合。
  • 使用下标(subscripting)来访问和更新元素。
  • 数组是一个值类型,所以被分配到一个新的变量或作为参数传递给函数的时候是被拷贝的。
  • 千万不要访问一个越界索引。

接下来去哪儿?

数组在编程里很常见,当你需要操作许多特定类型的元素的时候你会看到他们被大量使用。

在接下来的两篇教程里,你会学习字典(dictionaries)和集合(sets),另外两个 Swift 内置的集合类型。当你继续的时候,仍然把数组放在脑海里,这样就可以比较和发现差异;这会让你看到区别,帮助你判断何时使用哪个集合类型。

挑战

挑战 A:你就是编译器

以下哪个是有效语句?

1. let array1 = [Int]()
2. let array2 = []
3. let array3: [String] = []

对于接下来的5条语句,array4 被如此声明:

let array4 = [1, 2, 3]

4. print(array4[0])
5. print(array4[5])
6. array4[1...2]
7. array4[0] = 4
8. array4.append(4)

对于最后的5条语句,array5 被如此声明:

var array5 = [1, 2, 3]

9. array5[0] = array5[1]
10. array5[0..1] = [4, 5]
11. array5[0] = "Six"
12. array5 += 6
13. for item in array5 { print(item) }

挑战 B:移除一个元素

写一个函数,移除一个整数数组中给定的整数的第一个存在。这是函数的结构:

func removeOnce(itemToRemove: Int, fromArray: [Int]) -> [Int]

挑战 C:挑选

写一个函数从整数数组中移除给定的整数的所有存在。这是函数的结构:

func remove(itemToRemove: Int, fromArray: [Int]) -> [Int]

挑战 D:反转数组

数组有一个 reverse() 方法,反转数组里项目的顺序。写一个函数反转数组,但不使用 reverse()。这是函数的结构:

func reverse(array: [Int]) -> [Int]

挑战 E:随机化数组

下面的函数返回一个介于 0 和给定参数之间的随机数字:

import Foundation
func randomFromZeroTo(number: Int) -> Int {
  return Int(arc4random_uniform(UInt32(number)))
}

使用它写一个函数,把数组里的元素打乱成为随机顺序。这是函数的结构:

func randomArray(array: [Int]) -> [Int]

挑战源代码

https://yunpan.cn/cMZGvcJpQDZqD (提取码:f737)


介绍

欢迎来到Swift世界!Swift是一门苹果在2014年夏天发布的编程语言。从那之后,Swift发布了一个主要的版本跳跃,成为了开始在苹果平台:iOS,OS X,watchOS和tvOS开发的最简单的方式。

谁适合这篇教程

这篇教程适合懂一点编程、并且希望学习Swift的人。也许你已经为网站写过一些JavaScript代码,或者用Python写过一些简短的程序。这篇教程就是为你准备的!你会学习到编程的基本概念,同时也会成为Swift语言小能手。

如果你是赤裸裸的编程新手,这篇教程也是为你准备的!教程里贯穿有简短的锻炼和挑战来给你一些编程练习,同时测试你的知识。

需要准备什么

要看这篇教程,你需要准备如下的东西:

  • 一台运行OS X El Captian(10.11)的Mac,带有最新发布的更新并且安装了安全补丁。这样你才能够安装需要的开发工具:最新版本的Xcode。
  • Xcode 7.3 或更新的版本。Xcode是用Swift写代码的主要开发工具。最小也需要Xcode 7.3版本,因为那个版本包含Swift 2.2。你可以免费从Mac App Store下载Xcode的最新版本,这里:http://apple.co/1FLn51R

如果你还没有安装Xcode最新版本,在继续看下面的教程前要确定安装。

如何使用这篇教程

每篇教程都会介绍触手可及的话题理论,伴随大量Swift代码来示范在学习的实际的应用程序。

教程里的所有代码都是平台中立的;这意味着不是为iOS、OS X或任何其它平台而特定。代码在playgrounds里运行,你在本篇中已经学习了。

在剩下的教程里,你可以把代码在自己的playground里输入进去。这样你就可以和代码“玩耍”(play around),做一些改变立即就能看见代码运行的结果。

剩下的教程里会贯穿实际小练习,都是简短的练习,关于触手可及的主题。每篇的末尾也有挑战,会有编程问题也会有长一点的代码练习来测试你的知识。做完就能掌握大部分的Swift基础知识。

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

推荐阅读更多精彩内容