最近项目需要自定义一个 UIView,虽然不是第一次做,但是还是出现了很多问题,其中最严重是的获取的 self.bounds 是不对。

Debug 能力真的需要提高了,调试了很久,还是靠断点,逐个对比 bounds 才知道问题所在。

http://stackoverflow.com/questions/29763818/making-a-custom-uiview-subview-that-fills-its-superview

这一篇解释的很清楚,我自己混淆了几种方法的使用,还忘记了手动设置 frame 时,还忘了layoutSubviews()。以前可能是宽高确定或根据 UIScreen 来的计算的,一直没出现问题。

  1. Use Auto Layout
    • Interface Builder
    • Programmatically
  2. Manual Layout
    • Resizing Masks
    • Layout Subviews

关键代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupViews()
}

private func setupViews() {

let view = UIView(frame: CGRectZero)
view.setTranslatesAutoresizingMaskIntoConstraints(false)
super.init(frame: frame)
let viewsDict = ["view": view]
addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-0-[view]-0-|", options: .allZeros, metrics: nil, views: viewsDict))
addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-0-[view]-0-|", options: .allZeros, metrics: nil, views: viewsDict))
addSubview(view)

}

原因

viewDidLoad 时获取的size 可能是错误的。

viewDidLoad

The view controller has obtained its view. See the discussion earlier in this chapter of how a view controller gets its view. Recall that this does not mean that the view is in the interface or even that it has been given its correct size.

Core Animation Essentials (WWDC 2011 - Session 421):

https://developer.apple.com/videos/play/wwdc2011/421/

Animations Explained:

https://www.objc.io/issues/12-animations/animations-explained/#a-basic-animation

According above two documents,write code again by myself.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
import UIKit
import QuartzCore

class ViewController: UIViewController {
let layer = CALayer()

override func viewDidLoad() {
super.viewDidLoad()

self.addImageLayer()
}

func addImageLayer() {

layer.bounds = CGRect(x: 0, y: 0, width: 25, height: 25)
layer.position = CGPoint(x: 100, y: 100)
let image = UIImage(named: "yanFei")!
layer.contents = image.CGImage

self.view.layer.addSublayer(layer)
}

@IBAction func stopButtonClick(sender: AnyObject) {
}
@IBAction func firstButtonClick(sender: AnyObject) {
layer.opacity = 0
}
@IBAction func secendButtonClick(sender: AnyObject) {
// Just disable current run loop transaction
// CATransaction.setDisableActions(true)

CATransaction.setAnimationDuration(2)
layer.opacity = 1
layer.position = CGPoint(x: 100, y: 400)
}
@IBAction func thirdButtonClick(sender: AnyObject) {
CATransaction.setAnimationDuration(5)
layer.position = CGPoint(x: 0, y: UIScreen.mainScreen().bounds.height)
layer.opacity = 0
}

@IBAction func fourthButtonClick(sender: AnyObject) {

layer.position = CGPoint(x: layer.position.x, y: 400)

let drop = CABasicAnimation(keyPath: "position.y")
drop.fromValue = 30
drop.toValue = 400
drop.duration = 5
drop.delegate = self
// drop.beginTime = CACurrentMediaTime() + 0.5
layer.addAnimation(drop, forKey: nil)
}
@IBAction func fivethButtonClick(sender: AnyObject) {

let shake = CAKeyframeAnimation(keyPath: "position.x")
// values or path
shake.values = [0, 10, -10, 10, 0]
shake.keyTimes = [0, 1 / 6.0, 3 / 6.0, 5 / 6.0, 1]
shake.duration = 0.4

shake.additive = true

shake.delegate = self

layer.addAnimation(shake, forKey: "shake")
}

@IBAction func sixthButtonClick(sender: AnyObject) {

let boundingRect = CGRect(x: -50, y: -50, width: 100, height: 100)

let orbit = CAKeyframeAnimation(keyPath: "position")
orbit.path = CGPathCreateWithEllipseInRect(boundingRect, nil)
orbit.duration = 4
orbit.additive = true
orbit.repeatCount = Float.infinity
orbit.calculationMode = kCAAnimationPaced
orbit.rotationMode = kCAAnimationRotateAuto

layer.addAnimation(orbit, forKey: "orbit")
}

@IBAction func seventhButtonClick(sender: AnyObject) {

layer.position = CGPoint(x: 300, y: layer.position.y)

let timing = CABasicAnimation(keyPath: "position.x")
timing.fromValue = 30
timing.toValue = 300
timing.duration = 2

// timing.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
timing.timingFunction = CAMediaTimingFunction(controlPoints: 0.5, 0, 0.9, 0.7)
layer.addAnimation(timing, forKey: "timing")
}

@IBAction func eighthButtonClick(sender: AnyObject) {

let zPosistion = CABasicAnimation(keyPath: "zPosition")
zPosistion.fromValue = -1
zPosistion.toValue = 1
zPosistion.duration = 1.2

let rotation = CAKeyframeAnimation(keyPath: "transform.rotation")
rotation.values = [0, 0.14, 0]
rotation.duration = 1.2
rotation.timingFunctions = [CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut),
CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)]

