let
声明常量,var
声明变量- 注释:
//
和/*...*/
,多行注释/*...*/
可嵌套! - 句尾分号可省略
- 类型别名:
typealias AudioSample = UInt16
- 空合运算符,
a ?? b
等价于a!=nil ? a! : b
String
String
是值类型,由编译器保证只在必要时拷贝- 字符串插值:
\(...)
.characters
表示对应的unicode字符组,.characters.count
表示所含的unicode字符数- 不能用数字作索引,使用
.startIndex
、.endIndex
、.startIndex.advancedBy(7)
等 - 相等:==,前缀:.hasPrefix(),后缀:.hasSuffix()
- 可赋值给Selector:
let mySelector: Selector = “tappedButton:"
Tuple
let http404Error = (404, "Not Found")
- 只需要一部分元组值,忽略的部分用
_
标记:let (justTheStatusCode, _) = http404Error
- 通过下标访问元组元素:
http404Error.0
- 定义元组时给元素命名:
let http200Status = (statusCode: 200, description: "OK")
,然后通过名字访问这些元素:http200Status.description
Collection
- 数组类型
[Int]
、集合类型Set<Int>
、字典类型[String:Int]
- 已推断出类型时,
[]
空数组,[:]
空字典 - 合并两数组可用
+
- 数组可区间替换,如
array[4...6] = ["Bananas", "Apples"]
,把中间三个元素换为两个 - 带下标遍历数组,如:
for (index, value) in array.enumerate())
- 集合的值和字典的键必须hashable;所有基本类型(如String, Int, Double, Bool)都hashable
Enum
用法一:存储任何类型的关联值,类似union,给enum中各变量声明不同类型(且不赋实际值)
1
2
3
4enum Barcode {
case UPCA(Int, Int, Int, Int)
case QRCode(String)
}用法二:给enum中各变量赋予同类型的原始值(rawValue)
带原始值的枚举类型自带一个failable构造器1
2
3
4
5enum ASCIIControlCharacter: Character {
case Tab = "\t"
case LineFeed = "\n"
case CarriageReturn = "\r"
}init?(rawValue:)
关联值是自身这一枚举类型,要在这关联值前用
indirect
表示该成员可递归1
2
3
4
5enum ArithmeticExpression {
case Number(Int)
indirect case Addition(ArithmeticExpression, ArithmeticExpression)
indirect case Multiplication(ArithmeticExpression, ArithmeticExpression)
}
Optional
- 实际上是enum:
1
2
3
4enum Optional<T> {
case None
case Some(T)
} - 可空类型(形如
Type?
),可能存在某个值或不存在值,用someOptional!
强制展开 - 可空绑定,如
if let constName = someOptional { }
- 可空链式调用,如
street = paul.residence?.address?.street
,返回的都是可空值(即使原函数返回非空值) - 隐式展开可空值(形如
Type!
),变量在首次初始化后不再为空(一般作实例变量,由类初始化),直接用someOptional
访问
Control Flow
- if/for/while等的条件语句不需要括号:
for index in 1...n
- switch的每个值都至少要有个case分支对应,当现有的分支无法涵盖所有值时要使用default分支
- case分支不会fallthrough,不需要显式地
break
;但可用fallthrough
关键字让控制流直接接上下一个case的执行代码(跳过下一个case的条件检查) - case可以区间匹配(如数值区间
3...5
,tuple区间(_, 0)
,逗号分隔的多个区间1,3,5...7
),各case的区间重叠时按序选择匹配 - case中的模式匹配:
1
2
3
4
5
6
7
8let color = (1.0, 1.0, 1.0, 1.0)
switch color {
case (0.0, 0.5...1.0, let blue, _):
print("Green and \(blue * 100)% blue")
case let (r, g, b, 1.0) where r == g && g == b:
print("Opaque grey \(r * 100)%")
...
} - 循环和switch语句可以加标签,这样多层嵌套时可以break/continue到指定标签
guard
语句类似if语句,但guard
总是有一个else
分句
Function & Closure
- 内外参数同名:
func someFunc(paraName: Int) {}
;显式指定外部参数名:func someFunc(externalParaName localParaName: Int) {}
- 参数列表末可以提供默认参数:
func someFunc(paraName: Int = 12) {}
- 可变参数数组,如
func mean(numbers: Double...) -> Double {}
,最多只能有一个且放在参数列表最最后(默认参数之后) - 参数默认是常量,若要将参数作为可修改副本使用,在参数名前加
var
:func someFunc(var paraName: String) {}
inout
参数由外部在调用时传引用&varParam
:1
2
3
4
5func swapTwoInts(inout a: Int, inout _ b: Int) {
let tmp = a; a = b; b = tmp
}
...
swapTwoInts(&someInt, &anotherInt)- 函数类型由参数和返回值类型组成,如:
(Int, Int) -> Int
,(Int) -> Int
,() -> ()
- 函数中可以嵌套定义新函数
- 闭包语法:
1
2
3{ (parammeters) -> returnType in
statements
} - 闭包可用
$0, $1, $2
等速记参数:reversed = sort(names, { $0 > $1 })
Struct & Class
- struct不能继承;struct是值类型(传拷贝),class是引用类型(传引用)
- 所有基本类型(包括字符串、数组、字典)都是值类型,用struct实现的,编译器会保证只有确实必要时才执行实际拷贝
- struct可以按成员初始化:
let vga = Resolution(width: 640, height: 480)
lazy var
在首次使用时才初始化;全局常量/变量自动是lazy的(不需lazy标记)- 若某实例方法要修改struct/enum等值类型中的属性(包括修改
self
),得给方法添加mutating
声明 - 定义下标索引,如: 也可以是二维的,如:
1
2
3
4subscript(index: Int) -> Int {
get {}
set(newValue) {}
}subscript(row: Int, column: Int) -> Double { ... }
,访问时可用matrix[1, 0]
- 子类重载父类特性时要加
override
声明 - 不想被重载的特性用
final
声明 - 类至少有一个designated构造器,它先初始化本类引入的所有存储型属性,然后调用父类的designated构造器(delegate up),再为继承的属性设置新值
- convenience构造器要前加
convenience
声明,它调用本类的其他构造器并最终调用到某个designated构造器(delegate across),再为任意属性设置新值 - 两阶段初始化:就是个中序遍历,先初始化存储型属性,再访问super.init(),再之后算阶段二用来作些自定义
- 若子类没有designated构造器,则自动继承父类的designated构造器
- 若子类实现了父类的所有designated构造器(不管是自动继承的还是自己实现的),则自动继承父类的convenience构造器
- 可失败构造器:
init?(...)
,若要返回的对象隐式展开:init!(...)
- 子类都必须实现的构造器,前加
required
修饰 - 用闭包和函数设置属性的默认值:
1
2
3
4
5
6class SomeClass {
let someProperty: SomeType = {
…
return someValue
}()
} - 每个类最多只能有一个析构器
deinit
,子类析构器的最后会自动调用父类析构器
ARC
- 对生命周期中会变为nil的实例使用弱引用(weak, optional)
- 对初始化赋值后再也不会变为nil的实例使用无主引用(unowned)
- 两个类中的属性互相引用,要打破循环强引用:
- 两个都可为nil:其中一个使用弱引用(weak)
- 一个可为nil,一个不可为nil:不可为nil的使用无主引用(unowned)
- 两个都不可为nil:一个使用隐式展开可空属性(Type!),一个使用无主引用(unowned)
- 将闭包赋给属性,要打破循环引用,用无主引用来捕获
self
,如:1
2
3
4lazy var someClosure: (Int, String) -> String = {
[unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) -> String in
...
}1
2
3
4lazy var someClosure: () -> String = {
[unowned self, weak delegate = self.delegate!] in
...
}
Error Handling
- 错误用符合
ErrorType
协议的值表示:enum VendingMachineError: ErrorType
- 用
throws
声明函数将抛出错误:func canThrowErrors() throws -> String
- 用
throw
抛出错误:throw VendingMachineError.OutOfStock
- 调用抛出错误的函数时,前面加上
try
:try canThrowErrors()
- 用do-catch捕获错误:
1
2
3
4
5
6do {
try canThrowErrors()
...
} catch someError {
...
} - swift的错误和其他语言的异常类似,但是不会展开调用堆栈,因此throw语句的性能可以几乎和return语句一样
- 如果确认某个声明了throws的函数不会抛出错误,可以用
try!
禁止错误传播:try! willOnlyThrowIfTrue(false)
- 用
defer
语句将操作压栈,推迟到当前作用域结束时执行
Type Casting
- 类型检查用
is
,强制类型转换用as
,如let view = object as UIView
,尝试类型转换用as?
,如if let view = object as? UIView { ... }
- 可以转换整个数组:
if let viewArray = objectArray as? [UIView] { ... }
,有任一元素转换失败时整个返回nil AnyObject
表示任何class类型,Any
表示任何类型
Extension
- 可以添加新的方法,但不能重写已有的方法
- 可以添加计算型属性,但不能添加存储型属性,也不能给已有属性添加属性观测器(willSet/didSet等)
- 可以添加convenience构造器,但不能添加designated构造器或析构器
Protocol
- 如果协议的方法将改变遵循该协议的对象的属性,则在该方法前加
mutating
关键字(为了使struct/enum等值类型也能遵循该协议) - 协议可以继承一个或多个其他协议:
protocol SomeProtocol: InheritedProtocolOne, InheritedProtocolTwo {}
- 限制只能由class遵循该协议(struct/enum不能遵循该协议),要在继承列表开头使用
class
关键字:protocol SomeClassOnlyProtocol: class, InheritedProtocol {}
- 变量遵循了多个协议:
person: protocol<Named, Aged>
- 能用
is/as/as?
检查变量是否遵循协议 - 协议包含可选方法或属性时,要加
@objc
前缀:1
2
3
4protocol CounterDataSource {
optional func incrementForCount(count: Int) -> Int
optional var fixedIncrement: Int { get }
} - 可通过Extension为协议提供默认实现,扩展协议时还能用
where
描述限制条件:1
2
3extension CollectionType where Generator.Element : TextRepresentable {
// 扩展CollectionType协议,但只适用于元素遵循TextRepresentable的情况
} - 可以用
typealias
声明关联类型,给类型提供占位名,当某类实现这协议时能自动推断出实际的关联类型1
2
3
4
5protocol Container {
typealias ItemType
mutating func append(item: ItemType)
...
}
Generics
- 泛型,形如
struct Stack<T> { ... }
- 扩展泛型时,不需要在扩展的定义中再次声明类型参数,原类型参数可以直接使用:
1
2
3extension Stack {
var topItem: T? { ... }
} - 泛型的类型参数可以添加约束,如:
1
2
3
4
5func allItemsMatch<C1: Container, C2: Container
where C1.ItemType == C2.ItemType, C1.ItemType: Equatable>
(someContainer: C1, anotherContainer: C2) -> Bool {
...
}
Access Control
private
源文件内可见;internal
模块内可见,是默认值,模块指以独立单元构建和发布的framework或application;public
都可见- 函数的访问级别由参数和返回值中最小的一个决定
Operators
- 算数运算符默认不会溢出,溢出运算符有:
&+
、&-
、&*
等 - 重载运算符,如:
1
2
3
4struct Vector2D { var x = 0.0,, y = 0.0 }
func +(left: Vector2D, right: Vector2D) -> Vector2D { } // 中缀
prefix func -(vector: Vector2D) -> Vector2D {} // 前缀
func += (inout left: Vector2D, right: Vector2D) { left = left + right } // 组合赋值 - 自定义操作符可声明优先级和结合性: —
1
2infix operator +- { associativity left precedence 140 }
func +- (left: Vector2D, right: Vector2D) -> Vector2D { }
Command Line
- demangle:
xcrun swift-demangle
xxx
Using with Cocoa & ObjC
- 在.m文件中使用swift:
#import "ProductModuleName-Swift.h"
- 在.swift文件中使用objc:把要使用的objc头文件加入
ProductModuleName-Bridging-Header.h
中,之后所有.swift文件中不需要import就能使用 - 从objc类继承的swift类会由编译器自动插入
@objc
,因而在objc中可用 - 不从objc类继承的swift类要在objc中可用,需要自己添加
@objc
标注 - 用
@objc(<#name#>)
可以给swift类/方法起objc别名(不带名称空间) - swift闭包和objc闭包互通,swift闭包中的变量类似objc闭包中的
__block
变量 @NSManaged
就像@dynamic
,表示NSManagedObject
子类属性的存储和实现将在运行时提供
内存布局
- struct按值顺序排,会字节对齐
- swift类都继承自SwiftObject,SwiftObject实现了
协议,但不是NSObject的子类 - SwiftObject前两个8字节分别是isa和refCount (swift中的NSObject类空着这后8字节refCount不用)
- swift中的NSObject布局:isa, superclass, cache, …, 构成vtable的各方法指针
- class的optional,跟指针一样,指向地址或nil;struct的optional多加1字节表示值是否nil
- protocol共占40字节;class的protocol布局为:isa, 空着不用16字节, 指向底层类的metadata的指针,指向实现protocol的vtable的指针;struct的protocol布局为:24字节内inline存储值(超过24字节保存malloc的地址8字节, 空着不用16字节),指向底层类的metadata的指针,指向实现protocol的vtable的指针
Playground和.swift文件
main.swift
和playground一样都是order-dependent的,且允许顶层代码- iOS里加
@UIApplicationMain
的那个文件就相当于main.swift
- 其他swift文件都是order-independent的,且不允许顶层代码
Reference
- [《The Swift Programming Language》][1]
- 《Using Swift with Cocoa and Objective-C》
- Intermediate Swift, WWDC2014
- Exploring Swift Memory Layout [part I][] & [part II][] [1]: http://wiki.jikexueyuan.com/project/swift/ [part I]: https://www.mikeash.com/pyblog/friday-qa-2014-07-18-exploring-swift-memory-layout.html [part II]: https://www.mikeash.com/pyblog/friday-qa-2014-08-01-exploring-swift-memory-layout-part-ii.html