Swift笔记-20161205
- override
- 属性 (Properties)
- getter 和 setter
- guard
- struct
- init?
- try? try!
- 可空链式调用(Optional Chaining)
- @noescape 和 @escaping
- associatedtype
- [](#)
override
子类如果要重写父类的方法的话,需要用 override 标记——如果没有添加 override 就重写父类方法的话编译器会报错。
class Animal {
var leg: Int { return 2 }
func eat() {
print("eat food.")
}
}
class Tiger: Animal {
override var leg: Int { return 4 }
override func eat() {
print("eat meat.")
}
}
属性 (Properties)
- 存储属性存储常量或变量作为实例的一部分,而计算属性计算(不是存 储)一个值。
- 计算属性可以用于类、结构体和枚举,存储属性只能用于类和结构体。
struct Rect {
var origin = Point()
var size = Size()
var center: Point {
get {
let centerX = origin.x + (size.width / 2)
let centerY = origin.y + (size.height / 2)
return Point(x: centerX, y: centerY)
}
set(newCenter) {
origin.x = newCenter.x - (size.width / 2)
origin.y = newCenter.y - (size.height / 2)
}
}
}
Rect 提供了一个名为 center 的计算属性。一个矩形的中心点可以从原点( origin )和尺寸( size )算 出,所以不需要将它以显式声明的 center 来保存。 Rect 的计算属性 center 提供了自定义的 getter 和 setter 来获取和设置矩形的中心点,就像它有一个存储属性一样。
类型属性
使用关键字 static 来定义类型属性。在为类(class)定义计算型类型属性时,可以使用关键字 class 来支持子 类对父类的实现进行重写。下面的例子演示了存储型和计算型类型属性的语法:
struct SomeStructure {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
return 1
}
}
enum SomeEnumeration {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
return 6
}
}
class SomeClass {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
return 27
}
class var overrideableComputedTypeProperty: Int {
return 107
}
}
getter 和 setter
class EquilateralTriangle: NamedShape {
var sideLength: Double = 0.0
var perimeter: Double {
get {
return 3.0 * sideLength
}
set {
sideLength = newValue / 3.0
}
}
}
只读计算属性
只有 getter 没有 setter 的计算属性就是只读计算属性。只读计算属性总是返回一个值,可以通过点运算符访 问,但不能设置新的值。 只读计算属性的声明可以去掉 关键字和花括号
struct Cuboid {
var width = 0.0, height = 0.0, depth = 0.0
var volume: Double {
return width * height * depth
}
}
guard
像 if 语句一样, guard 的执行取决于一个表达式的布尔值。我们可以使用 guard 语句来要求条件必须为真 时,以执行 guard 语句后的代码。不同于 if 语句,一个 guard 语句总是有一个 else 分句,如果条件不为真则执 行 else 分句中的代码。
func greet(person: [String: String]) {
guard let name = person["name"] else {
return
}
print("Hello \(name)")
}
greet(["name": "John"])
// prints "Hello John!"
struct
来创建一个结构体。结构体和类有很多相同的地方,比如方法和构造器。它们之间最大的一个区别就是结构体是传值,类是传引用。
init?
- 可失败构造器: 如果一个类、结构体或枚举类型的对象,在构造自身的过程中有可能失败,则为其定义一个可失败构造器,是非常有用的。
- 其语法为在 init 关键字后面加添问号 (init?)
- 通过 return nil 语句,来表明可失败构造器在何种情况下“失败”。
struct User {
let name: String
let message: String
init?(data: Data) {
guard let obj = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
return nil
}
guard let name = obj?["name"] as? String else {
return nil
}
guard let message = obj?["message"] as? String else {
return nil
}
self.name = name
self.message = message
}
}
try? try!
可以使用 try? 通过将其转换成一个可选值来处理错误。如果在评估 表达式时一个错误被抛出,那么这个表 达式的值就是 。例如下面代码中的 和 有相同的值和特性:
func someThrowingFunction() throws -> Int {
// ...
}
let x = try? someThrowingFunction()
let y: Int?
do {
y = try someThrowingFunction()
} catch {
y = nil
}
有时你知道某个 throwing 函数实际上在运行时是不会抛出错误的。在这种条件下,你可以在表达式前面写 try! 来使错误传递失效,并把调用包装在一个运行时断言(runtime assertion)中来断定不会有错误抛出。如果实际上确实抛出了错误,你就会得到一个运行时错误。
例如下面的代码使用了 loadImage(_:) 函数,该函数从给定 的路径下装载图片资源,如果图片不能够被载入则抛出一个错误。此种情况下因为图片是和应用绑定的,运行时 不会有错误被抛出,所以是错误传递失效是没问题的。
let photo = try! loadImage("./Resources/John Appleseed.jpg")
可空链式调用(Optional Chaining)
使用可空链式调用来强制展开
通过在想调用非空的属性、方法、或下标的可空值(optional value)后面放一个问号,可以定义一个可空 链。这一点很像在可空值后面放一个叹号(!)来强制展开其中值。它们的主要的区别在于当可空值为空时可空 链式只是调用失败,然而强制展开将会触发运行时错误。
class Person {
var residence: Residence?
}
class Residence {
var numberOfRooms = 1
}
let john = Person()
let roomCount = john.residence!.numberOfRooms // this triggers a runtime error
Residence 有一个 Int 类型的属性 numberOfRooms ,其默认值为1。 Person 具有一个可空的 residence 属性,其类型为 Residence? 。
如果使用叹号(!)强制展开获得这个 john 的 residence 属性中的 numberOfRooms 值,会触发运行时错误,因为这时没有可以展开的 residence
可空链式调用提供了一种另一种访问 numberOfRooms 的方法,使用问号(?)来代替原来叹号(!)的位置:
if let roomCount = john.residence?.numberOfRooms {
print("John's residence has \(roomCount) room(s).")
} else {
print("Unable to retrieve the number of rooms.")
}
为可空链式调用定义模型类
通过可空链式调用访问属性
john.residence?.address = someAddress
通过 john.residence 来设定 address 属性, 取决于 john.residence 是否为nil
通过可空链式调用来调用方法
我们就可以使用 if 语句来判断能否成功调用 printNumberOfRooms() 方 法,即使方法本身没有定义返回值。通过返回值是否为 nil 可以判断调用是否成功:
if john.residence?.printNumberOfRooms() != nil {
print("It was possible to print the number of rooms.")
} else {
print("It was not possible to print the number of rooms.")
}
// prints "It was not possible to print the number of rooms."
通过可空链式调用来访问下标
if let firstRoomName = john.residence?[0].name {
print("The first room name is \(firstRoomName).")
} else {
print("Unable to retrieve the first room name.")
}
// prints "Unable to retrieve the first room name."
多层链接
f let johnsStreet = john.residence?.address?.street {
print("John's street name is \(johnsStreet).")
} else {
print("Unable to retrieve the address.")
}
// prints "Unable to retrieve the address."
@noescape 和 @escaping
当一个闭包作为参数传到一个函数中,但是这个闭包在函数返回之后才被执行,我们称该闭包从函数中逃逸。当 你定义接受闭包作为参数的函数时,你可以在参数名之前标注 @noescape ,用来指明这个闭包是不允许“逃逸”出这个函数的。将闭包标注 @noescape 能使编译器知道这个闭包的生命周期(译者注:闭包只能在函数体中 被执行,不能脱离函数体执行,所以编译器明确知道运行时的上下文),从而可以进行一些比较激进的优化。
func someFunctionWithNoescapeClosure(@noescape closure: () -> Void) {
closure()
}
举个例子, sort(_:) 方法接受一个用来进行元素比较的闭包作为参数。这个参数被标注了 @noescape ,因为它确 保自己在排序结束之后就没用了。
一种能使闭包“逃逸”出函数的方法是,将这个闭包保存在一个函数外部定义的变量中。举个例子,很多启动异步操作的函数接受一个闭包参数作为 completion handler。这类函数会在异步操作开始之后立刻返回,但是闭包 直到异步操作结束后才会被调用。在这种情况下,闭包需要“逃逸”出函数,因为闭包需要在函数返回之后被调 用。例如:
var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: () -> Void) {
completionHandlers.append(completionHandler)
}
associatedtype
关联类型。
protocol Request {
...
associatedtype Response
}
struct UserRequest: Request {
...
typealias Response = User
}
_
class Client {
func send<T: Request>(_ r: Request, handler: @escaping (Response?) -> Void) { // T: 泛型; _: 忽略外部参数名称,调用时就不用写参数名
let url = URL(string: host.appending(r.path))!
var request = URLRequest(url: url)
request.httpMethod = r.method.rawValue
// 在示例中我们不需要 `httpBody`,实践中可能需要将 parameter 转为 data
// request.httpBody = ...
let task = URLSession.shared.dataTask(with: request) {
data, _, error in // 忽略参数名称,因为用不了
if let data = data, let res = parse(data: data) {
DispatchQueue.main.async { handler(res) }
} else {
DispatchQueue.main.async { handler(nil) }
}
}
task.resume()
}
}