let position = CAKeyframeAnimation(keyPath: "position")
position.values = [NSValue(CGPoint: CGPointZero), NSValue(CGPoint: CGPoint(x: 110, y: -20)), NSValue(CGPoint: CGPointZero)]
position.timingFunctions = [CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut),
CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)]
position.duration = 1.2
position.additive = true

let group = CAAnimationGroup()
group.animations = [zPosistion, rotation, position]
group.duration = 1.2
group.beginTime = CACurrentMediaTime() + 0.5

layer.addAnimation(group, forKey: nil)

layer.zPosition = 1
}
}

extension ViewController {
override func animationDidStart(anim: CAAnimation) {
print(anim)
}

override func animationDidStop(anim: CAAnimation, finished flag: Bool) {
print(anim)
}
}

Demo link:
https://github.com/gewill/test-projects/tree/master/test%20Core%20Animation

When we design function or API, have to choose parameters default values. Here are 3 common styles.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
enum UserType: String {
case Weixin = "wechat"
case Weibo = "weibo"
case QQ = "qq"
}

// 1. Like NSLayoutAnchor, call function can be like less input parameters
func testDefaultParametersValue(userType: UserType = .Weibo) {
print(userType)
}


testDefaultParametersValue()
testDefaultParametersValue(.QQ)

// 2. All parameters have to input
func testOptionalParametersValue(var userType: UserType?) {
// Can set default value inside too
if userType == nil {
userType = .Weibo
}

print(userType)
}

testOptionalParametersValue(nil)
testOptionalParametersValue(.Weixin)


// 3. Like Kingfisher can less parameters or pass nil
func testAll(id: Int? = nil, city: String) {
print(id)
print(city)
}

testAll(city: "Shanghai")
testAll(nil, city: "Shanghai")
testAll(12, city: "Shanghai")

Use resursion to get more 90% data, still can’t upto 100%.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96

import Foundation
import Alamofire
import SwiftyJSON
import Alamofire-SwiftyJSON
import RealmSwift

// MARK: - friendships

// Weibo 30% more or less limits how to aovid this.
// Thus count each response json user nubmer, to calculate true next cursor.
// But Weibo return much fewer than 30%, fanially we got 93% data.
// http://open.weibo.com/wiki/2/friendships/friends

static func allFriends(uid uid: Int?, cursor: Int?, completionHandler: (stateCode: WeiboServiceError, error: String?, nextCursor: Int?) -> Void) {

let pageNumber: Int = 200
var parameters: [String: AnyObject] = ["count": pageNumber]

AccountManager.currentAcvtiveWeiboAccount { (stateCode, error, weiboAccount) -> Void in
if let weiboAccount = weiboAccount {
parameters.updateValue(weiboAccount.accessToken, forKey: "access_token")
parameters.updateValue(weiboAccount.accountId, forKey: "uid")
}
}

if let uid = uid {
parameters.updateValue(uid, forKey: "uid")
}
if let cursor = cursor {
parameters.updateValue(cursor, forKey: "cursor")
}
Alamofire.request(.GET, WeiboApi.FriendshipsFriends, parameters: parameters)
.responseSwiftyJSON({ (request, response, json, error) in
if error != nil {
completionHandler(stateCode: .Error, error: "Please check internet connection.", nextCursor: nil)
} else if json["error"] != JSON.null {
completionHandler(stateCode: .Error, error: json["error"].stringValue, nextCursor: nil)
} else {
WeiboStore.friendsJSONToUserAndSave(json: json, completionHandler: { (stateCode, error, count) -> Void in
if stateCode == .Error {
completionHandler(stateCode: .Error, error: error, nextCursor: nil)
} else {

let previousCursor = json["previous_cursor"].intValue
let nextCursor = previousCursor + count!

if count > 0 {

self.allFriends(uid: uid, cursor: nextCursor, completionHandler: { (stateCode, error, nextCursor) -> Void in
})
} else {

completionHandler(stateCode: .Success, error: nil, nextCursor: nextCursor)
}
}
})
}
})
}


