魏名华

不要偷懒,做更好的自己

Nothing


No Welcome Message

Swift笔记 20161205

Swift笔记-20161205

  1. override
  2. 属性 (Properties)
  3. getter 和 setter
  4. guard
  5. struct
  6. init?
  7. try? try!
  8. 可空链式调用(Optional Chaining)
  9. @noescape 和 @escaping
  10. associatedtype
  11. [](#)

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()
    }
}