接著看下面的幾個(gè)小節(jié),話說剩下的幾個(gè)小姐也忒特么長了吧~
Protocols
這一小節(jié)主要講解的是Swift中協(xié)議膏秫,超長的一節(jié)右遭。
-
Property Requirements
這里有點(diǎn)難翻譯,直接說吧缤削。這里說的是協(xié)議中定義屬性的時(shí)候窘哈,遵循協(xié)議的類型也要在其內(nèi)部定義該屬性,并且如果協(xié)議中屬性是gettable和settable的亭敢,則類型內(nèi)屬性必須是var的(不能是constant或者只讀)滚婉,如果協(xié)議中的屬性是gettable的,則類型內(nèi)可以是任何類型帅刀∪酶梗看一下代碼吧:
protocol FullyNamed {
var fullName: String { set get }
}
struct Person: FullyNamed {
var fullName: String
}
struct Animal: FullyNamed {
// 編譯錯(cuò)誤: Type 'Animal' does not conform to protocol 'FullyNamed'
// 必須是val
let fullName: String
}
protocol FullyNamed {
var fullName: String { get }
}
struct Person: FullyNamed {
var fullName: String
}
struct Animal: FullyNamed {
// 沒有錯(cuò)誤
let fullName: String
}
-
Initializer Requirements
協(xié)議也可以定義構(gòu)造器,在遵循協(xié)議的類型中可以將這個(gè)構(gòu)造器實(shí)現(xiàn)成指定構(gòu)造器或者便利構(gòu)造器:
protocol SomeProtocol {
init(someParameter: Int)
}
class OtherClass: SomeProtocol {
required init(someParameter: Int) {
// initializer implementation goes here
}
}
class SomeClass: SomeProtocol {
required convenience init(someParameter: Int) {
// initializer implementation goes here
self.init(test: 0)
}
init(test: Int) {
}
}
-
Class-Only Protocols
可以在協(xié)議后面添加上AnyObject表示這個(gè)協(xié)議只能被類遵循
class Person: SomeClassOnlyProtocol {
}
struct Size: SomeClassOnlyProtocol {
//報(bào)錯(cuò):Non-class type 'Size' cannot conform to class protocol 'SomeClassOnlyProtocol'
}
protocol SomeClassOnlyProtocol: AnyObject {
}
-
Checking for Protocol Conformance
也可以用is和as去操作協(xié)議
class Person: SomeClassOnlyProtocol {
func test() {
print("Hello")
}
}
protocol SomeClassOnlyProtocol: AnyObject {
func test()
}
let p = Person()
let p1: Any = p
print(p1 is SomeClassOnlyProtocol)
// 打印 true
(p1 as? SomeClassOnlyProtocol)?.test()
// 打印 Hello
-
Optional Protocol Requirements
和oc一樣扣溺,Swift協(xié)議中的屬性或者方法也是可以修飾成可以不實(shí)現(xiàn)的骇窍,但是Swift中比較奇怪一點(diǎn)。首先需要在協(xié)議前面加上@objc锥余,然后在可以選擇不實(shí)現(xiàn)的方法前面加上@objc optional腹纳,@objc在文檔中解釋是為了能夠在OC中調(diào)用,optional就是可選的意思嘛(ps:加上@objc之后該協(xié)議只能被Objective-C的類遵循驱犹,無法被結(jié)構(gòu)體和枚舉遵循)
class Person: SomeProtocol {
}
@objc protocol SomeProtocol {
@objc optional func test()
}
struct test: SomeProtocol {
//錯(cuò)誤: Non-class type 'test' cannot conform to class protocol 'SomeProtocol'
}
-
Protocol Extensions
也可以給協(xié)議擴(kuò)展方法嘲恍,不過這里不只是聲明方法,需要實(shí)現(xiàn)
class Person: SomeProtocol {
}
@objc protocol SomeProtocol {
}
extension SomeProtocol {
func test() {
print("hello")
}
}
let p = Person()
p.test()
//打印 hello
Generics
這一小節(jié)主要講解的是Swift中泛型雄驹,依舊是長長的一節(jié)佃牛。
Generics are one of the most powerful features of Swift, and much of the Swift standard library is built with generic code. In fact, you’ve been using generics throughout the Language Guide, even if you didn’t realize it. For example, Swift’s Array and Dictionary types are both generic collections. You can create an array that holds Int values, or an array that holds String values, or indeed an array for any other type that can be created in Swift. Similarly, you can create a dictionary to store values of any specified type, and there are no limitations on what that type can be.
貌似泛型還是Swift的一大特性,并且SWIFT標(biāo)準(zhǔn)庫的大部分都是用泛型代碼構(gòu)建的医舆。數(shù)組字典其實(shí)都是泛型集合吁脱。
-
Generic Functions
來看看泛型的簡單應(yīng)用:
方法中的應(yīng)用
func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
let temporaryA = a
a = b
b = temporaryA
}
var someInt = 3
var anotherInt = 107
swapTwoValues(&someInt, &anotherInt)
// someInt is now 107, and anotherInt is now 3
var someString = "hello"
var anotherString = "world"
swapTwoValues(&someString, &anotherString)
// someString is now "world", and anotherString is now "hello"
類型中的應(yīng)用
class Stack<Value> {
var items = [Value]()
subscript(index: Int) -> Value {
return items[index]
}
func push(item: Value) {
items.append(item)
}
func pop() {
items.removeLast()
}
}
let s = Stack<String>()
s.push(item: "Hello")
s.push(item: " ")
s.push(item: "World")
s.push(item: "!")
print(s[3])
-
Extending a Generic Type
給一個(gè)泛型擴(kuò)展
extension Stack {
var top: Value? {
return items.isEmpty ? nil : items[items.count-1]
}
}
-
Type Constraint Syntax
也可以對(duì)一個(gè)泛型進(jìn)行約束:
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
// function body goes here
}
這個(gè)函數(shù)中T必須是SomeClass的子類桑涎,U必須遵循SomeProtocol協(xié)議
-
Associated Types
關(guān)聯(lián)類型。哇兼贡,這段話真的是很難翻譯啊攻冷。百度好多都是直接抄文檔的啥解釋也沒有,看了很久才搞懂一點(diǎn)遍希。associatedtype修飾的東西可以代指一個(gè)類型等曼,文檔中也有例子,不過我覺得還是簡化一下看的更明白:
protocol Container {
associatedtype Item
mutating func append(_ item: Item)
}
struct Stack<Element>: Container {
var items = [Element]()
mutating func append(_ item: Element) {
items.append(item)
}
}
struct Queue<Value>: Container {
var items = [Value]()
mutating func append(_ item: Value) {
items.append(item)
}
}
這里associatedtype后面跟的其實(shí)相當(dāng)于是一個(gè)泛型凿蒜,接著Stack和Queue遵行Container協(xié)議禁谦,在Stack中Item代指Element類型,而在Queue中代指Value類型废封。
-
Adding Constraints to an Associated Type
protocol Container {
associatedtype Item: Equatable
mutating func append(_ item: Item)
var count: Int { get }
subscript(i: Int) -> Item { get }
}
-
Extensions with a Generic Where Clause
extension中使用泛型限制條款
extension Stack where Element: Equatable {
func isTop(_ item: Element) -> Bool {
guard let topItem = items.last else {
return false
}
return topItem == item
}
}
這里的Stack集合中的元素必須是遵循Equatable協(xié)議的,也就是說只有當(dāng)Stack集合中的元素遵循了Equatable協(xié)議州泊,該集合才能獲得isTop這個(gè)擴(kuò)展方法。后面也有提到關(guān)聯(lián)類型以及下標(biāo)中都可以這樣使用漂洋。
Automatic Reference Counting
這一小節(jié)主要講解的是Swift中的內(nèi)存管理遥皂。和oc一樣,swift也是自動(dòng)管理內(nèi)存刽漂,但是仍然存在一些情況演训,需要特殊處理。
-
Strong Reference Cycles Between Class Instances
類實(shí)例之間的強(qiáng)引用循環(huán),這里介紹了一個(gè)循環(huán)引用的列子贝咙,和之前oc碰到的情況很像样悟,先看一段代碼:
class Person {
let name: String
init(name: String) { self.name = name }
var apartment: Apartment?
deinit { print("\(name) is being deinitialized") }
}
class Apartment {
let unit: String
init(unit: String) { self.unit = unit }
var tenant: Person?
deinit { print("Apartment \(unit) is being deinitialized") }
}
var john: Person?
var unit4A: Apartment?
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
john!.apartment = unit4A
unit4A!.tenant = John
john = nil
unit4A = nil
結(jié)果運(yùn)行的時(shí)候2個(gè)實(shí)例對(duì)象的析構(gòu)函數(shù)都不會(huì)被調(diào)用,說明2者都沒有被成功釋放庭猩。官方文檔中3張圖很形象的描述了這個(gè)過程窟她。-
Resolving Strong Reference Cycles Between Class Instances
有兩種解決方法:
1.Weak References
和oc的弱引用幾乎一樣徙缴,需要注意的是因?yàn)槿跻迷诔绦蜻\(yùn)行過程中可能會(huì)被設(shè)置為nil试伙,所以weak修飾的通常是變量而不是常量嘁信。上面代碼需要改成如下:
class Person {
let name: String
init(name: String) { self.name = name }
var apartment: Apartment?
deinit { print("\(name) is being deinitialized") }
}
class Apartment {
let unit: String
init(unit: String) { self.unit = unit }
weak var tenant: Person?
deinit { print("Apartment \(unit) is being deinitialized") }
}
var john: Person?
var unit4A: Apartment?
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
john!.apartment = unit4A
unit4A!.tenant = John
john = nil
unit4A = nil
對(duì)于這段代碼文檔中也有圖片形容.不過我試過如果John不設(shè)置成nil于样,unit4A也是不能正常釋放,并且john!.apartment也不為nil潘靖,依舊指向著那塊內(nèi)存空間穿剖。
2.Unowned References
Like a weak reference, an unowned reference does not keep a strong hold on the instance it refers to. Unlike a weak reference, however, an unowned reference is used when the other instance has the same lifetime or a longer lifetime. You indicate an unowned reference by placing the unowned keyword before a property or variable declaration.
An unowned reference is expected to always have a value. As a result, ARC never sets an unowned reference’s value to nil, which means that unowned references are defined using nonoptional types.
Unowned和弱引用一樣的地方是對(duì)實(shí)例對(duì)象沒有保持一個(gè)強(qiáng)引用,不同的是Unowned是用在當(dāng)其他實(shí)例對(duì)象擁有相同或者更長的生命周期的時(shí)候卦溢。
unowned修飾的實(shí)例對(duì)象一直會(huì)有一個(gè)值糊余,所以ARC從不將它設(shè)置成nil秀又,這意味著unowned修飾的實(shí)例對(duì)象通常被定義成非可選類型。
使用如下:
class Customer {
let name: String
var card: CreditCard?
init(name: String) {
self.name = name
}
deinit { print("\(name) is being deinitialized") }
}
class CreditCard {
let number: UInt64
unowned let customer: Customer
init(number: UInt64, customer: Customer) {
self.number = number
self.customer = customer
}
deinit { print("Card #\(number) is being deinitialized") }
}
var john: Customer?
john = Customer(name: "John Appleseed")
john!.card = CreditCard(number: 1234_5678_9012_3456, customer: John!)
john = nil
對(duì)應(yīng)的內(nèi)存圖:文檔上沒有第三張圖了贬芥,不過這里也容易看出下面的內(nèi)存情況吐辙,因?yàn)镃ustomer instance沒有string指針指著了,所以會(huì)被釋放掉蘸劈,接著CreditCard instance也沒有strong指針指著昏苏,也會(huì)被釋放。
-
Strong Reference Cycles for Closures
在閉包中的強(qiáng)引用威沫,oc中也存在這樣的情況贤惯,當(dāng)你沒做任何處理直接在閉包中通過self.someProperty去訪問屬性或者 self.someMethod()去調(diào)用方法就會(huì)對(duì)self進(jìn)行一次"捕獲",從而造成循環(huán)引用棒掠,使self不能正常釋放孵构。
錯(cuò)誤代碼如下:
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = {
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
}
init(name: String, text: String? = nil) {
self.name = name
self.text = text
}
deinit {
print("\(name) is being deinitialized")
}
}
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
// Prints "<p>hello, world</p>"
paragraph = nil
對(duì)應(yīng)的內(nèi)存圖:文檔中沒有給出paragraph = nil的內(nèi)存圖,不過paragraph = nil之后烟很,閉包仍然引用著實(shí)例對(duì)象颈墅,所以這個(gè)實(shí)例對(duì)象不能成功釋放,這里也可以看出閉包是引用類型的溯职。
-
Resolving Strong Reference Cycles for Closures
解決閉包的強(qiáng)引用
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = {
[unowned self] in
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
}
init(name: String, text: String? = nil) {
self.name = name
self.text = text
}
deinit {
print("\(name) is being deinitialized")
}
}
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
// Prints "<p>hello, world</p>"
paragraph = nil
// Prints "p is being deinitialized"
paragraph = nil之前的內(nèi)存圖如下:這樣子就能解決閉包強(qiáng)引用的問題了精盅。