// Model
static func friendsJSONToUserAndSave(json json: JSON, completionHandler: (stateCode: WeiboStoreError, error: String?, count: Int?) -> Void) {

var count = 0

let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
dispatch_async(queue) {

let realm = try! Realm()
realm.beginWrite()

if json["users"] != JSON.null {

for (_, subJson): (String, JSON) in json["users"] {
let friend = self.JSONToUserModel(subJson)
friend.isFriend = true
realm.add(friend, update: true)

count += 1
}
}

do {
try realm.commitWrite()
dispatch_async(dispatch_get_main_queue(), { () -> Void in
completionHandler(stateCode: .Success, error: nil, count: count)
})
} catch {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
completionHandler(stateCode: .Error, error: "Realm Database save error.", count: nil)
})
}
}
}

The Swift Programming Language Examples

源码在 GitHub:https://github.com/gewill/The-Swift-Programming-Language-2.1-Examples

Playground ->

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// : Playground - noun: a place where people can play

import UIKit

//: 嵌套类型(Nested Types)



//嵌套让我们可以为常数生成一个命名空间(namespace)。例如:我们可以使用Constants.FoursquareApi.BaseUrl来访问Foursquare的BaseUrl常数,这样会使得数据可读性更高,并为相关的常数提供一系列封装。
//http://geek.csdn.net/news/detail/58593
import Foundation
struct Constants {
struct FoursquareApi {
static let BaseUrl = "https://api.foursquare.com/v2/"
}
struct TwitterApi {
static let BaseUrl = "https://api.twitter.com/1.1/"
}
struct Configuration {
static let UseWorkaround = true
}
}


//上面的嵌套是不错的实践,当然扑克牌作为例子是非常合适的。

struct BlackjackCard {
// 嵌套的 Suit 枚举
enum Suit: Character {
case Spades = "♠", Hearts = "♡", Diamonds = "♢", Clubs = "♣"
}

// 嵌套的 Rank 枚举
enum Rank: Int {
case Two = 2, Three, Four, Five, Six, Seven, Eight, Nine, Ten
case Jack, Queen, King, Ace
struct Values {
let first: Int, second: Int?
}
var values: Values {
switch self {
case .Ace:
return Values(first: 1, second: 11)
case .Jack, .Queen, .King:
return Values(first: 10, second: nil)
default:
return Values(first: self.rawValue, second: nil)
}
}
}

// BlackjackCard 的属性和方法
let rank: Rank, suit: Suit
var description: String {
var output = "suit is \(suit.rawValue),"
output += " value is \(rank.values.first)"
if let second = rank.values.second {
output += " or \(second)"
}
return output
}
}

BlackjackCard(rank: .Ace, suit: .Spades).description
let heartsSymbol = BlackjackCard.Suit.Hearts.rawValue

The Swift Programming Language Examples

源码在 GitHub:https://github.com/gewill/The-Swift-Programming-Language-2.1-Examples

Playground ->

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
// : Playground - noun: a place where people can play

import UIKit

//: 错误处理(Error Handling)

//说实话错误处理这里,习惯了 AFNetworking 方式的闭包或者 block。因为闭包可以返回更过参数如:错误类型/错误内容/其他参数
//这里学会三种调用方法即可,try? 最方便,如不打算考虑处理错误信息


enum VendingMachineError: ErrorType {
case InvalidSelection //选择无效
case InsufficientFunds(coinsNeeded: Int) //金额不足
case OutOfStock //缺货
}


struct Item {
var price: Int
var count: Int
}

