此篇文章整理自我以前学习Swift时的一些练习代码,其存在的意义多是可以通过看示例代码更快地回忆Swift的主要语法。
如果你想系统学习Swift或者是Swift的初学者请绕路,感谢Github上The Swift Programming Language
开源翻译的中文版,感谢极客学院wiki提供的PDF版本。
代码和PDF版本上传至Github,有兴趣的可以下载下来试试。
SwiftTour
Base
var str = "Hello, World!"
print(str)
// 变量用var声明,常量用let声明,变量类型自动推断
// 可在变量后声明类型,用冒号分隔
var myVariable = 42
let myConstant = 42
myVariable = 50
let explictDouble: Double = 60
// 值永远不会被隐式转换为其他类型,需要转换必须采用显式转换
let label = "The width is "
let width = 94
let widthLabel = label + String(width)
// 更为常见的将值转换为字符串的方法
let apples = 3
let oranges = 5
let appleSummary = "I have \(apples) apples"
let fruitSummary = "I have \(apples + oranges) pieces of fruit"
// []用来创建数组或字典
var shoppingList = ["catfish", "water", "tulips", "blue paint"]
shoppingList[1] = "bottle of water"
var occupations = [
"Malcolm": "Captain",
"Kaylee": "Mechanic"
]
occupations["Jayne"] = "Public Relations"
// 创建一个空数组或者字典
let emptyArray1 = [String]()
let emptyDictionary1 = [String: Float]()
let emptyArray2: [String] = []
let emptyDictionary2: [String: Float] = [:]
// 控制流,if和switch用于选择,for-in,for,while和repeat-while用于循环
let individualScores = [75, 43, 103, 87, 12]
var teamScore = 0
for score in individualScores {
if score > 50 {
teamScore += 3
} else {
teamScore += 1
}
}
print(teamScore)
// 由于不支持隐式转换,条件必须是一个布尔表达式
// 一般采用可选值配合if和let,处理值缺失判断
var optionalString: String? = "Hello"
print(optionalString == nil)
var optionalName: String? = "John Appleseed"
var greeting = "Hello"
if let name = optionalName {
greeting = "Hello, \(name)"
}
// Switch支持任意类型的数据以及各种比较操作
let vegetable = "red pepper"
switch vegetable {
case "celery" :
print("Add some raisins and make ants on a log")
case "cucumber", "watercress" :
print("That woould make a good tea sandwich")
case let x where x.hasSuffix("pepper") :
print("Is it a spicy \(x)?")
default :
print("Everything tastes good in soup")
}
// 使用for-in遍历字典
let interestingNumbers = [
"Prime": [2, 3, 5, 7, 11, 13],
"Fibonacci": [1, 1, 2, 3, 5, 8],
"Square": [1, 4, 9, 16, 25],
]
var largest = 0
var numberGroup: String = "Null"
for (kind, numbers) in interestingNumbers {
for number in numbers {
if number > largest {
largest = number
numberGroup = kind
}
}
}
print(numberGroup, largest)
// while循环
var n = 2
while n \< 100 {
n = n * 2
}
print(n)
var m = 2
repeat {
m = m * 2
} while m \< 100
print(m)
// 可以在循环中使用..\<来表示范围,包含上界使用...
var firstForLoop = 0
for i in 0..\<4 {
firstForLoop += i
}
print(firstForLoop)
var secondForLoop = 0
for var i = 0; i \< 4; ++i {
secondForLoop += i
}
print(secondForLoop)
函数
// 使用func来声明函数,尾置返回
func greet(name: String, day: String) -\>String {
return "Hello \(name), today is \(day)"
}
greet("Bob", day: "Tuesday")
// 使用元组来让函数返回多个值
func calculateStatistics(scores: [Int])-\>(min: Int, max: Int, sum: Int) {
var min = scores[0]
var max = scores[0]
var sum = 0
for score in scores {
if score > max {
max = score
} else if score < min {
min = score
}
sum += score
}
return (min, max, sum)
}
let statistics = calculateStatistics([5, 3, 100, 3, 9])
print(statistics.sum)
// 函数可以带有可变个数的参数,这些参数在函数内表现为数组的形式
func sumOf(numbers: Int...)-\>Int {
var sum = 0
for number in numbers {
sum += number
}
return sum
}
sumOf()
sumOf(42, 597, 12)
// 函数可以嵌套,被嵌套的函数可以访问外侧函数的变量
func returnFifteen()-\>Int {
var y = 10
func add() {
y += 5
}
add()
return y
}
print(returnFifteen())
// 函数是类型,也可以作为另外一个函数的返回值
func makeIncrementer()-\>(Int -\> Int) {
func addOne(number: Int)->Int {
return 1 + number
}
return addOne
}
var increment = makeIncrementer()
print(increment(7))
// 函数也可以当做参数传入另外一个函数
func hasAnyMatches(list: [Int], condition: Int -\> Bool)-\>Bool {
for item in list {
if condition(item) {
return true
}
}
return false
}
func lessThanTen(number: Int)-\>Bool {
return number < 10
}
var numbers = [20, 19, 7, 12]
hasAnyMatches(numbers, condition: lessThanTen)
//闭包就是嵌套的匿名函数,{ in }
let mappedNumbers = numbers.map({number in 3 \* number})
print(mappedNumbers)
let sortedNumbers = numbers.sort{$0 \< $1}
print(sortedNumbers)
类和对象
// 使用class和类名创建一个类
class Shape {
var numberOfSides = 0
func simpleDescription() ->String {
return "A Shape with \(numberOfSides) sides"
}
}
var shape = Shape()
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()
// 使用init创建构造器,使用deinit创建一个析构函数
class NameShape {
var numberOfSides: Int = 0
var name: String
init(name: String) {
self.name = name
}
func simpleDescription() ->String {
return "A Shape with \(numberOfSides) sides"
}
}
// 子类重写父类的方法需要用override标记
class Squre: NameShape {
var sideLength: Double
init(sideLength: Double, name: String){
self.sideLength = sideLength
super.init(name: name)
numberOfSides = 4
}
func area()->Double {
return sideLength * sideLength
}
override func simpleDescription() -> String {
return "A square with sides of length \(sideLength)"
}
}
let test = Squre(sideLength: 5.2, name: "My test Square")
test.area()
test.simpleDescription()
// 类的getter和setter
class EquilateralTriangle: NameShape {
var sideLength: Double = 0.0
init(sideLength: Double, name: String){
self.sideLength = sideLength
super.init(name: name)
numberOfSides = 3
}
var perimeter: Double {
get {
return 3.0 * sideLength
}
set {
sideLength = newValue / 3.0
}
}
override func simpleDescription() -> String {
return "An equilateral triangle with sides of length \(sideLength)"
}
}
var triangel = EquilateralTriangle(sideLength: 3.1, name: "a triangle")
print(triangel.perimeter)
triangel.perimeter = 9.9
print(triangel.sideLength)
// 不需要计算属性,仍然需要在设置新值之前或之后运行代码,使用willset和didset
// 确保三角形的边长综合正方形的边长相同
class TriangleAndSquare {
var triangle: EquilateralTriangle {
willSet {
square.sideLength = newValue.sideLength
}
}
var square: Squre {
willSet {
triangel.sideLength = newValue.sideLength
}
}
init(size: Double, name: String) {
square = Squre(sideLength: size, name: name)
triangle = EquilateralTriangle(sideLength: size, name: name)
}
}
var triangleAndSquare = TriangleAndSquare(size: 10, name: "Another tested Shape")
print(triangleAndSquare.square.sideLength)
print(triangleAndSquare.triangle.sideLength)
triangleAndSquare.triangle = EquilateralTriangle(sideLength: 30, name: "Other")
print(triangleAndSquare.triangle.sideLength)
print(triangleAndSquare.square.sideLength)
// 处理变量的可选值时,可以在操作前加?
let optionalSquare: Squre? = Squre(sideLength: 2.5, name: "Optional square")
let sideLength = optionalSquare?.sideLength
print(sideLength)
枚举和结构体
import Foundation
// 使用enum创建一个枚举,枚举可以包含方法
enum Rank: Int {
case Ace = 1
case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten
case Jack, Queen, King
func simpleDescription()->String {
switch self {
case .Ace:
return "Ace"
case .Jack:
return "jack"
case .Queen:
return "queen"
case .King:
return "king"
default:
return String(self.rawValue)
}
}
}
let ace = Rank.Ten
let aceRawValue = ace.rawValue
print(aceRawValue)
print(ace.simpleDescription())
// 使用init?(rawValue:)初始化构造器在原始值和枚举值之间进行转换
let c = Rank(rawValue: 34)
print(c)
if let convertedRank = Rank(rawValue: 3) {
let threeDescription = convertedRank.simpleDescription()
}
// 可以不设置原始值,但系统会内部为枚举设置枚举值
enum Suit {
case Spades, Hearts, Diamonds, Clubs
func simpleDecription()->String {
switch self {
case .Spades:
return "spades"
case .Hearts:
return "hearts"
case .Diamonds:
return "diamonds"
case .Clubs:
return "clubs"
}
}
}
let hearts = Suit.Hearts
let heartsDescription = hearts.simpleDecription()
print(heartsDescription)
// struct和class的区别是,结构体传值,类传引用
struct Card {
var rank: Rank
var suit: Suit
func simpleDescription()->String {
return "The \(rank.simpleDescription()) of \(suit.simpleDecription())"
}
}
let threeOfSpades = Card(rank: .Three, suit: .Spades)
let threeOfSpadesDescription = threeOfSpades.simpleDescription()
print(threeOfSpadesDescription)
// 枚举成员的实例值
enum ServerResponse {
case Result(String,String)
case Error(String)
}
let success = ServerResponse.Result("6:00", "9:00")
let failure = ServerResponse.Error("Out of cheese")
let success1 = ServerResponse.Result("8:00", "9:00")
switch success1 {
case let .Result(sunrise, sunset):
let serverReponse = "Sunrise is at \(sunrise) and sunset is at \(sunset)"
print(serverReponse)
case let .Error(error):
let serverReponse = "Failure... \(error)"
print(serverReponse)
}
协议
import Foundation
// 使用protocol来声明一个协议
protocol ExampleProtocol {
var simpleDescription: String { get }
mutating func adjust()
}
// 类、枚举和结构题都可以实现协议
class SimpleClass: ExampleProtocol {
var simpleDescription: String = "A very simple class"
var anotherProperty: Int = 69105
func adjust() {
simpleDescription += "Now 100% adjusted"
}
}
var a = SimpleClass()
a.adjust()
let aDescription = a.simpleDescription
print(aDescription)
// mutating关键字标记一个会修改结构体的方法
struct SimpleStructure: ExampleProtocol {
var simpleDescription: String = "A very simple class"
mutating func adjust() {
simpleDescription += " (adjusted)"
}
}
var b = SimpleStructure()
b.adjust()
let bDescription = b.simpleDescription
print(bDescription)
// 使用extension来为现有的类型添加功能
extension Int: ExampleProtocol {
var simpleDescription: String {
return "The number \(self)"
}
mutating func adjust() {
self += 42
}
}
print(7.simpleDescription)
// 可以像使用其他类型一样使用协议名
// 即使protocolValue变量运行时类型是simpleClass,编译器会把它的类型当作ExampleProtocol
// 这意味着,不能调用类在它实现协议之外的方法或属性
let protocolValue: ExampleProtocol = a
print(protocolValue.simpleDescription)
// print(protocolValue.anotherProperty) //Error
泛型
import Foundation
// 在尖括号里写一个名字来创建一个泛型函数或类型
func repeatItem<Item>(item: Item, numberOfTimes: Int)-\>[Item] {
var result = [Item]()
for _ in 0..<numberOfTimes {
result.append(item)
}
return result
}
let result = repeatItem("knock", numberOfTimes: 5)
print(result)
// 也可以穿件泛型函数、方法、类、枚举和结构体
enum OptionalValue<Wrapped> {
case None
case Some(Wrapped)
}
var possibleInteger: OptionalValue<Int> = .None
possibleInteger = .Some(3)
// 在类型名后面使用where来指定对类型的需求
// 比如,限定类型实现某一个协议,限定两个类型是相同的,或者限定某个类必须有一个特定的父类
func anyCommonElements\<T: SequenceType, U: SequenceType where T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element\>(lhs: T, \_ rhs: U)-\>Bool {
for lhsItem in lhs {
for rhsItem in rhs {
if lhsItem == rhsItem {
return true
}
}
}
return false
}
print(anyCommonElements([1, 2 ,3], [3]))
SwiftBasics
SwiftBasics是对Swift类型和变量的介绍,有过别的语言基础的扫一眼即可。需要注意的地方主要是可选类型和元组,这是Swift特有的类型。
import Foundation
// Print(\_:,separator:,terminator:)
print("Hello World", "iOS",separator: "\*", terminator: "")
// 可以通过分号,实现一行写多条独立语句
let cat = "Tom"; print("Hello \(cat)")
// 可以访问不同整数类型的min和max属性来获取对应类型的最小值和最大值
// Swift中的Int长度与当前平台有关,在32位平台Int与Int32长度相同,在64位平台Int与Int64长度相同
print(Int8.max)
print(Int8.min)
print(Int.max)
print(Int64.max)
// 整数和浮点数都可以增加额外的零并且包含下划线,并不会影响字面值
let oneMillion = 001\_000\_000
print(oneMillion)
// 数值类型的转换需要调用类型内置构造器
// 可以通过扩展现有类型的构造器,使之接受更多的类型转换
let one:UInt8 = 1
let two:Int8 = 2
let onePlusTwo = one + UInt8(two)
print(onePlusTwo)
// 可以把多个值组合成一个复合值,元组内的值可以是任意类型,并不要求是相同类型
let http404Error = (404, "Not Found")
print(http404Error)
// 可以将一个元组的内容分解成单独的常量和变量
print("The status code is \(http404Error.0)")
let (statusCode, statusMessage) = http404Error
print("The status message is \(statusMessage)")
let (justTheStatusCode, \_) = http404Error
print("The status code is \(justTheStatusCode)")
// 在元组命名时对单个元素进行命名
let http200Status = (statusCode: 200, statusMessage: "OK")
print("The status code is \(http200Status.statusCode)")
// 可选类型
// 并不是所有对字符串都能转换为整数,Int构造器可能会失败,转换结果是可选类型
let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
print(convertedNumber)
// 可以通过nil给可选类型变量赋值,nil不可以用于非可选类型
// 声明了一个可选类型变量没有赋值,系统会自动设置为nil
var serverReponseCode: Int? = 404
serverReponseCode = nil
var surveyAnswer: String?
// 可以使用相等不等符判断可选类型是否有值,当确定有值时,可以用!强制解析
if convertedNumber != nil {
print("convertedNumber contains some integer value of \(convertedNumber!)")
}
// 可选绑定可以用在if和while语句中来对可选类型的值进行判断并把值赋给一个临时常量或变量
if let actualNumber = Int(possibleNumber) {
print("\'\(possibleNumber)\' has an integer value of \(actualNumber)")
} else {
print("\'\(possibleNumber)\' could not be converted to an integer")
}
// 可以包含多个可选绑定在if语句中,并使用where子句做布尔值判断
if let firstNumber = Int("4"), secondNumber = Int("42") where firstNumber \< secondNumber {
print("\(firstNumber) < \(secondNumber)")
}
// 用!声明隐式解析可选类型
// 隐式可选类型其实就是一个普通的可选类型,但是可以被当作非可选类型来使用,并不需要每次都解析可选值
let possibleString: String? = "An optional string"
let forcedString: String = possibleString!
print(forcedString)
let assumedString: String! = "An implicitly unwrapped optional string"
let implicitString: String = assumedString
print(implicitString)
SwiftBasicOperators
SwiftBasicOperators部分是对Swift支持的运算符的介绍。Swift支持大部分标准C语言的运算符,且改进许多特性来减少常规编码错误。如赋值符(=)不返回值,算术运算符会检测并不允许溢出。区别于C语言,在Swift中,你可以对浮点数取余运算(%)。此外,Swift还提供了C语言中没有的区间运算符(a..<b和a...b)。
import Foundation
// 如果赋值的右边是一个多元组,它的元素可以马上被分解成多个常量或变量
let (x, y) = (1, 2)
print(x, y)
// 取余运算,对负数b求余时,b的符号会被忽略
print(9 % 4)
print(-9 % 4)
print(9 % -4)
// 浮点数求余
print(8 % 2.5)
// 一元负号和一元正号运算符写在操作数之前,中间没有空格
// 注意的是,一元正号不做任何改变地返回操作数的值
let minusSix = -6
print(-minusSix)
print(+minusSix)
// 空合运算符 a ?? b
// 表达式a必须是Optional类型,b的类型必须与a的存储值类型一致
// 如果a为nil,则返回b的值。如果a有值,则返回a的解析值。
// 并不会改变a和b的值
let defaultColorName = "red"
var userDefinedColorName: String?
var colorNameToUse = userDefinedColorName ?? defaultColorName
print(colorNameToUse, userDefinedColorName)
userDefinedColorName = "grren"
colorNameToUse = userDefinedColorName ?? defaultColorName
print(colorNameToUse, userDefinedColorName)
// 区间运算符
for index in 1...5 {
print("\(index) * 5 = \(index * 5)")
}
let names = ["Anna", "Alex", "Brain", "Jack"]
let count = names.count
for i in 0..\<count {
print("第\(i)个人叫\(names[i])")
}
SwiftStrings
SwiftStrings部分主要介绍Swift字符串与字符。Swift的String和Character类型提供了一种快速的兼容Unicode的方式处理文本,字符串的每个字符都是Unicode字符,支持访问Unicode的不同表示形式。
import Foundation
// 每个字符串都是由编码无关的Unicode字符组成
// 创建一个空字符串
var emptyString = ""
var anotherEmptyString = String()
// 通过isEmpty属性来判断字符串是否为空
if emptyString.isEmpty {
print("Nothing to see here")
}
// 通过for-in循环遍历字符串的characters属性来获取每个字符的值
for character in "Dog!".characters {
print(character)
}
// 字符串可以通过character字符数组初始化
let catCharacters:[Character] = ["C", "a", "t"]
let catString = String(catCharacters)
print(catString)
// 连接字符串
let string1 = "Hello "
let string2 = "there"
var welcome = string1 + string2
let exclamationMark: Character = "!"
welcome.append(exclamationMark)
print(welcome)
// \u{}表示Unicode字符,注意大括号里面的数值是十六进制
let dollarSign = "\u{24}"
let blackHeart = "\u{2665}"
let sparklingHeart = "\u{1F496}"
print(dollarSign, blackHeart, sparklingHeart)
// Swift字符Character是采用可扩展字符集,即有的字符是由多个Unicode字符组合而成
let exampleCharacter1 = "\u{1112}\u{1161}\u{11AB}"
let exampleCharacter2 = "\u{D55C}"
print(exampleCharacter1,exampleCharacter2)
// 字符串的characters.count方法计算的是字符串有多少字符组成
let unusualMenagerie = "Koala\u{1F1FA}\u{1F1F8}"
print(unusualMenagerie)
print(unusualMenagerie.characters.count)
var word = "cafe"
print("the number of characters in \(word) is \(word.characters.count)")
word += "\u{301}"
print("the number of characters in \(word) is \(word.characters.count)")
// 字符串索引,String.Index,不支持整数做索引
// predecessor()取前一个索引,successor()取后一个索引
// advancedBy()获取索引
// 越界会引发运行时候错误
let greeting = "Guten Tag!"
print(greeting[greeting.startIndex])
print(greeting[greeting.endIndex.predecessor()])
print(greeting[greeting.startIndex.successor()])
let index = greeting.startIndex.advancedBy(7)
print(greeting[index])
print(word[greeting.startIndex])
print(greeting[index.advancedBy(1)])
print(greeting[index.advancedBy(2, limit: greeting.endIndex)])
// 使用characters属性的indices属性会创建一个包含所有索引的范围
for index in greeting.characters.indices {
print("\(greeting[index])", terminator: " ")
}
// 调用insert()插入元素
// 调用insertContentsOf()插入字符串
welcome.insert("!", atIndex: welcome.endIndex)
print(welcome)
welcome.insertContentsOf("here".characters, at: welcome.endIndex.predecessor())
print(welcome)
// removeAtIndex()删除索引指定的元素
// removeRange()删除索引范围元素
welcome.removeAtIndex(welcome.endIndex.predecessor())
print(welcome)
welcome.removeRange(welcome.endIndex.advancedBy(-4)..\<welcome.endIndex)
print(welcome)
// 调用hasPrefix()判断是否包含指定前缀
// 调用hasSuffix()判断是否包含指定后缀
let prefixAndSuffix = ["abc123", "absfeh23", "23"]
var prefix = 0
var suffix = 0
for i in prefixAndSuffix {
if i.hasPrefix("ab") {
prefix++
}
if i.hasSuffix("23") {
suffix++
}
}
print(prefix, suffix)
//Unicode编码形式,UTF-8(编码字符串为8位),UTF-16,UTF-32
let dogString = "Dog!🐶"
for codeUnit in dogString.utf8 {
print("\(codeUnit)", terminator: " ")
}
print("")
for codeUnit in dogString.unicodeScalars {
print("\(codeUnit.value)", terminator: " ")
}
print("")
for codeUnit in dogString.unicodeScalars {
print("\(codeUnit)", terminator: " ")
}
print("")
print(dogString.utf8.count)
print(dogString.utf16.count)
print(dogString.unicodeScalars.count)
// 字符串比较时需注意
// 两个字符,即便构成它们的Unicode标量不同,只要它们拥有同样的语言意义和外观,就认为它们相等
let s1 = "caf\u{E9}"
let s2 = "caf\u{65}\u{301}"
print(s1, s2)
if s1 == s2 {
print("equal string")
}
SwiftCollectionTypes
SwiftCollection部分主要介绍Swift中的三种集合类型Array、Set、Dictionary,Swift中集合类型封装了很多属性和方法,语法特性都与C语言有较大的区别。
Array
import Foundation
// Swift提供Array、Set、Dictionary三种集合类型
// 创建一个空数组
// 如果创建时数组类型已知,可以直接用[]定义一个空数组
var someInts = [Int]()
someInts.append(3)
someInts = []
// 创建特定大小并且所有数据都被初始化
var threeDoubles = [Double](count: 3, repeatedValue: 0.0)
// 通过两个数组相加创建一个数组
var anotherThreeDoubles = Array(count: 3, repeatedValue: 2.5)
var sixDoubles = threeDoubles + anotherThreeDoubles
print(sixDoubles)
// 用字面值构造数组
var shoppingList1: [String] = ["Eggs", "Milk"]
var shoppingList2 = ["Eggs", "Milk"]
// count属性、isEmpty属性
// append()方法
if shoppingList1.isEmpty {
print("Array is empty")
} else {
print("not empty, count \(shoppingList1.count)")
}
shoppingList1.append("shoes")
shoppingList1 += ["water"]
print(shoppingList1)
// 允许使用整数索引
print(shoppingList1[0])
shoppingList1[0] = "six eggs"
shoppingList1[1...3] = ["Bananas", "Apples"]
print(shoppingList1)
// insert()和removeAtIndex()
shoppingList1.insert("Maple Syrup", atIndex: 0)
let mapleSyrup = shoppingList1.removeAtIndex(0)
print(mapleSyrup, shoppingList1)
let apples = shoppingList1.removeLast()
print(apples, shoppingList1)
// for-in遍历
for item in shoppingList1 {
print(item)
}
// enumerate()方法遍历,返回每个数组元素索引值和元素值组成的元组
for (index, value) in shoppingList1.enumerate() {
print("Item \(index+1): \(value)")
}
Set
// 集合(Set)存储相同类型并且没有确定顺序的值。适用于元素顺序不重要或者希望元素只出现一次
// 集合只能存储可哈希化的类型,哈希值是Int类型的,相等的对象哈希值必须相同
// 自定义的类型作为集合的值或者字典的Key,自定义的类型需符合Hashable协议,提供一个类型位Int的可读属性hashValue
// 因为Hashable协议符合Equatable协议,所以符合该协议的类型也必须提供一个是否相等(==)运算符的实现
// 创建一个空的集合
var letters = Set<Character>()
letters.insert("a")
letters = []
print(letters)
// 用数组初始化集合,需要显式声明集合的类型,元素类型可以从数组中推断
var favoriteGenres: Set = ["Rock", "Classical", "Hip hop"]
// count、isEmpty属性
if !favoriteGenres.isEmpty {
print(favoriteGenres.count)
}
// insert方法
favoriteGenres.insert("Jazz")
// remove方法
// 如果该值是集合的元素,删除值,返回删除值。如果不是集合的元素,则返回nil
if let removedGenre = favoriteGenres.remove("Rock") {
print(removedGenre)
} else {
print("nil")
}
// contains()方法检查Set中是否包含一个特定的值
if favoriteGenres.contains("Funk") {
print("I get up on the good foot")
} else {
print("It's too funky in here")
}
// for-in遍历一个Set
for genre in favoriteGenres {
print(genre)
}
// Set没有确定的顺序,但可以利用sort方法返回一个有序集合
for genre in favoriteGenres.sort() {
print(genre)
}
// Set支持高效的集合操作
let oddDigits: Set = [1, 3, 5, 7, 9]
let evenDigits: Set = [0, 2, 4, 6, 8]
let singleDigitPrimeNumbers: Set = [2, 3, 5, 7]
print(oddDigits.union(evenDigits).sort())
print(oddDigits.intersect(evenDigits).sort())
print(oddDigits.subtract(singleDigitPrimeNumbers).sort())
print(oddDigits.exclusiveOr(singleDigitPrimeNumbers).sort())
// Set关系
let houseAnimals: Set = ["🐶", "🐱"]
let farmAnimals: Set = ["🐂", "🐔", "🐑", "🐶", "🐱"]
let cityAnimals: Set = ["🐭", "🐦"]
print(houseAnimals.isSubsetOf(farmAnimals))
print(farmAnimals.isSupersetOf(houseAnimals))
print(farmAnimals.isDisjointWith(cityAnimals))
print(houseAnimals.isStrictSubsetOf(houseAnimals))
print(houseAnimals.isSubsetOf(houseAnimals))
Dictionary
// 字典是储存相同类型的容器,每个值都关联唯一的键值(key),与数组不同的是,字典的数据项目没有具体顺序
// 创建一个空字典
var namesOfIntegers = [Int: String]()
namesOfIntegers[16] = "sixteen"
namesOfIntegers = [:]
// 用字典字面值创建字典,字典类型可由字面值推断
// var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
var airports = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
// count、isEmpty属性
if !airports.isEmpty {
print(airports.count)
}
// 使用下标语法增加数据项
airports["LHR"] = "London"
airports["LHR"] = "London Heathrow"
// updateValue()方法更新Key对应的值或者创建新Key
// 返回更新之前的原值或nil
if let oldValue = airports.updateValue("Dublin Airport", forKey: "DUB") {
print(oldValue)
}
// 下标语法方式也返回可选值
if let airportName = airports["CHZ"] {
print(airportName)
} else {
print("The airport is not in the dictionary")
}
print(airports)
// 通过下标语法给某个键赋值为nil来从字典中删除一对键值
airports["APL"] = "Apple"
airports["APL"] = nil
print(airports)
// removeValueForKey()方法
// 如果删除的Key存在,删除该键值,返回删除值。如果删除的Key不存在,返回nil
if let removedValue = airports.removeValueForKey("DUB") {
print(removedValue)
}
// for-in遍历,返回(key, value)元组
for (airportCode, airportName) in airports {
print("\(airportCode): \(airportName)")
}
// 通过访问keys或者values属性,我们也可以遍历字典的键或值
for airportCode in airports.keys {
print(airportCode)
}
// 使用keys或者values属性构造一个数组
let airportCodes = [String][8]
print(airportCodes)
// 字典类型是无序集合,可以以特定顺序排序遍历字典的键或值
print(airports.keys.sort())
SwiftControlFlow
Swift提供了类似C语言的流程控制结构,包括可以多次执行任务的for和while循环,基于特定条件选择执行不同代码分支的if、guard和switch语句,还有控制流程跳转到其他代码的break和continue语句。
除了传统的for循环,Swift中还增加了for-in循环更加方便的遍历容器。switch语句也远比C语言中更加强大,如case无需写break,case可以匹配区间、元组和特定类型描述。case语句中匹配的值可以由case体内部临时变量决定,也可以由where分句描述更复杂的匹配条件。
For循环
import Foundation
// 传统for循环
for var index = 0; index \< 3; ++index {
print("index is \(index)")
}
// for-in循环
for index in 1...5 {
print("\(index) times 5 is \(index * 5)")
}
// 不需要知道区间内每一项的值,可以用下划线替代变量名
let base = 3
let power = 4
var answer = 1
for \_ in 1...power {
answer *= base
}
print(answer)
// for-in遍历数组
let names = ["Anna", "Alex", "Brain", "Jack"]
for name in names {
print("Hello \(name)!")
}
// for-in遍历字典
let numbersOfLegs = ["spider": 8, "ant": 6, "cat": 4]
for (animalName, legCount) in numbersOfLegs {
print("\(animalName)s have \(legCount) legs")
}
While循环
官方文档中,While循环用一个简单的游戏来说明,点个赞。
游戏的规则如下:
- 游戏盘面爆款25个方格,游戏目标是达到或者超过第25个方格
- 每一轮,你通过掷骰子来确定你移动的步数,移动的路线如右图所示
- 如果在某轮结束,你移动到梯子的底部,可以顺着梯子爬上去
- 如果在某轮结束,你移动到蛇的头部,你会顺着蛇的身体滑下去
//盘面用数组board表示
let finalSquare = 25
var board = [Int](count: finalSquare + 1, repeatedValue: 0)
//有蛇和梯子的位置输入前进和后退值,如3号位置的梯子会前进8,14号位置会后退10
board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
//本例骰子树并未设置随机,直到玩家位置到25号位置或超过25号位置,游戏结束
var square = 0
var diceRoll = 0
while square \< finalSquare {
// 掷骰子
if ++diceRoll == 7 { diceRoll = 1 }
// 根据点数移动
square += diceRoll
if square < board.count {
// 如果玩家还在棋盘上,顺着梯子爬上去或者顺着蛇滑下去
square += board[square]
}
}
print("Game Over!")
// repeat-while循环重写本例
square = 0
diceRoll = 0
repeat {
// 顺着梯子爬上去或者顺着蛇滑下去
square += board[square]
// 掷骰子
if ++diceRoll == 7 { diceRoll = 1 }
// 根据点数移动
square += diceRoll
} while square \< finalSquare
print("Game Over!")
条件语句
// if语句用法,与C语言相同
let temperatureInFahrenheit = 90
if temperatureInFahrenheit \<= 32 {
print("It's very cold. Consider wearing a scarf")
} else if temperatureInFahrenheit \>= 86 {
print("It's really warm. Don't forget to wear sunscreen")
}
// switch语句会尝试与若干个模式(pattern)进行匹配,匹配成功执行对应代码
// 为了匹配特定的值,Swift提供了几种更复杂的匹配模式
// case语句决定哪一条分支被执行,switch语句必须完备
// 每一个case分支必须包含至少一条语句,case语句会自动break
let someCharacter: Character = "e"
switch someCharacter {
case "a", "e", "i", "o", "u" :
print("\(someCharacter) is a vowel")
case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m", "y",
"n", "p", "q", "r", "s", "t", "v", "v", "w", "x", "z":
print("\(someCharacter) is a consonant")
default:
print("\(someCharacter) is not a vowel or a consonant")
}
// 区间匹配
let approximateCount = 62
let countedThings = "moons orbiting Saturn"
var naturalCount: String
switch approximateCount {
case 0:
naturalCount = "no"
case 1..\<5:
naturalCount = "a few"
case 5..\<12:
naturalCount = "several"
case 12..\<100:
naturalCount = "dozens of"
case 100..\<1000:
naturalCount = "hundreds of"
default:
naturalCount = "many"
}
print("There are \(naturalCount) \(countedThings)")
// 使用元组在同一个switch语句中测试多个值,元组中的元素可以是值,也可以是区间
// 可以使用下划线来匹配所有值
let somePoint = (1, 1)
switch somePoint {
case (0, 0):
print("(0, 0) is at the origin")
case (\_, 0):
print("(\(somePoint.0), 0) is on the x-axis")
case (0, \_):
print("(0, \(somePoint.1)) is on the y-axis")
case (-2...2, -2...2):
print("(\(somePoint.0), \(somePoint.1)) is inside the box")
default:
print("(\(somePoint.0), \(somePoint.1)) is outside of the box")
}
// case允许将任意匹配的值绑定在临时变量或常量上,在该case分支中可以引用该值
let anotherPoint = (2, 1)
switch anotherPoint {
case (let x, 0):
print("on the x-axis with x value of \(x)")
case (0, let y):
print("on the y-axis with y value of \(y)")
case (let x, let y):// = case let(x, y):
print("somewhere else at (\(x), \(y))")
}
// case分支可以使用where语句判断额外的条件
let yetAnotherPoint = (1, -1)
switch yetAnotherPoint {
case let(x, y) where x == y:
print("(\(x), \(y)) is on the line y=x")
case let(x, y) where x == -y:
print("(\(x), \(y)) is on the line y=-x")
case let(x, y):
print("(\(x), \(y)) is just some arbitrary point")
}
Control Transfer Statements
// continue语句立刻停止本次循环,重新开始下一次循环
let puzzleInput = "great minds think alike"
var puzzleOutput = ""
for character in puzzleInput.characters {
switch character {
case "a", "e", "i", "o", "u", " " :
continue
default:
puzzleOutput.append(character)
}
}
print(puzzleOutput)
// break语句会立刻结束整个控制流的执行
let numberSymbol: Character = "三"
var possibleIntegerValue: Int?
switch numberSymbol {
case "1", "一" :
possibleIntegerValue = 1
case "2", "二" :
possibleIntegerValue = 2
case "3", "三" :
possibleIntegerValue = 3
case "4", "四" :
possibleIntegerValue = 4
default:
break
}
if let integerValue = possibleIntegerValue {
print("The integer value of \(numberSymbol) is \(integerValue)")
} else {
print("An integer value could not be found for \(numberSymbol)")
}
// Swift中的switch不会从一个case分支执行到别的分支。如果需要执行多个分支的代码,需要使用fallthrough
let integerToDescribe = 5
var description = "The number \(integerToDescribe) is "
switch integerToDescribe {
case 2, 3, 5, 7, 11, 13, 17, 19:
description += "a primer number, and also"
fallthrough
default:
description += " an integer "
}
print(description)
// 带标签的语句
// 为了便于在嵌套控制流中,准确break或continue循环体和switch代码块
// 重写蛇与梯子的游戏代码
// 盘面用数组board表示
let finalSquare = 25
var board = [Int](count: finalSquare + 1, repeatedValue: 0)
// 有蛇和梯子的位置输入前进和后退值,如3号位置的梯子会前进8,14号位置会后退10
board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
// 本例骰子树并未设置随机,直到玩家位置到25号位置或超过25号位置,游戏结束
var square = 0
var diceRoll = 0
// 这个版本的游戏使用while循环体和switch代码块来实现,while循环体又一个标签名gameLoop
gameLoop: while square != finalSquare {
if ++diceRoll == 7 { diceRoll = 1 }
switch square + diceRoll {
case finalSquare:
break gameLoop
case let newSqure where newSqure > finalSquare:
continue gameLoop
default:
square += diceRoll
square += board[square]
}
}
print("Game Over!")
// guard不同于if语句,一个guard语句总有一个else分句
func greet(person: [String: String]) {
guard let name = person["name"] else {
return
}
print("Hello \(name)!")
guard let location = person["location"] else {
return
}
print("I hope the weather is nice in \(location)")
}
greet(["name": "John"])
greet(["name": "Jane", "location": "Cupertino"])
SwiftFunction
Swift函数语法灵活,既支持没有参数名字的C风格函数,也支持带外部参数的OC风格函数。函数可以接受不确定数量的传入参数,支持传入传出参数。函数类型可以像其他类型一般使用,可以作为函数形参,也可以作为函数返回类型。函数定义可以写在其他函数定义中,在嵌套函数中实现封装。
import Foundation
// 使用元组作为函数返回
func minMax\_(array: [Int]) -\>(min: Int, max: Int) {
var currentMin = array[0]
var currentMax = array[0]
for value in array[1..<array.count] {
if value < currentMin {
currentMin = value
} else if value > currentMax {
currentMax = value
}
}
return (currentMin, currentMax)
}
let bounds = minMax\_([8, -6, 2, 109, 3, 71])
print("min is \(bounds.0) and max is \(bounds.max)")
// 可选元组返回类型,返回的元组可能没有值
func minMax(array: [Int]) -\>(min: Int, max: Int)? {
if array.isEmpty { return nil }
var currentMin = array[0]
var currentMax = array[0]
for value in array[1..<array.count] {
if value < currentMin {
currentMin = value
} else if value > currentMax {
currentMax = value
}
}
return (currentMin, currentMax)
}
if let bounds = minMax([8, -6, 2, 109, 3, 71]) {
print("min is \(bounds.0) and max is \(bounds.max)")
}
// 参数名称。函数参数有一个内部参数名和一个外部参数名
// 外部参数名标记传递来函数调用的参数,本地参数名在实现函数的时候使用
// 本地参数名前指定外部参数名,中间以空格分隔
// 如不指定外部参数名,系统会将内部参数名作为外部参数名,第一个参数除外,第一个参数默认忽略外部参数名
// 忽略外部参数名可以通过下划线\_代替外部参数名
func someFunction(firstParameterName: Int, \_ secondParameterName: Int) {
//
}
someFunction(1, 2)
// 带有默认值的参数放在函数参数列表的最后。
// 可变参数
// 在变量类型名后面加...的方式定义一个可变参数,接受零个或多个值
// 可变参数的传入值在函数体内为此类型的一个数组
// 最多可以有一个可变参数,并且可变参数一般置于最后
func arithmericMean(numbers: Double...) -\>Double {
var total: Double = 0
for number in numbers {
total += number
}
return total / Double(numbers.count)
}
print(arithmericMean(1, 2, 3, 4, 5))
print(arithmericMean(3, 8.25, 18.75))
// 函数参数默认是常量,试图在函数体内更改参数值将会导致编译错误。
// 通过在参数名前面加关键字var来定义变量参数
// 变量参数仅仅存在于函数调用的生命周期
func alignRight (var string: String, totallLength: Int, pad: Character)-\>String {
let amountToPad = totallLength - string.characters.count
if amountToPad < 1 {
return string
}
let padString = String(pad)
for _ in 1...amountToPad {
string = padString + string
}
return string
}
let originalString = "hello"
let paddedString = alignRight(originalString, totallLength: 10, pad: "-")
print(paddedString)
// 如果想要一个函数可以修改参数的值,并且这些修改在函数调用结束之后仍然存在,需要设置为输入输出参数
// 在参数名前面加关键字inout来定义输入输出参数
// 当传入的参数作为输入输出参数时,需要在参数前加&符号,表示这个值可以被函数修改
// 用inout标记的参数不能再被var或者let标记
func swapTwoInts (inout a: Int, inout b: Int) {
let temp = a
a = b
b = temp
}
var someInt = 3
var anotherInt = 107
print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
swapTwoInts(&someInt, b: &anotherInt)
print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
// 函数类型由参数类型和返回类型组成
// 没有参数并返回void类型的函数的函数类型是:()-\>void
// 下面函数的函数类型:(Int, Int)-\>Int
func addTwoInts(a: Int, \_ b: Int)-\>Int {
return a + b
}
func mutiplyTwoInts(a: Int, \_ b: Int)-\>Int {
return a * b
}
// 可以像使用其他类型一样使用函数类型
var mathFunction: (Int, Int)-\>Int = addTwoInts
print(mathFunction(2, 3))
mathFunction = mutiplyTwoInts
print(mathFunction(2, 3))
// 变量类型同样可以推断
let anotherMathFunction = mutiplyTwoInts
print(mathFunction(3, 3))
// 函数类型可以作为参数类型
func printMathResult(mathFunction: (Int, Int)-\>Int, \_ a: Int, \_ b: Int) {
print(mathFunction(a, b))
}
printMathResult(addTwoInts, 2, 4)
// 函数作为返回类型
func choseMathFunction(mathtype: String)-\>((Int, Int)-\>Int)? {
switch mathtype {
case "add" :
return addTwoInts
case "mutiply" :
return mutiplyTwoInts
default:
return nil
}
}
if let function = choseMathFunction("add") {
print(function(3, 5))
}
// 嵌套函数
// 嵌套函数对外界不可见,但是可以被封闭它的函数来调用。一个封闭函数也可以返回某个嵌套函数,使得这个函数可以再其他域中被使用
func choseMathFunction2(mathtype: String)-\>((Int, Int)-\>Int)? {
func divideTwoInts(a: Int, _ b: Int)->Int {
return a / b
}
switch mathtype {
case "add" :
return addTwoInts
case "mutiply" :
return mutiplyTwoInts
case "divide" :
return divideTwoInts
default:
return nil
}
}
if let function = choseMathFunction2("divide") {
print(function(6, 3))
}
SwiftClosure
闭包部分主要介绍了Swift中闭包表达式的基本语法,闭包的值捕获、非逃逸闭包@noescape
和自动闭包@autoclosure
值得注意一下,这些都是不太容易理解的地方。
import Foundation
// 闭包是函数代码块
// 闭包可以捕获或存储其所在上下文中任一常量和变量的引用,闭合并包裹
// 全局函数是一个有名字但不回捕获任何值的闭包
// 嵌套函数是一个有名字并可以捕获其封闭函数域内值的闭包
// 闭包表达式是一个利用轻量语法所写的可以捕获上下文中变量或常量的匿名闭包
// sort()方法需要传入两个参数
// 一个参数是数组,另外一个参数是一个闭包,负责排序判定,类型为:(String, String)-\>Bool
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
// 闭包函数方式
func backwards(s1: String, s2: String)-\>Bool {
return s1 > s2
}
var reversed1 = names.sort(backwards)
print(names, names.sort(), reversed1)
// 闭包表达式方式
let reversed2 = names.sort({(s1: String, s2: String)-\>Bool in return s1 \> s2})
print(reversed2)
// 根据上下文推断类型
// 参数类型由names数组元素推断,返回类型由返回值推断
// 大部分使用闭包作为函数参数的情况都可以推断闭包的参数和返回类型
let reversed3 = names.sort({s1, s2 in return s1 \> s2})
print(reversed3)
// 单行表达式闭包可以隐藏return语句
let reversed4 = names.sort({s1, s2 in s1 \> s2})
print(reversed4)
// 参数名称缩写
let reversed5 = names.sort({$0 \> $1})
print(reversed5)
// 运算符函数,前提是排序元素(String)实现了运算符\>
let reversed6 = names.sort(\>)
print(reversed6)
// 尾随闭包
// 如果需要将一个很长的闭包表达式作为最后一个参数传递给函数时,可采用尾随闭包增加函数可读性
let reversed7 = names.sort(){$0 \> $1}
print(reversed7)
// 例子:Array类型的map方法调用一个闭包生成一个字典
let digitNames = [
0: "Zero", 1: "One", 2: "Two", 3: "Three", 4: "Four",
5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"
]
let numbers = [16, 58, 510]
let strings = numbers.map(){
(var number) -> String in
var output = ""
while number > 0 {
output = digitNames[number % 10]! + output
number /= 10
}
return output
}
print(strings)
// 闭包可以在其定义的上下文捕获常量或变量
// 最简单的闭包形式是嵌套函数,嵌套函数可以捕获其外部函数所有的参数以及定义的常量或变量
// 系统会拷贝捕获的参数,即便在原函数域外也可以使用,参数属于闭包
// 闭包的传递是引用类型,对应唯一的一份闭包
func makeIncrementor(forIncrement amount: Int) -\> ()-\>Int {
var runningTotal = 0
func incrementor() ->Int {
runningTotal += amount
return runningTotal
}
return incrementor
}
let incrementByTen = makeIncrementor(forIncrement: 10)
print(incrementByTen())
print(incrementByTen())
let incrementBySeven = makeIncrementor(forIncrement: 7)
print(incrementBySeven())
print(incrementByTen())
let alsoIncrementByTen = incrementByTen
print(alsoIncrementByTen())
// 非逃逸闭包,只在函数体内执行,函数返回之后不执行
// 闭包作为函数的参数,用@noescape标记表示该闭包为非逃逸闭包
var Header: ()-\>Void
func someFunctionWithNoEscape(@noescape closure: ()-\>Void)-\>()-\>Void {
closure()
// Header = closure 由于标记为非逃逸,不允许将闭包传出
// return closure
return {print("a closure")}
}
// 闭包能实现懒求值,直到闭包调用时才执行操作
// 采用自动闭包的函数调用时,函数参数可以接受一个闭包返回类型的语句,相当于省略{}
// @autoclosure 特性暗含了 @noescape 特性
var string = "Hello"
var closureContainer: ()-\>String = {"World"}
func someFunctionWithClosure(closure: ()-\>String) {
print(closure())
}
someFunctionWithClosure(){string}
func someFunctionWithAutoClosure(@autoclosure closure: ()-\>String) {
print(closure())
// closureContainer = closure 标记为自动闭包,不允许将闭包传出
}
someFunctionWithAutoClosure(string)
// 需要逃逸的自动闭包可以使用@autoclosure(escaping)
func anotherFunctionWithAutoClosure(@autoclosure(escaping) closure: ()-\>String) {
print(closure())
closureContainer = closure // 允许闭包逃逸
}
print(closureContainer())
anotherFunctionWithAutoClosure(string)
print(closureContainer()) // 闭包逃逸,在函数体外执行