总的来说,语法有java的味道,也有python的味道,还有swift自己的味道。
有些语法还是挺不伦不类的,不太好理解,即使你有几年的java或python经验,也不见得有些语法你能很轻松的看明白。
有些语法特性很好,很个性,但有些语法个人感觉乱,特性多,注意点多,还不太好理解。
慢慢学习吧。。。
=================================================================
值类型
Int
Double
Bool
String
用双引号""表示
Character
用双引号""表示
Array
Dictionaries
Struct
Enum
引用类型
函数类型(详情请继续往下看)
闭包函数表达式也是引用类型
类类型
=================================================================
类型转换
Int()
Double()
Bool()
String()
=================================================================
比较字符串是否相等直接用==号
=================================================================
nil只能赋值于可选类型
可选类型?
var age:String? = "21"
可选可以通过if语句来判断是否有值 if age {} else {}
强制解析!
println(age!)
可选绑定
if var ageInt = age.toInt() {}
隐式解析可选类型,不用强制解析,在第一次赋值后,确保永远有值,不用再每次解析是否有值,
一个隐式解析可选类型其实就是一个普通的可选类型,但是可以被当做非可选类型来使用,并不需要每次都使用解析来获取可选值。
主要被用在 Swift 中类的构造过程中
let age:String! = "21"
println(age)
=================================================================
Swift 的赋值操作并不返回任何值,所以if x = y编辑不通过
浮点数求余计算
Swift 也提供恒等===和不恒等!==这两个比较符来判断两个对象是否引用同一个对象实例
闭区间运算符(a...b)定义一个包含从a到b(包括a和b)的所有值的区间
半闭区间(a..b)定义一个从a到b但不包括b的区间
=================================================================
数组
var shoppingList = ["Eggs", "Milk"]
var shoppingList: [String] = ["Eggs", "Milk"]
shoppingList.isEmpty
shoppingList.append("Flour")
shoppingList += "Baking Powder"
shoppingList[4...6] = ["Bananas", "Apples"]
shoppingList.insert("Maple Syrup", atIndex: 0)
shoppingList.removeAtIndex(0)
shoppingList.removeLast()
for item in shoppingList {}
for (index, value) in enumerate(shoppingList) {}
var someInts = [Int]()
someInts = []
var threeDoubles = [Double](count: 3, repeatedValue:0.0)
=================================================================
字典
var airports: Dictionary[String, String] = ["TYO": "Tokyo", "DUB": "Dublin"]
var airports = ["TYO": "Tokyo", "DUB": "Dublin"]
airports.count
airports.updateValue("Dublin Internation", forKey: "DUB")
airports.removeValueForKey("DUB")
airports["APL"] = nil #我们还可以使用下标语法来通过给某个键的对应值赋值为nil来从字典里移除一个键值对
for (airportCode, airportName) in airports {}
for airportCode in airports.keys {}
for airportName in airports.values {}
let airportCodes = [String](airports.keys)
let airportNames = [String](airports.values)
var namesOfIntegers = [Int: String]()
namesOfIntegers = [:]
=================================================================
循环
for index in 1...5 {} #闭区间
for _ in 1..<5 {} #开区间
loop:for var index = 0; index < 3; ++index {}
loop: while condition {
do statements
}
do {
statements
} while condition
=================================================================
分支条件判断
switch value {
#No Implicit Fallthrough
#number
case 1, 2, 3:
do...
case 4...6:
do...
#tuple
case (a, b) where a == b:
do...
case (_, b):
do...
case (a, _):
do...
fallthrough #这个关键字基本用不到
case (1...3, 4...6):
do...
default:
do...
}
=================================================================
函数
函数参数默认是常量。
func halfOpenRangeLength(start: Int, end: Int) -> Int {
return end - start
}
func sayGoodbye(personName: String) {
println("Goodbye, \(personName)!")
}
func count(string: String) -> (vowels: Int, consonants: Int, others: Int) {
return (1, 2, 3)
}
以上3函数,都定义了局部参数名(local parameter name),它们只能在函数体中使用
func join(string s1: String, toString s2: String, withJoiner joiner: String) -> String {
return s1 + joiner + s2
}
以上1函数,都定义了外部参数名,如果你提供了外部参数名,那么函数在被调用时,必须使用外部参数名
func containsCharacter(#string: String, #characterToFind: Character) -> Bool {
for character in string {
if character == characterToFind {
return true
}
}
return false
}
以上1函数,只写一次参数名,并用井号(#)作为前缀就可以了。这告诉 Swift 使用这个参数名作为局部和外部参数
func join(string s1: String, toString s2: String, withJoiner joiner: String = " ") -> String {
return s1 + joiner + s2
}
以上1函数,你可以在函数体中为每个参数定义默认值。当默认值被定义后,调用这个函数时可以忽略这个参数。
将带有默认值的参数放在函数参数列表的最后。这样可以保证在函数调用时,非默认参数的顺序是一致的,同时使得相同的函数在不同情况下调用时显得更为清晰。
func join(s1: String, s2: String, joiner: String = " ") -> String {
return s1 + joiner + s2
}
以上1函数,为了使定义外部参数名更加简单,当你未给带默认值的参数提供外部参数名时,Swift 会自动提供外部名字。
注意: 你可以使用下划线(_)作为默认值参数的外部参数名,这样可以在调用时不用提供外部参数名。但是给带默认值的参数命名总是更加合适的。
func arithmeticMean(numbers: Double...) -> Double {
var total: Double = 0
for number in numbers {
total += number
}
return total / Double(numbers.count)
}
以上1函数,定义了可变参数,一个函数至多能有一个可变参数,而且它必须是参数表中最后的一个。
func alignRight(var string: String, count: Int, pad: Character) -> String {
let amountToPad = count - countElements(string)
for _ in 1...amountToPad {
string = pad + string
}
return string
}
以上1函数,定义了变量参数。
注意: 对变量参数所进行的修改在函数调用结束后便消失了,并且对于函数体外是不可见的。变量参数仅仅存在于函数调用的生命周期中。
func swapTwoInts(inout a: Int, inout b: Int) {
let temporaryA = a
a = b
b = temporaryA
}
var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
以上1函数,定义了输入输出参数,就好像C语言里的传地址。
swapTwoInts 函数,有两个分别叫做 a 和 b 的输入输出参数。
一个输入输出参数有传入函数的值,这个值被函数修改,然后被传出函数,替换原来的值。
注意: 输入输出参数不能有默认值,而且可变参数不能用 inout 标记。如果你用 inout 标记一个参数,这个参数不能被 var 或者 let 标记。
=================================================================
函数类型
每个函数都有种特定的函数类型,由函数的参数类型和返回类型组成。如下
(Int, Int) -> Int
() -> ()
func addTwoInts(a: Int, b: Int) -> Int {
return a + b
}
var mathFunction: (Int, Int) -> Int = addTwoInts
let mathFunction = addTwoInts
以上语句,定义一个叫做 mathFunction 的变量,
类型是‘一个有两个 Int 型的参数并返回一个 Int 型的值的函数’,并让这个新变量指向 addTwoInts 函数
就像其他类型一样,当赋值一个函数给常量或变量时,你可以让 Swift 来推断其函数类型:
函数类型作为参数类
func printMathResult(mathFunction: (Int, Int) -> Int, a: Int, b: Int) {
println("Result: \(mathFunction(a, b))")
}
printMathResult(addTwoInts, 3, 5)
以上语句,你可以用(Int, Int) -> Int这样的函数类型作为另一个函数的参数类型。这样你可以将函数的一部分实现交由给函数的调用者。
函数类型作为返回类型
func stepForward(input: Int) -> Int {
return input + 1
}
func stepBackward(input: Int) -> Int {
return input - 1
}
func chooseStepFunction(backwards: Bool) -> (Int) -> Int {
return backwards ? stepBackward : stepForward
}
以上定义的全部是全局函数
嵌套函数,如下
func chooseStepFunction(backwards: Bool) -> (Int) -> Int {
func stepForward(input: Int) -> Int { return input + 1 }
func stepBackward(input: Int) -> Int { return input - 1 }
return backwards ? stepBackward : stepForward
}
默认情况下,嵌套函数是对外界不可见的,但是可以被他们封闭函数(enclosing function)来调用。
一个封闭函数也可以返回它的某一个嵌套函数,使得这个函数可以在其他域中被使用。
=================================================================
闭包(Closures)
闭包可以捕获和存储其所在上下文中任意常量和变量的引用。
这就是所谓的闭合并包裹着这些常量和变量,俗称闭包。
Swift 会为您管理在捕获过程中涉及到的所有内存操作。
闭包采取如下三种形式之一:
全局函数是一个有名字但不会捕获任何值的闭包
嵌套函数是一个有名字并可以捕获其封闭函数域内值的闭包
闭包表达式是一个利用轻量级语法所写的可以捕获其上下文中变量或常量值的匿名闭
Swift 的闭包表达式拥有简洁的风格,并鼓励在常见场景中进行语法优化
reversed = sort(names, { (s1: String, s2: String) -> Bool in
return s1 > s2
})
以上代码,闭包表达式由{}包裹,函数参数与函数体由关键字in分隔。
reversed = sort(names, { s1, s2 in return s1 > s2 } )
根据上下文推断类型
以上代码,由swift根据sort函数第二个参数已定义好的参数类型与返回值类型,自动推断闭包表达式的参数类型和返回值类型
reversed = sort(names, { s1, s2 in s1 > s2 } )
单表达式闭包隐式返回
以上代码,单行表达式闭包可以通过隐藏return关键字来隐式返回单行表达式的结果
reversed = sort(names, { $0 > $1 } )
参数名称缩写
以上代码,Swift 自动为内联函数提供了参数名称缩写功能,您可以直接通过$0,$1,$2来顺序调用闭包的参数。
in关键字也同样可以被省略,因为此时闭包表达式完全由闭包函数体构成
reversed = sort(names, >)
运算符函数
以上代码,Swift 的String类型定义了关于大于号 (>) 的字符串实现,其作为一个函数接受两个String类型的参数并返回Bool类型的值。
因此,您可以简单地传递一个大于号,Swift可以自动推断出您想使用大于号的字符串函数实现
func someFunctionThatTakesAClosure(closure: () -> ()) {
// 函数体部分
}
// 以下是不使用尾随闭包进行函数调用
someFunctionThatTakesAClosure({
// 闭包主体部分
})
// 以下是使用尾随闭包进行函数调用
someFunctionThatTakesAClosure() {
// 闭包主体部分
}
// 以下是使用尾随闭包进行函数调用
someFunctionThatTakesAClosure {
// 闭包主体部分
}
reversed = sort(names) { $0 > $1 }
以上代码,定义了尾随闭包。
当闭包非常长以至于不能在一行中进行书写时,尾随闭包变得非常有用。
如果函数只需要闭包表达式一个参数,当您使用尾随闭包时,您甚至可以把()省略掉。
注意,尾随闭包表达式只能作为最后一个参数传递给函数。
举例来说,Swift 的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
}
// strings 常量被推断为字符串类型数组,即 String[]
// 其值为 ["OneSix", "FiveEight", "FiveOneZero"]
字典digitNames下标后跟着一个叹号 (!),因为字典下标返回一个可选值 (optional value),表明即使该 key 不存在也不会查找失败。
func makeIncrementor(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementor() -> Int {
runningTotal += amount
return runningTotal
}
return incrementor
}
let incrementByTen = makeIncrementor(forIncrement: 10)
incrementByTen()
// 返回的值为10
let incrementBySeven = makeIncrementor(forIncrement: 7)
incrementBySeven()
// 返回的值为7
incrementByTen()
// 返回的值为20
以上代码,定义了一个捕获值的例子。
=================================================================
枚举
enum CompassPoint {
case North
case South
case East
case West
}
以上代码,North,South,East和West不是隐式的等于0,1,2和3。
enum Planet {
case Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune
}
以上代码,多个成员值可以出现在同一行上,用逗号隔开
给枚举类型起一个单数名字而不是复数名字,以便于读起来更加容易理解
var directionToHead = CompassPoint.West
directionToHead = .East
以上代码,一旦directionToHead被声明为一个CompassPoint,你可以使用更短的点(.)语法将其设置为另一个CompassPoint的值
directionToHead = .South
switch directionToHead {
case .North:
println("Lots of planets have a north")
case .South:
println("Watch out for penguins")
case .East:
println("Where the sun rises")
case .West:
println("Where the skies are blue")
}
// 输出 "Watch out for penguins”
以上代码,以匹配单个枚举值和switch语句,switch语句必须全面
let somePlanet = Planet.Earth
switch somePlanet {
case .Earth:
println("Mostly harmless")
default:
println("Not a safe place for humans")
}
// 输出 "Mostly harmless”
以上代码,你可以提供一个默认default分支来涵盖所有未明确被提出的任何成员
enum Barcode {
case UPCA(Int, Int, Int)
case QRCode(String)
}
以上代码,你可以定义 Swift 的枚举存储任何类型的“相关值”,如果需要的话,每个成员的数据类型可以是各不相同的
enum ASCIIControlCharacter: Character {
case Tab = "\t"
case LineFeed = "\n"
case CarriageReturn = "\r"
}
enum Planet: Int {
case Mercury = 1, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune
}
以上代码,作为相关值的替代,枚举成员可以被默认值(称为原始值)预先填充,其中这些原始值具有相同的类型。
原始值可以是字符串,字符,或者任何整型值或浮点型值。每个原始值在它的枚举声明中必须是唯一的。
当整型值被用于原始值,如果其他枚举成员没有值时,它们会自动递增。
let earthsOrder = Planet.Earth.toRaw()
使用枚举成员的toRaw方法可以访问该枚举成员的原始值
=================================================================
类和结构体
struct Resolution {
var width = 0
var heigth = 0
}
class VideoMode {
var resolution = Resolution()
var interlaced = false
var frameRate = 0.0
var name: String?
}
let someResolution = Resolution()
let someVideoMode = VideoMode()
可以使用点语法为访问属性值或为属性赋值
let vga = resolution(width:640, heigth: 480)
所有结构体都有一个自动生成的成员逐一构造器,用于初始化新结构体实例中成员的属性。
与结构体不同,类实例没有默认的成员逐一构造器。
用===和!==来判断两个引用是否指向同一个实例
用===和!==来判断两个数组是否共用相同元素
当操作数组内容时,数组(Array)能提供接近C语言的的性能,并且拷贝行为只有在必要时(长度有可能发生变化时)才会发生。
我们通过调用数组的copy方法进行强制显式复制。
如果一个数组被多个变量引用,在其中的一个变量上调用unshare方法,则会拷贝此数组,此时这个变量将会有属于它自己的独立数组拷贝。
如果字典实例中所储存的键(keys)和/或值(values)是值类型(结构体或枚举),当赋值或调用发生时,它们都会被拷贝。
相反,如果键(keys)和/或值(values)是引用类型,被拷贝的将会是引用,而不是被它们引用的类实例或函数。
字典的键和值的拷贝行为与结构体所储存的属性的拷贝行为相同。
=================================================================
属性
存储属性
计算属性
属性观察器
存储属性存储常量或变量作为实例的一部分,计算属性计算(而不是存储)一个值。
计算属性可以用于类、结构体和枚举里,存储属性只能用于类和结构体。
当值类型的实例被声明为常量的时候,它的所有属性也就成了常量。
当把一个引用类型的实例赋给一个常量后,仍然可以修改实例的变量属性。
延迟存储属性是指当第一次被调用的时候才会计算其初始值的属性。在属性声明前使用@lazy来标示一个延迟存储属性。
必须将延迟存储属性声明成变量(使用var关键字),因为属性的值在实例构造完成之前可能无法得到。
而常量属性在构造过程完成之前必须要有初始值,因此无法声明成延迟属性。
计算属性不直接存储值,而是提供一个 getter 来获取值,一个可选的 setter 来间接设置其他属性或变量的值。
如果计算属性的 setter 没有定义表示新值的参数名,则可以使用默认名称newValue。
必须使用var关键字定义计算属性,包括只读计算属性,因为它们的值不是固定的。
可以为除了延迟存储属性之外的其他存储属性添加属性观察器
willSet在设置新的值之前调用,在willSet的实现代码中可以为这个参数指定一个名称,如果不指定则参数仍然可用,这时使用默认名称newValue表示。
didSet在新的值被设置之后立即调用,didSet观察器会将旧的属性值作为参数传入,可以为该参数命名或者使用默认参数名oldValue。
注意:
willSet和didSet观察器在属性初始化过程中不会被调用,它们只会当属性的值在初始化之外的地方被设置时被调用。
使用关键字static来定义值类型的类型属性,关键字class来为类(class)定义类型属性。
举个全面的例子:
class AlternativeRect {
static var birthday = "2014.01.01"
var name = "A"
var age = 18
@lazy var gender = "F"
var id: String {
get {
return name + "-" + age
}
set {
self.id = newValue
}
willSet {
printf(\(newValue))
}
didSet {
printf(\(oldValue))
}
}
}
=================================================================
方法
实例方法
类型方法
Swift 默认仅给方法的第一个参数名称一个局部参数名称;默认同时给第二个和后续的参数名称局部参数名称和外部参数名称。
同样,可以用#提供一致的内外参数名,可以用_不提供外部参数名。
结构体和枚举是值类型。一般情况下,值类型的属性不能在它的实例方法中被修改。
你可以选择变异(mutating)这个方法,然后方法就可以从方法内部改变它的属性;
struct Point {
var x = 0.0, y = 0.0
mutating func moveByX(deltaX: Double, y deltaY: Double) {
x += deltaX
y += deltaY
}
}
class SomeClass {
class func someTypeMethod() {
// type method implementation goes here
}
}
=================================================================
下标脚本
对于同一个目标可以定义多个下标脚本,通过索引值类型的不同来进行重载,而且索引值的个数可以是多个。
下标脚本允许你通过在实例后面的方括号中传入一个或者多个的索引值来对实例进行访问和赋值。
定义下标脚本使用subscript关键字,显式声明入参(一个或多个)和返回类型。
newValue的类型必须和下标脚本定义的返回类型相同。
struct Matrix {
let rows: Int, columns: Int
var grid: Double[]
init(rows: Int, columns: Int) {
self.rows = rows
self.columns = columns
grid = Array(count: rows * columns, repeatedValue: 0.0)
}
func indexIsValidForRow(row: Int, column: Int) -> Bool {
return row >= 0 && row < rows && column >= 0 && column < columns
}
subscript(row: Int, column: Int) -> Double {
get {
assert(indexIsValidForRow(row, column: column), "Index out of range")
return grid[(row * columns) + column]
}
set {
assert(indexIsValidForRow(row, column: column), "Index out of range")
grid[(row * columns) + column] = newValue
}
}
}
=================================================================
继承
在 Swift 中,类可以调用和访问超类的方法,属性和下标脚本(subscripts),
并且可以重写(override)这些方法,属性和下标脚本来优化或修改它们的行为。
class P {
var numberOfWheels = 0
init(){
numberOfWheels = 1
}
func say(){println("ppppp")}
}
class S : P {
override var numberOfWheels: Int {
get {
return super.numberOfWheels
}
set {
super.numberOfWheels = min(newValue, 8)
}
}
init(){
super.init()
numberOfWheels = 2
}
override func say(){println("sssss")}
}
你不可以为继承来的常量存储型属性或继承来的只读计算型属性添加属性观察器。
此外还要注意,你不可以同时提供重写的 setter 和重写的属性观察器。
=================================================================
构造过程
类和结构体在实例创建时,必须为所有存储型属性设置合适的初始值。
当你为存储型属性设置默认值或者在构造器中为其赋值时,它们的值是被直接设置的,不会触发任何属性观测器(property observers)。
构造器以关键字init命名。
构造器并不像函数和方法那样在括号前有一个可辨别的名字。所以在调用构造器时,主要通过构造器中的参数名和类型来确定需要调用的构造器。
Swift 会为每个构造器的参数自动生成一个跟内部名字相同的外部名。
可选类型的属性将自动初始化为空nil,表示这个属性是故意在初始化时设置为空的。
只要在构造过程结束前常量的值能确定,你可以在构造过程中的任意时间点修改常量属性的值。
对某个类实例来说,它的常量属性只能在定义它的类的构造过程中修改;不能在子类中修改。
Swift 将为所有属性已提供默认值的且自身没有定义任何构造器的结构体或基类,提供一个默认的构造器。
如果结构体对所有存储型属性提供了默认值且自身没有提供定制的构造器,它们能自动获得一个逐一成员构造器。
构造器可以通过调用其它构造器来完成实例的部分构造过程。这一过程称为构造器代理,它能减少多个构造器间的代码重复。
值类型(结构体和枚举类型)不支持继承,所以构造器代理的过程相对简单,因为它们只能代理给本身提供的其它构造器。
对于值类型,你可以使用self.init在自定义的构造器中引用其它的属于相同值类型的构造器。并且你只能在构造器内部调用self.init。
注意,如果你为某个值类型定义了一个定制的构造器,你将无法访问到默认构造器(如果是结构体,则无法访问逐一对象构造器)。
类里面的所有存储型属性--包括所有继承自父类的属性--都必须在构造过程中设置初始值。
指定构造器和便利构造器。
指定构造器是类中最主要的构造器。一个指定构造器将初始化类中提供的所有属性,并根据父类链往上调用父类的构造器来实现父类的初始化。
便利构造器是类中比较次要的、辅助型的构造器。你可以定义便利构造器来调用同一个类中的指定构造器,并为其参数提供默认值。你也可以定义便利构造器来创建一个特殊用途或特定输入的实例。
构造器链
为了简化指定构造器和便利构造器之间的调用关系,Swift 采用以下三条规则来限制构造器之间的代理调用:
规则 1
指定构造器必须调用其直接父类的的指定构造器。
规则 2
便利构造器必须调用同一类中定义的其它构造器。
规则 3
便利构造器必须最终以调用一个指定构造器结束。
一个更方便记忆的方法是:
指定构造器必须总是向上代理
便利构造器必须总是横向代理
=================================================================
析构过程
通常当你的实例被释放时不需要手动地去清理。但是,当使用自己的资源时,你可能需要进行一些额外的清理。
例如,如果创建了一个自定义的类来打开一个文件,并写入一些数据,你可能需要在类实例被释放之前关闭该文件。
class P {
deinit {
// 执行析构过程
}
}
=================================================================
自动引用计数
引用计数仅仅应用于类的实例。结构体和枚举类型是值类型,不是引用类型,也不是通过引用的方式存储和传递。
只要强引用还在,实例是不允许被销毁的。
在两个类实例互相保持对方的强引用,并让对方不被销毁。这就是所谓的循环强引用。
你可以通过定义类之间的关系为弱引用或者无主引用,以此替代强引用,从而解决循环强引用的问题。
Swift 提供了两种办法用来解决你在使用类的属性时所遇到的循环强引用问题:弱引用(weak reference)和无主引用(unowned reference)。
在实例的生命周期中,如果某些时候引用没有值,那么弱引用可以阻止循环强引用。如果引用总是有值,则可以使用无主引用。
因为弱引用可以没有值,你必须将每一个弱引用声明为可选类型。
弱引用必须被声明为变量,表明其值能在运行时被修改。弱引用不能被声明为常量。
声明属性或者变量时,在前面加上weak关键字表明这是一个弱引用。
和弱引用不同的是,无主引用是永远有值的。因此,无主引用总是被定义为非可选类型(non-optional type)。
你可以在声明属性或者变量时,在前面加上关键字unowned表示这是一个无主引用。
Person和Apartment的例子展示了两个属性的值都允许为nil,并会潜在的产生循环强引用。这种场景最适合用弱引用来解决。
Customer和CreditCard的例子展示了一个属性的值允许为nil,而另一个属性的值不允许为nil,并会潜在的产生循环强引用。这种场景最适合通过无主引用来解决。
然而,存在着第三种场景,在这种场景中,两个属性都必须有值,并且初始化完成后不能为nil。在这种场景中,需要一个类使用无主属性,而另外一个类使用隐式解析可选属性。
当闭包和捕获的实例总是互相引用时并且总是同时销毁时,将闭包内的捕获定义为无主引用。
相反的,当捕获引用有时可能会是nil时,将闭包内的捕获定义为弱引用。
=================================================================
可先链
如果可选的目标有值,那么调用就会成功;相反,如果选择的目标为空(nil),则这种调用将返回空(nil)。
多次请求或调用可以被链接在一起形成一个链,如果任何一个节点为空(nil)将导致整个链失效。
通过在想调用的属性、方法、或下标脚本的可选值(optional value)(非空)后面放一个问号,可以定义一个可选链。
这一点很像在可选值后面放一个叹号来强制拆得其封包内的值。
它们的主要的区别在于当可选值为空时可选链即刻失败,然而一般的强制解析将会引发运行时错误。
调用可选链的返回结果与原本的返回结果具有相同的类型,但是原本的返回结果被包装成了一个可选值,
当可选链调用成功时,一个应该返回Int的属性将会返回Int?
class Person {
var residence: Residence?
}
class Residence {
var numberOfRooms = 1
}
let john = Person()
let roomCount = john.residence!.numberOfRooms //将导致运行时错误
可选链提供了一种另一种获得numberOfRooms的方法。利用可选链,使用问号来代替原来!的位置:
if let roomCount = john.residence?.numberOfRooms {
println("John‘s residence has \(roomCount) room(s).")
} else {
println("Unable to retrieve the number of rooms.")
}
// 打印 "Unable to retrieve the number of rooms.
=================================================================
类型转换
类型转换在 Swift 中使用is 和 as操作符实现。当你不确定转型可以成功时,用类型转换的可选形式(as?)。
你也可以用来检查一个类是否实现了某个协议。
向下转换,向上转换
Swift为不确定类型提供了两种特殊类型别名:
AnyObject可以代表任何class类型的实例。
Any可以表示任何类型,除了方法类型(function types)。
let someObjects: AnyObject[] = [AnyObject]()
for movie in someObjects as Movie[] {}
var things = [Any]()
=================================================================
嵌套类型
要在一个类型中嵌套另一个类型,将需要嵌套的类型的定义写在被嵌套类型的区域{}内,而且可以根据需要定义多级嵌套。
Swift允许你定义嵌套类型,可以在枚举类型、类和结构体中定义支持嵌套的类型。
说简单点,就是内部类。
=================================================================
扩展
扩展就是向一个已有的类、结构体或枚举类型添加新功能(functionality)。
这包括在没有权限获取原始源代码的情况下扩展类型的能力(即逆向建模)。
Swift 中的扩展可以:
添加计算型属性和计算静态属性
定义实例方法和类型方法
提供新的构造器
定义下标
定义和使用新的嵌套类型
使一个已有类型符合某个协议
extension SomeType {
// 加到SomeType的新功能写到这里
}
extension SomeType: SomeProtocol, AnotherProctocol {
// 协议实现写到这里
}
extension Double {
var km: Double { return self * 1_000.0 }
var m : Double { return self }
var cm: Double { return self / 100.0 }
var mm: Double { return self / 1_000.0 }
var ft: Double { return self / 3.28084 }
}
=================================================================
协议
协议(Protocol)用于定义完成某项任务或功能所必须的方法和属性,
协议实际上并不提供这些功能或任务的具体实现(Implementation)--而只用来描述这些实现应该是什么样的。
协议可以要求其遵循者提供特定的实例属性,实例方法,类方法,操作符或下标脚本等。
protocol SomeProtocol {
// 协议内容
}
struct SomeStructure: FirstProtocol, AnotherProtocol {
// 结构体内容
}
协议可以规定其遵循者提供特定名称与类型的实例属性(instance property),外也可以指定属性是只读的还是可读写的。
protocol SomeProtocol {
var mustBeSettable : Int { get set }
var doesNotNeedToBeSettable: Int { get }
}
常在协议的定义中使用class前缀表示该属性为类成员;在枚举和结构体实现协议时中,需要使用static关键字作为前缀。
协议中的方法支持变长参数(variadic parameter),不支持参数默认值(default value)。
议中类方法的定义与类属性的定义相似,在协议定义的方法前置class关键字来表示。当在枚举或结构体实现类方法时,需要使用static关键字来代替。
用类实现协议中的mutating方法时,不用写mutating关键字;用结构体,枚举实现协议中的mutating方法时,必须写mutating关键字。
协议能够继承一到多个其他协议。语法与类的继承相似,多个协议间用逗号,分隔
一个协议可由多个协议采用protocol<SomeProtocol, AnotherProtocol>这样的格式进行组合,称为协议合成(protocol composition)。
协议合成并不会生成一个新协议类型,而是将多个协议合成为一个临时的协议,超出范围后立即失效。
protocol Named {
var name: String { get }
}}
protocol Aged {
var age: Int { get }
}}
struct Person: Named, Aged {
var name: String
var age: Int
}}
func wishHappyBirthday(celebrator: protocol<Named, Aged>) {
println("Happy birthday \(celebrator.name) - you‘re \(celebrator.age)!")
}}
检验协议的一致性
is操作符用来检查实例是否遵循了某个协议。
as?返回一个可选值,当实例遵循协议时,返回该协议类型;否则返回nil
as用以强制向下转型。
@objc用来表示协议是可选的,也可以用来表示暴露给Objective-C的代码,此外,@objc型协议只对类有效,因此只能在类中检查协议的一致性。
协议中使用@optional关键字作为前缀来定义可选成员。可选协议在调用时使用可选链。
=================================================================
泛型
func swapTwoValues<T>(inout a: T, inout b: T)
数的泛型版本使用了占位类型名字(通常此情况下用字母T来表示)来代替实际类型名(如Int、String或Double)。
泛型函数名后面跟着的展位类型名字(T)是用尖括号括起来的(<T>)。
这个尖括号告诉 Swift 那个T是swapTwoValues函数所定义的一个类型。
可支持多个类型参数,命名在尖括号中,用逗号分开。
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
// function body goes here
}
Apple Swift 中文教程 快速参考 基本语法 更新中...