class VendingMachine {
var inventory = [
"Candy Bar": Item(price: 12, count: 7),
"Chips": Item(price: 10, count: 4),
"Pretzels": Item(price: 7, count: 11)
]
var coinsDeposited = 0
func dispenseSnack(snack: String) {
print("Dispensing \(snack)")
}

func vend(itemNamed name: String) throws {
guard var item = inventory[name] else {
throw VendingMachineError.InvalidSelection
}

guard item.count > 0 else {
throw VendingMachineError.OutOfStock
}

guard item.price <= coinsDeposited else {
throw VendingMachineError.InsufficientFunds(coinsNeeded: item.price - coinsDeposited)
}

coinsDeposited -= item.price
--item.count
inventory[name] = item
dispenseSnack(name)
}
}

let favoriteSnacks = [
"Alice": "Chips",
"Bob": "Licorice",
"Eve": "Pretzels",
]
func buyFavoriteSnack(person: String, vendingMachine: VendingMachine) throws {
let snackName = favoriteSnacks[person] ?? "Candy Bar"
try vendingMachine.vend(itemNamed: snackName)
}

//: 用 Do-Catch 处理错误
var vendingMachine = VendingMachine()
vendingMachine.coinsDeposited = 8
do {
try buyFavoriteSnack("Alice", vendingMachine: vendingMachine)
} catch VendingMachineError.InvalidSelection {
print("Invalid Selection.")
} catch VendingMachineError.OutOfStock {
print("Out of Stock.")
} catch VendingMachineError.InsufficientFunds(let coinsNeeded) {
print("Insufficient funds. Please insert an additional \(coinsNeeded) coins.")
}
// 打印 “Insufficient funds. Please insert an additional 2 coins.”


//: 将错误转换成可选值

func fetchData() -> Data? {
if let data = try? fetchDataFromDisk() { return data }
if let data = try? fetchDataFromServer() { return data }
return nil
}

//: 禁用错误传递

//有时你知道某个 throwing 函数实际上在运行时是不会抛出错误的,在这种情况下,你可以在表达式前面写try!来禁用错误传递,这会把调用包装在一个断言不会有错误抛出的运行时断言中。如果实际上抛出了错误,你会得到一个运行时错误。

//例如,下面的代码使用了loadImage(_:)函数,该函数从给定的路径加载图片资源,如果图片无法载入则抛出一个错误。在这种情况下,因为图片是和应用绑定的,运行时不会有错误抛出,所以适合禁用错误传递:

let photo = try! loadImage("./Resources/John Appleseed.jpg")

//: 指定清理操作
//可以使用defer语句在即将离开当前代码块时执行一系列语句。

func processFile(filename: String) throws {
if exists(filename) {
let file = open(filename)
defer {
close(file)
}
while let line = try file.readline() {
// 处理文件。
}
// close(file) 会在这里被调用,即作用域的最后。
}
}


The Swift Programming Language Examples

源码在 GitHub:https://github.com/gewill/The-Swift-Programming-Language-2.1-Examples

Playground ->

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// : Playground - noun: a place where people can play

import UIKit

//: 自动引用计数(Automatic Reference Counting)

//自动引用计数实践
class Person0 {
let name: String
init(name: String) {
self.name = name
print("\(name) is being initialized")
}
deinit {
print("\(name) is being deinitialized")
}
}

var reference1: Person0?
var reference2: Person0?
var reference3: Person0?

reference1 = Person0(name: "John Appleseed")

reference2 = reference1
reference3 = reference1

reference1 = nil
reference2 = nil

reference3 = nil

//: 解决实例之间的循环强引用

//Swift 提供了两种办法用来解决你在使用类的属性时所遇到的循环强引用问题:弱引用(weak reference)和无主引用(unowned reference)。

//弱引用和无主引用允许循环引用中的一个实例引用另外一个实例而不保持强引用。这样实例能够互相引用而不产生循环强引用。

//对于生命周期中会变为nil的实例使用弱引用。相反地,对于初始化赋值后再也不会被赋值为nil的实例,使用无主引用。

//和弱引用类似,无主引用不会牢牢保持住引用的实例。和弱引用不同的是,无主引用是永远有值的。因此,无主引用总是被定义为非可选类型(non-optional type)。你可以在声明属性或者变量时,在前面加上关键字unowned表示这是一个无主引用。

//: 1 - Person和Apartment的例子展示了两个属性的值都允许为nil,并会潜在的产生循环强引用。这种场景最适合用弱引用来解决。
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

//: 2 - Customer和CreditCard的例子展示了一个属性的值允许为nil,而另一个属性的值不允许为nil,这也可能会产生循环强引用。这种场景最适合通过无主引用来解决。
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 lee: Customer?
lee = Customer(name: "Brunce Lee")
let card = CreditCard(number: 1234567890123456, customer: lee!)
lee = nil
card

//: 3 - 两个属性都必须有值,并且初始化完成后永远不会为nil。在这种场景中,需要一个类使用无主属性,而另外一个类使用隐式解析可选属性。

class Country {
let name: String
var capitalCity: City!
init(name: String, capitalName: String) {
self.name = name
self.capitalCity = City(name: capitalName, country: self)
}
}
class City {
let name: String
unowned let country: Country
init(name: String, country: Country) {
self.name = name
self.country = country
}
}

var country: Country?
country = Country(name: "Canada", capitalName: "Ottawa")
print("\(country!.name)'s capital city is called \(country!.capitalCity.name)")
country = nil

//: 解决闭包引起的循环强引用

//在定义闭包时同时定义捕获列表作为闭包的一部分,通过这种方式可以解决闭包和类实例之间的循环强引用。

/*
lazy var someClosure: (Int, String) -> String = {
[unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) -> String in
// 这里是闭包的函数体
}
*/

class HTMLElement {

let name: String
let text: String?

lazy var asHTML: Void -> 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())
paragraph = nil



The Swift Programming Language Examples

源码在 GitHub:https://github.com/gewill/The-Swift-Programming-Language-2.1-Examples

Playground ->

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// : Playground - noun: a place where people can play

import UIKit

//: 自动引用计数(Automatic Reference Counting)

//自动引用计数实践
class Person0 {
let name: String
init(name: String) {
self.name = name
print("\(name) is being initialized")
}
deinit {
print("\(name) is being deinitialized")
}
}

var reference1: Person0?
var reference2: Person0?
var reference3: Person0?

reference1 = Person0(name: "John Appleseed")

reference2 = reference1
reference3 = reference1

reference1 = nil
reference2 = nil

reference3 = nil

//: 解决实例之间的循环强引用

//Swift 提供了两种办法用来解决你在使用类的属性时所遇到的循环强引用问题:弱引用(weak reference)和无主引用(unowned reference)。

//弱引用和无主引用允许循环引用中的一个实例引用另外一个实例而不保持强引用。这样实例能够互相引用而不产生循环强引用。

//对于生命周期中会变为nil的实例使用弱引用。相反地,对于初始化赋值后再也不会被赋值为nil的实例,使用无主引用。

//和弱引用类似,无主引用不会牢牢保持住引用的实例。和弱引用不同的是,无主引用是永远有值的。因此,无主引用总是被定义为非可选类型(non-optional type)。你可以在声明属性或者变量时,在前面加上关键字unowned表示这是一个无主引用。

//: 1 - Person和Apartment的例子展示了两个属性的值都允许为nil,并会潜在的产生循环强引用。这种场景最适合用弱引用来解决。
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

//: 2 - Customer和CreditCard的例子展示了一个属性的值允许为nil,而另一个属性的值不允许为nil,这也可能会产生循环强引用。这种场景最适合通过无主引用来解决。
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 lee: Customer?
lee = Customer(name: "Brunce Lee")
let card = CreditCard(number: 1234567890123456, customer: lee!)
lee = nil
card

//: 3 - 两个属性都必须有值,并且初始化完成后永远不会为nil。在这种场景中,需要一个类使用无主属性,而另外一个类使用隐式解析可选属性。

class Country {
let name: String
var capitalCity: City!
init(name: String, capitalName: String) {
self.name = name
self.capitalCity = City(name: capitalName, country: self)
}
}
class City {
let name: String
unowned let country: Country
init(name: String, country: Country) {
self.name = name
self.country = country
}
}

var country: Country?
country = Country(name: "Canada", capitalName: "Ottawa")
print("\(country!.name)'s capital city is called \(country!.capitalCity.name)")
country = nil

//: 解决闭包引起的循环强引用

//在定义闭包时同时定义捕获列表作为闭包的一部分,通过这种方式可以解决闭包和类实例之间的循环强引用。

/*
lazy var someClosure: (Int, String) -> String = {
[unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) -> String in
// 这里是闭包的函数体
}
*/

class HTMLElement {

let name: String
let text: String?

lazy var asHTML: Void -> 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())
paragraph = nil



The Swift Programming Language Examples

源码在 GitHub:https://github.com/gewill/The-Swift-Programming-Language-2.1-Examples

Playground ->

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// : Playground - noun: a place where people can play

import UIKit

//: 析构过程(Deinitialization)

//析构器只适用于类类型,当一个类的实例被释放之前,析构器会被立即调用。析构器用关键字deinit来标示,类似于构造器要用init来标示。

//有点像 UIViewController 的 func ViewWillDisappear

class Bank {
static var coinsInBank = 10_000
static func vendCoins(var numberOfCoinsToVend: Int) -> Int {
numberOfCoinsToVend = min(numberOfCoinsToVend, coinsInBank)
coinsInBank -= numberOfCoinsToVend
return numberOfCoinsToVend
}
static func receiveCoins(coins: Int) {
coinsInBank += coins
}
}

class Player {
var coinsInPurse: Int
init(coins: Int) {
coinsInPurse = Bank.vendCoins(coins)
}
func winCoins(coins: Int) {
coinsInPurse += Bank.vendCoins(coins)
}
deinit {
Bank.receiveCoins(coinsInPurse)
}
}

var playerOne: Player? = Player(coins: 100)
print("A new player has joined the game with \(playerOne!.coinsInPurse) coins")
print("There are now \(Bank.coinsInBank) coins left in the bank")

playerOne?.winCoins(300)

print("The player has \(playerOne!.coinsInPurse) coins")
print("There are now \(Bank.coinsInBank) coins left in the bank")

playerOne = nil
print("The player has \(playerOne?.coinsInPurse) coins")
print("There are now \(Bank.coinsInBank) coins left in the bank")


The Swift Programming Language Examples

源码在 GitHub:https://github.com/gewill/The-Swift-Programming-Language-2.1-Examples

Playground ->

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
// : Playground - noun: a place where people can play

import UIKit

//: 构造过程(Initialization)

//: 通过定义构造器(Initializers)来实现构造过程,这些构造器可以看做是用来创建特定类型新实例的特殊方法。与 Objective-C 中的构造器不同,Swift 的构造器无需返回值,它们的主要任务是保证新实例在第一次使用前完成正确的初始化。

//: 类的实例也可以通过定义析构器(deinitializer)在实例释放之前执行特定的清除工作。想了解更多关于析构器的内容,请参考析构过程。

//: 类和结构体在创建实例时,必须为所有存储型属性设置合适的初始值。存储型属性的值不能处于一个未知的状态。

struct Fahrenheit {
var temperature: Double
init() {
temperature = 32.0
}
}

struct Fahrenheit1 {
var temperature = 32.0
}

//: 如果你在定义构造器时没有提供参数的外部名字,Swift 会为构造器的每个参数自动生成一个跟内部名字相同的外部名。

struct Color {
let red, green, blue: Double
init(red: Double, green: Double, blue: Double) {
self.red = red
self.green = green
self.blue = blue
}
init(white: Double) {
red = white
green = white
blue = white
}
}

let green = Color(red: 1, green: 1, blue: 1)

struct Celsius {
var temperatureInCelsius: Double
init(fromFahrenheit fahrenheit: Double) {
temperatureInCelsius = (fahrenheit - 32.0) / 1.8
}
init(fromKelvin kelvin: Double) {
temperatureInCelsius = kelvin - 273.15
}
init(_ celsius: Double) {
temperatureInCelsius = celsius
}
}
let bodyTemperature = Celsius(37.0)

//: 可选类型的属性将自动初始化为nil,表示这个属性是有意在初始化时设置为空的。

//: 如果结构体或类的所有属性都有默认值,同时没有自定义的构造器,那么 Swift 会给这些结构体或类提供一个默认构造器(default initializers)。这个默认构造器将简单地创建一个所有属性值都设置为默认值的实例。

class ShoppingListItem {
var name: String?
var quantity = 1
}

var item = ShoppingListItem()

//: 结构体的逐一成员构造器

struct Size {
var width = 0.0, height = 0.0
}

let size = Size(width: 3, height: 4)

//: 构造器可以通过调用其它构造器来完成实例的部分构造过程。这一过程称为构造器代理,它能减少多个构造器间的代码重复。

struct Point {
var x = 0.0, y = 0.0
}

struct Rect {
var origin = Point()
var size = Size()

init() { }

init(origin: Point, size: Size) {
self.origin = origin
self.size = size
}

init(center: Point, size: Size) {
let originX = center.x - (size.width / 2)
let originY = center.y - (size.height / 2)
self.init(origin: Point(x: originX, y: originY), size: size)
}
}

let rect = Rect(origin: Point(x: 3, y: 3), size: Size(width: 5, height: 6))
let rect1 = Rect(center: Point(), size: Size(width: 4, height: 4))

//: 如果你想用另外一种不需要自己定义init()和init(origin:size:)的方式来实现这个例子,请参考扩展。

//: Swift 为类类型提供了两种构造器来确保实例中所有存储型属性都能获得初始值,它们分别是指定构造器和便利构造器。

//: 两段式构造过程: Swift 中类的构造过程包含两个阶段。第一个阶段,每个存储型属性被引入它们的类指定一个初始值。当每个存储型属性的初始值被确定后,第二阶段开始,它给每个类一次机会,在新实例准备使用之前进一步定制它们的存储型属性。

//: Swift 的两段式构造过程跟 Objective-C 中的构造过程类似。最主要的区别在于阶段 1,Objective-C 给每一个属性赋值0或空值(比如说0或nil)。Swift 的构造流程则更加灵活,它允许你设置定制的初始值,并自如应对某些属性不能以0或nil作为合法默认值的情况。

//阶段 1
//
//某个指定构造器或便利构造器被调用。
//完成新实例内存的分配,但此时内存还没有被初始化。
//指定构造器确保其所在类引入的所有存储型属性都已赋初值。存储型属性所属的内存完成初始化。
//指定构造器将调用父类的构造器,完成父类属性的初始化。
//这个调用父类构造器的过程沿着构造器链一直往上执行,直到到达构造器链的最顶部。
//当到达了构造器链最顶部,且已确保所有实例包含的存储型属性都已经赋值,这个实例的内存被认为已经完全初始化。此时阶段 1 完成。
//阶段 2
//
//从顶部构造器链一直往下,每个构造器链中类的指定构造器都有机会进一步定制实例。构造器此时可以访问self、修改它的属性并调用实例方法等等。
//最终,任意构造器链中的便利构造器可以有机会定制实例和使用self。

//: 构造器的继承和重写

//跟 Objective-C 中的子类不同,Swift 中的子类默认情况下不会继承父类的构造器。Swift 的这种机制可以防止一个父类的简单构造器被一个更专业的子类继承,并被错误地用来创建子类的实例。
//你在子类中“重写”一个父类便利构造器时,不需要加override前缀。
//当你在编写一个和父类中指定构造器相匹配的子类构造器时,你实际上是在重写父类的这个指定构造器。因此,你必须在定义子类构造器时带上override修饰符。

//: 构造器的自动继承

//如上所述,子类在默认情况下不会继承父类的构造器。但是如果满足特定条件,父类构造器是可以被自动继承的。在实践中,这意味着对于许多常见场景你不必重写父类的构造器,并且可以在安全的情况下以最小的代价继承父类的构造器。
//
//假设你为子类中引入的所有新属性都提供了默认值,以下 2 个规则适用:
//
//规则 1
//
//如果子类没有定义任何指定构造器,它将自动继承所有父类的指定构造器。
//
//规则 2
//
//如果子类提供了所有父类指定构造器的实现——无论是通过规则 1 继承过来的,还是提供了自定义实现——它将自动继承所有父类的便利构造器。
//
//即使你在子类中添加了更多的便利构造器,这两条规则仍然适用。
//对于规则 2,子类可以将父类的指定构造器实现为便利构造器。

//简单总结就是:None or All

//: 指定构造器和便利构造器实践

class Food {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "[Unnamed]")
}
}

//All
class RecipeIngredient: Food {
var quantity: Int
init(name: String, quantity: Int) {
self.quantity = quantity
super.init(name: name)
}
override convenience init(name: String) {
self.init(name: name, quantity: 1)
}
}

//None
class ShoppingListItem1: RecipeIngredient {
var purchased = false
var description: String {
var output = "\(quantity) x \(name)"
output += purchased ? " ✔" : " ✘"
return output
}
}

//: 可失败构造器

//创建自定义的可选类型
//如果一个类、结构体或枚举类型的对象,在构造过程中有可能失败,则为其定义一个可失败构造器。这里所指的“失败”是指,如给构造器传入无效的参数值,或缺少某种所需的外部资源,又或是不满足某种必要的条件等。

struct Animal {
let species: String
init?(species: String) {
if species.isEmpty { return nil }
self.species = species
}
}

let someCreature = Animal(species: "Gigg")
print(someCreature)

//枚举类型的可失败构造器

enum TemperatureUnit {
case Kelvin, Celsius, Fahrenheit
init?(symbol: Character) {
switch symbol {
case "K":
self = .Kelvin
case "C":
self = .Celsius
case "F":
self = .Fahrenheit
default:
return nil
}
}
}
let fahrenheitUnit = TemperatureUnit(symbol: "F")
if fahrenheitUnit != nil {
print("This is a defined temperature unit, so initialization succeeded.")
}

//带原始值的枚举类型会自带一个可失败构造器init?(rawValue:),该可失败构造器有一个名为rawValue的参数,其类型和枚举类型的原始值类型一致,如果该参数的值能够和某个枚举成员的原始值匹配,则该构造器会构造相应的枚举成员,否则构造失败。

enum TemperatureUnit1: Character {
case Kelvin = "K", Celsius = "C", Fahrenheit = "F"
}

let fahrenheitUnit1 = TemperatureUnit1(rawValue: "K")
if fahrenheitUnit1 != nil {
print("This is a defined temperature unit, so initialization succeeded.")
}

//: 类的可失败构造器

//值类型(也就是结构体或枚举)的可失败构造器,可以在构造过程中的任意时间点触发构造失败。比如在前面的例子中,结构体Animal的可失败构造器在构造过程一开始就触发了构造失败,甚至在species属性被初始化前。

//而对类而言,可失败构造器只能在类引入的所有存储型属性被初始化后,以及构造器代理调用完成后,才能触发构造失败。

//这个很好理解因为继承和构造器代理调用的原因,只能在最后一步判断是否构造失败

class Product {
let name: String!
init?(name: String) {
self.name = name
if name.isEmpty { return nil }
}
}

//可失败构造器也可以代理到其它的非可失败构造器。通过这种方式,你可以增加一个可能的失败状态到现有的构造过程中。

//如同其它的构造器,你可以在子类中重写父类的可失败构造器。或者你也可以用子类的非可失败构造器重写一个父类的可失败构造器。这使你可以定义一个不会构造失败的子类,即使父类的构造器允许构造失败。
//你可以用非可失败构造器重写可失败构造器,但反过来却不行。

class Document {
var name: String?

init() { }

init?(name: String) {
self.name = name
if name.isEmpty { return nil }
}
}

class AutomoticallyNamedDocument: Document {

override init() {

super.init()
self.name = "[Untitiled]"
}

override init(name: String) {
super.init()
if name.isEmpty {
self.name = "[Untitiled]"
} else {
self.name = name
}
}
}

let newDoc = AutomoticallyNamedDocument()
newDoc.name

//你可以在init?中代理到init!,反之亦然。你也可以用init?重写init!,反之亦然。你还可以用init代理到init!,不过,一旦init!构造失败,则会触发一个断言

//: 必要构造器

//在类的构造器前添加required修饰符表明所有该类的子类都必须实现该构造器

//: 通过闭包或函数设置属性的默认值

//提供了一种便利

class SomeClass {
let someProperty: String = {
// 在这个闭包中给 someProperty 创建一个默认值
// someValue 必须和 SomeType 类型相同
return "Lee"
}()
}

let some = SomeClass()
some.someProperty

struct Checkerboard {
let boardColors: [Bool] = {

var tempColors = [Bool]()
var isBlack = false

for i in 1 ... 10 {

for j in 1 ... 10 {
tempColors.append(isBlack)
isBlack = !isBlack
}

isBlack = !isBlack
}

return tempColors
}()

func squareIsBlackAtRow(row: Int, column: Int) -> Bool {
return boardColors[(row * 10) + column]
}
}

let board = Checkerboard()
board.squareIsBlackAtRow(0, column: 10) //bug 此处仅为示范代码