-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsearch.xml
More file actions
203 lines (143 loc) · 71.4 KB
/
search.xml
File metadata and controls
203 lines (143 loc) · 71.4 KB
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
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title><![CDATA[Blender 常用快捷键]]></title>
<url>%2F2017%2F05%2F07%2FBlender-%E5%B8%B8%E7%94%A8%E5%BF%AB%E6%8D%B7%E9%94%AE%2F</url>
<content type="text"><![CDATA[快捷键 作用 模式 TAB 物体模式和编辑模式切换 - A 全选 编辑模式 B 框选 编辑模式 C 圈选 编辑模式 Shift + C 准星归世界中心 任意模式 Shift + D 复制 物体模式 E 挤出 编辑模式 Alt + E 挤出 编辑模式 F 生成(两点成线,多点成面,两线成面) 编辑模式 G 移动 任意模式 Shift + G 选择相似 编辑模式 H 隐藏所选 任意模式 Alt + H 显示隐藏所选 任意模式 Shift + H 隐藏未选择项 任意模式 Ctrl + I 反选 编辑模式 Ctrl + J 合并(平面/物件) 任意模式 K 刻刀 编辑模式 M 移动到其他涂层 选中物体后 N 视图内右侧菜单 - Ctrl + N 新建文档 - Ctrl + O 打开文档 - Alt + M 合并(到中心,到光标) - R 旋转 任意模式 Ctrl + R 环切 编辑模式 S 缩放 任意模式 Ctrl + S 保存 - Ctrl + Shift + S 另存为 - T 视图内左侧菜单 视图内任意 U UV映射 编辑模式 Ctrl + Alt + U 用户设置 - W 专用项 编辑模式 X 删除/X轴 物体选中 Y Y轴 - Z 线框模式切换/Z轴 试图内 Ctrl + Z 撤销动作 任意模式 ESC 退出(渲染/快捷菜单) 渲染退出在渲染窗口 F12 渲染 - 1 前视图 - 3 侧视图 - 7 顶视图 - 5 切换正交/透视 -]]></content>
</entry>
<entry>
<title><![CDATA[Swift As]]></title>
<url>%2F2017%2F05%2F02%2FSwift-As%2F</url>
<content type="text"><![CDATA[1.从派生类转化为基类,向上转型12345678910class Animal { var a = "ani"}class Cat: Animal { var c = "cat"}let cat = Cat()let animal = cat as Animal 2.消除二意性,数值类型转化1234let num1 = 25 as Intlet num2 = 25 as Floatlet num3 = 25 as Doubleprint(num1,num2,num3) 3.switch语句进行模式匹配123456789101112131415161718192021222324class Animal { }class Cat: Animal { }class Dog: Animal { }let cat = Cat()let animal = cat as Animalswitch animal {case let cat as Dog: print("ani object")case let cat as Cat: print("cat object")default: break} 4.0 as! 向下转型使用,强制转换类型,转换失败,会曝错误12let animal: Animal = Cat()let cat = animal as! Cat 5.0 as? 转化成功返回可选值类型,需要拆包使用,转化失败,返回nil由于as?在转换失败也不会报错,如不能百分百确保转换成功,使用as?,能确定成功,可以使用as!123456let animal: Animal = Cat()if let cat = animal as? Cat { print("cat is not nil")} else { print("cat is nil")}]]></content>
</entry>
<entry>
<title><![CDATA[Swift Closures]]></title>
<url>%2F2017%2F04%2F26%2FSwift-Closures%2F</url>
<content type="text"><![CDATA[闭包是自包含的函数代码块,可以在代码中被传递和使用。Swift 中的闭包与 C 和 Objective-C 中的代码块(blocks)以及其他一些编程语言中的匿名函数比较相似。闭包可以捕获和存储其所在上下文中任意常量和变量的引用。被称为包裹常量和变量。 Swift 会为你管理在捕获过程中涉及到的所有内存操作。 闭包包含三种形式: 全局函数是一个有名字但不会捕获任何值的闭包 嵌套函数是一个有名字并可以捕获其封闭函数域内值的闭包 闭包表达式是一个利用轻量级语法所写的可以捕获其上下文中变量或常量值的匿名闭包 Swift 的闭包表达式拥有简洁的风格,并鼓励在常见场景中进行语法优化,主要优化如下: 利用上下文推断参数和返回值类型 隐式返回单表达式闭包,即单表达式闭包可以省略 return 关键字 参数名称缩写 尾随闭包语法 闭包表达式闭包表达式是一种利用简洁语法构建内联闭包的方式。闭包表达式提供了一些语法优化,使得撰写闭包变得简单明了。下面闭包表达式的例子通过使用几次迭代展示了 sorted(by:) 方法定义和语法优化的方式。每一次迭代都用更简洁的方式描述了相同的功能。 sorted方法Swift 标准库提供了名为 sorted(by:) 的方法,它会根据你所提供的用于排序的闭包函数将已知类型数组中的值进行排序。一旦排序完成,sorted(by:) 方法会返回一个与原数组大小相同,包含同类型元素且元素已正确排序的新数组。原数组不会被 sorted(by:) 方法修改。使用sorted(by:)对下面数组排序:1let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"] 根据规则:全局函数是一个有名字但不会捕获任何值的闭包 ,我们创建一个普通的函数backward(),sorted(by:)接受一个闭包,该闭包函数需要传入与数组元素类型相同的两个值,并返回一个布尔类型值来表明当排序结束后传入的第一个参数排在第二个参数前面还是后面。如果第一个参数值出现在第二个参数值前面,排序闭包函数需要返回true,反之返回false。我们根据规则生成了一个如下的闭包函数:123func backward(_ s1: String, _ s2: String) -> Bool { return s1 > s2} 排序代码:123456let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]func backward(_ s1: String, _ s2: String) -> Bool { return s1 > s2}var reverseNames = names.sorted(by: backward) 闭包表达式语法可以使用闭包表达式对上述代码进行优化。闭包表达式的语法一般形似:123{ (parameters) -> returnType in statements} 闭包的函数体部分由关键字in引入。该关键字表示闭包的参数和返回值类型定义已经完成,闭包函数体即将开始。 我们可以对backward(::)转化为闭包表达式:1234var reverseNames = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 > s2}) 根据上下文推断类型因为排序闭包函数是作为 sorted(by:) 方法的参数传入的,Swift 可以推断其参数和返回值的类型。sorted(by:) 方法被一个字符串数组调用,因此其参数必须是 (String, String) -> Bool 类型的函数。这意味着 (String, String) 和 Bool 类型并不需要作为闭包表达式定义的一部分。因为所有的类型都可以被正确推断,返回箭头(->)和围绕在参数周围的括号也可以被省略:123var reverseNames = names.sorted(by: { s1, s2 in return s1 > s2}) 单行表达式闭包隐式返回在这个例子中,sorted(by:) 方法的参数类型明确了闭包必须返回一个 Bool 类型值。因为闭包函数体只包含了一个单一表达式(s1 > s2),该表达式返回 Bool 类型值,因此这里没有歧义,return 关键字可以省略。123var reverseNames = names.sorted(by: { s1, s2 in s1 > s2}) 参数名称缩写Swift 自动为内联闭包提供了参数名称缩写功能,你可以直接通过 $0,$1,$2 来顺序调用闭包的参数,以此类推。如果你在闭包表达式中使用参数名称缩写,你可以在闭包定义中省略参数列表,并且对应参数名称缩写的类型会通过函数类型进行推断。in关键字也同样可以被省略,因为此时闭包表达式完全由闭包函数体构成:123var reverseNames = names.sorted(by: { $0 > $1}) 运算符方法实际上还有一种更简短的方式来编写上面例子中的闭包表达式。Swift 的 String 类型定义了关于大于号(>)的字符串实现,其作为一个函数接受两个 String 类型的参数并返回 Bool 类型的值。而这正好与 sorted(by:) 方法的参数需要的函数类型相符合。因此,你可以简单地传递一个大于号,Swift 可以自动推断出你想使用大于号的字符串函数实现:1var reverseNames = names.sorted(by: > ) 尾随闭包如果你需要将一个很长的闭包表达式作为最后一个参数传递给函数,可以使用尾随闭包来增强函数的可读性。尾随闭包是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用。在使用尾随闭包时,你不用写出它的参数标签:12345678910111213func someFuncationThatTakesAClosure(closure: () -> Void) { }//不使用尾随闭包someFuncationThatTakesAClosure(closure: { })//使用尾随闭包someFuncationThatTakesAClosure { } 在sorted(by:)方法中,可以简化为:1reversedNames = names.sorted() { $0 > $1 } 如果闭包表达式是函数或方法的唯一参数,则当你使用尾随闭包时,你甚至可以把 () 省略掉:1reversedNames = names.sorted{ $0 > $1 } 当闭包非常长以至于不能在一行中进行书写时,尾随闭包变得非常有用。举例来说,Swift 的 Array 类型有一个 map(:) 方法,这个方法获取一个闭包表达式作为其唯一参数。该闭包函数会为数组中的每一个元素调用一次,并返回该元素所映射的值。具体的映射方式和返回值类型由闭包来指定。下例介绍了如何在 map(:) 方法中使用尾随闭包将 Int 类型数组 [16, 58, 510] 转换为包含对应 String 类型的值的数组[“OneSix”, “FiveEight”, “FiveOneZero”]:123456789101112131415161718let digitNames = [ 0: "Zero", 1: "One", 2: "Two", 3: "Three", 4: "Four", 5: "Five", 6: "Six", 7: "Seven",8: "Eight", 9: "Nine"]let numbers = [16, 58, 510]let strings = numbers.map { (number) -> String in var number = number var output = "" repeat { output = digitNames[number % 10]! + output number /= 10 } while number > 0 return output}print(strings) 字典 digitNames 下标后跟着一个叹号(!),因为字典下标返回一个可选值(optional value),表明该键不存在时会查找失败。在上例中,由于可以确定 number % 10 总是 digitNames 字典的有效下标,因此叹号可以用于强制解包 (force-unwrap) 存储在下标的可选类型的返回值中的String类型的值。 值捕获闭包可以在其被定义的上下文中捕获常量或变量。即使定义这些常量和变量的原作用域已经不存在,闭包仍然可以在闭包函数体内引用和修改这些值。Swift 中,可以捕获值的闭包的最简单形式是嵌套函数,也就是定义在其他函数的函数体内的函数。嵌套函数可以捕获其外部函数所有的参数以及定义的常量和变量。12345678910func makeIncrementer(forIncrement amout: Int) -> () -> Int { var runningTotal = 3 func incrementer() -> Int { runningTotal += amout return runningTotal } return incrementer}var a = makeIncrementer(forIncrement: 5)() 定义一个函数makeIncrementer,返回值为() -> Int,意味着返回值是一个函数,其中嵌套一个函数incrementer。incrementer() 从上下文中捕获了两个值,runningTotal 和 amount。捕获这些值之后,makeIncrementer 将 incrementer 作为闭包返回。每次调用 incrementer 时,其会以 amount 作为增量增加 runningTotal 的值。incrementer() 函数并没有任何参数,但是在函数体内访问了 runningTotal 和 amount 变量。这是因为它从外围函数捕获了 runningTotal 和 amount 变量的引用。捕获引用保证了 runningTotal 和 amount 变量在调用完 makeIncrementer 后不会消失,并且保证了在下一次执行 incrementer 函数时,runningTotal 依旧存在。 闭包是引用类型上面的例子中,incrementBySeven 和 incrementByTen 都是常量,但是这些常量指向的闭包仍然可以增加其捕获的变量的值。这是因为函数和闭包都是引用类型。无论你将函数或闭包赋值给一个常量还是变量,你实际上都是将常量或变量的值设置为对应函数或闭包的引用。上面的例子中,指向闭包的引用 incrementByTen 是一个常量,而并非闭包内容本身。 逃逸闭包]]></content>
</entry>
<entry>
<title><![CDATA[Swift Delegate]]></title>
<url>%2F2017%2F04%2F26%2FSwift-Delegate%2F</url>
<content type="text"><![CDATA[委托(代理)是一种设计模式,它允许类或结构体将一些需要它们负责的功能委托给其他类型的实例。委托模式的实现很简单:定义协议来封装那些需要被委托的功能,这样就能确保遵循协议的类型能提供这些功能。委托模式可以用来响应特定的动作,或者接收外部数据源提供的数据,而无需关心外部数据源的类型。 简单模拟一下tableview的实现:12345678910111213141516171819202122protocol TableViewDelegate { func numberOfRow(row: Int)}class TableView { var delegate: TableViewDelegate? func clickRow() { //检查委托对象是否实现了协议方法,如实现,则调用 delegate?.numberOfRow(row: 3) }}class MyController: TableViewDelegate { func numberOfRow(row: Int) { print("num is \(row)") }}let table = TableView()table.delegate = MyController()table.clickRow() 结果为:1num is 3 换一个马甲,代理传值问题也是同理,从ConB向ConA传递值1234567891011121314151617181920protocol Method { func delevier(Str: String)}class ConA: Method { func delevier(Str: String) { print("str is \(Str)") }}class ConB { var delegate: Method? func click() { delegate?.delevier(Str: "roy") }}let b = ConB()b.delegate = ConA()b.click()]]></content>
</entry>
<entry>
<title><![CDATA[Swift Protocol]]></title>
<url>%2F2017%2F04%2F26%2FSwift-Protocol%2F</url>
<content type="text"><![CDATA[官方概述 协议定义了方法,属性和其它要求的蓝图,以适应特定的任务和功能。协议能够被类、结构体、枚举来实现,满足协议要求的类、枚举、结构体被称为协议的遵守者。翻译官方文档(翻译可能有纰漏) 我的理解: 我们可以定义一些类和方法,只定义不实现,类似于定义了一些接口,但是不处理接口的实现,我们把这些定义的集合暂且称之为协议。类,结构体,枚举等可以遵守这些协议,遵守协议可以获取定义的方法和属性,有一点点类似继承,但是又有很大不同,协议没有方法实现,只有定义,这有点类似于C++的抽象基类(Abstract Class)。简而言之,协议只定义不实现,可以被其它(类,结构体,枚举)遵守和实现。(理解可能会曲解原意) 协议语法定义协议非常简单,有点类似于类、结构体、枚举的定义123protocol SomeProtocol { // Protocol definition goes here} 自定义类型声明采用特定的协议,将协议名称置于类型名之后,用冒号分隔开,作为他们定义的一部分,可以列举多个协议,中间用逗号隔开。123struct SomeStructure: FirstProtocol, AnotherProtocol { // structure definition goes here} 如果一个类继承父类,将父类放在遵守的协议之前,使用逗号分隔。123class SomeStructure: SomeSuperclass, FirstProtocol, AnotherProtocol { // class definition goes here} 属性要求协议要求声明属性声明为变量属性,使用前缀 var 关键字,gettable and settable(可读写) 属性在声明后写 { get set }, gettable 声明类型后写 { get }1234protocol SomeProtocol { var mustBeSettable: Int { get set } var doesNotNeedToBeSettable: Int { get }} 若果是类成员,使用static修饰123protocol AnotherProtocol { static var someTypeProperty: Int { get set }} 定义一个协议,其中有一个属性fullName,定义一个结构体,遵守协议12345678910protocol FullyNamed { var fullName: String { get }}struct Person: FullyNamed { var fullName: String}let john = Person(fullName: "John Appleseed")print(john.fullName) 结果是:1John Appleseed 定义一个更复杂的类,同样遵守FullyNamed协议12345678910111213141516class Starship: FullyNamed { var prefix: String? var name : String init(name: String, prefix: String? = nil) { self.name = name self.prefix = prefix } var fullName: String { return (prefix != nil ? prefix! + " " : "") + name }}var ss1 = Starship(name: "耀国", prefix: "王")print(ss1.fullName)var ss2 = Starship(name: "耀国")print(ss2.fullName) 结果是:12王 耀国耀国 方法要求可以在协议中定义方法,如例所示:123protocol RandomNumberGenerator { func random() -> Double} 类方法使用static修饰:123protocol SomeProtocol { static func someTypeMethod()} 看下面的例子:1234567891011121314class LinearCongruentialGenerator: RandomNumberGenerator { var lastRandom = 42.0 let m = 139968.0 let a = 3877.0 let c = 29573.0 func random() -> Double { lastRandom = ((lastRandom * a + c).truncatingRemainder(dividingBy: m)) return lastRandom / m }}let generator = LinearCongruentialGenerator()print("Here is a random number: \(generator.random()))")print("And another one: \(generator.random())") 突变方法要求方法修改实例类型称为突变方法,在结构体和枚举方法中修改需要给方法加上mutating(class不用添加)123protocol Togglable { mutating func toggle()} 在枚举中实现此协议1234567891011121314enum OnOffSwitch: Togglable { case off, on mutating func toggle() { switch self { case .off: self = .on case .on: self = .off } }}var lightSwitch = OnOffSwitch.offlightSwitch.toggle() 构造要求必须使用required标记构造实现,使用 required 修饰符可以确保所有子类也必须提供此构造器实现,从而也能符合协议。 如果类已经被标记为 final,那么不需要在协议构造器的实现中使用 required 修饰符,因为 final 类不能有子类。关于 final 修饰符的更多内容,请参见防止重写。 1234567891011protocol SomeProtocol { init(someParameter: Int)}class SomeClass: SomeProtocol { required init(someParameter: Int) { // initializer implementation goes here }}let c = SomeClass(someParameter: 12) 如果一个子类重写了父类的指定构造器,并且该构造器满足了某个协议的要求,那么该构造器的实现需要同时标注 required 和 override 修饰符:123456789101112131415protocol SomeProtocol { init()}class SomeSuperClass { init() { }}class SomeSubClass: SomeSuperClass, SomeProtocol { required override init() { }} 协议作为类型尽管协议本身并未实现任何功能,但是协议可以被当做一个成熟的类型来使用。使用场景: 作为函数、方法或构造器中的参数类型或返回值类型 作为常量、变量或属性的类型 作为数组、字典或其他容器中的元素类型 协议是一种类型,因此协议类型的名称应与其他类型(例如 Int,Double,String)的写法相同,使用大写字母开头的驼峰式写法,例如(FullyNamed 和 RandomNumberGenerator)。 协议作为类型使用例子:12345678910111213141516171819202122232425262728293031323334protocol RandomNumberGenerator { func random() -> Double}class LinearCongruentialGenerator: RandomNumberGenerator { var lastRandom = 42.0 let m = 139968.0 let a = 3877.0 let c = 29573.0 func random() -> Double { lastRandom = ((lastRandom * a + c).truncatingRemainder(dividingBy: m)) return lastRandom / m }}class Dice { let sides: Int let generator: RandomNumberGenerator init(sides: Int, generator: RandomNumberGenerator) { self.sides = sides self.generator = generator } func roll() -> Int { return Int(generator.random() * Double(sides)) }}var d = Dice(sides: 6, generator: LinearCongruentialGenerator())for _ in 1...5 { print("Random dice roll is \(d.roll())")} 通过扩展增加协议一致性即便无法修改源代码,依然可以通过扩展令已有类型遵循并符合协议。扩展可以为已有类型添加属性、方法、下标以及构造器,因此可以符合协议中的相应要求。例如为声明一个协议,并使得Dice遵守该协议:123456789101112protocol TextRepresentable { var textualDescription: String { get }}extension Dice: TextRepresentable { var textualDescription: String { return "A \(sides)-sided dice" }}let d2 = Dice(sides: 12, generator: LinearCongruentialGenerator())print(d2.textualDescription) 协议类型的集合协议类型可以在数组或者字典这样的集合中使用,在协议类型提到了这样的用法。下面的例子创建了一个元素类型为 TextRepresentable 的数组:1234let things: [TextRepresentable] = [d, d]for thing in things { print(thing.textualDescription)} 协议的继承协议能够继承一个或多个其他协议,可以在继承的协议的基础上增加新的要求。协议的继承语法与类的继承相似,多个被继承的协议间用逗号分隔:123protocol InheritingProtocol: SomeProtocol, AnotherProtocol {} 类类型专属协议你可以在协议的继承列表中,通过添加 class 关键字来限制协议只能被类类型遵循,而结构体或枚举不能遵循该协议。class 关键字必须第一个出现在协议的继承列表中,在其他继承的协议之前:123protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol {} 协议合成有时候需要同时遵循多个协议,你可以将多个协议采用 SomeProtocol & AnotherProtocol 这样的格式进行组合,称为 协议合成(protocol composition)。你可以罗列任意多个你想要遵循的协议,以与符号(&)分隔。123456789101112131415protocol Named { var name: String { get }}protocol Aged { var age: Int { get }}struct Person: Named, Aged { var name: String var age: Int}func wishHappyBirthday(to celebrator: Named & Aged) { print("Happy birthday, \(celebrator.name), you're \(celebrator.age)!")}let birthdayPerson = Person(name: "Malcolm", age: 21)wishHappyBirthday(to: birthdayPerson) 可选协议要求协议可以定义可选要求,遵循协议的类型可以选择是否实现这些要求。在协议中使用 optional 关键字作为前缀来定义可选要求。可选要求用在你需要和 Objective-C 打交道的代码中。协议和可选要求都必须带上@objc属性。标记 @objc 特性的协议只能被继承自 Objective-C 类的类或者 @objc 类遵循,其他类以及结构体和枚举均不能遵循这种协议。 协议扩展协议可以通过扩展来为遵循协议的类型提供属性、方法以及下标的实现。通过这种方式,你可以基于协议本身来实现这些功能,而无需在每个遵循协议的类型中都重复同样的实现,也无需使用全局函数。12345extension RandomNumberGenerator { func randomBool() -> Bool { return random() > 0.5 }} 通过协议扩展,所有遵循协议的类型,都能自动获得这个扩展所增加的方法实现,无需任何额外修改:]]></content>
</entry>
<entry>
<title><![CDATA[Swift 存储属性、类属性、计算属性]]></title>
<url>%2F2017%2F04%2F25%2FSwift-%E5%AD%98%E5%82%A8%E5%B1%9E%E6%80%A7%E3%80%81%E7%B1%BB%E5%B1%9E%E6%80%A7%E3%80%81%E8%AE%A1%E7%AE%97%E5%B1%9E%E6%80%A7%2F</url>
<content type="text"><![CDATA[存储型属性,主要用来存储值,使用实例访问类属性使用对象直接访问,使用static修饰计算型属性,不能直接存储值,使用get/set来取值和赋值,可以操作其它属性的变化 12345678910111213141516171819202122232425262728293031323334353637class Student { //定义(存储)属性 var name: String? var age: Int = 0 var gender: String? //定义类属性 static var skin: String? //计算属性(get) var doubleAge: Int { return age * 2 } var fiveTime: Int { get { return age * 5 } set(newValue) { age = newValue + 1 } }}let stu = Student()stu.name = "roy"if let name = stu.name { print(name)}stu.age = 13stu.gender = "boy"stu.doubleAgestu.fiveTime = 23stu.fiveTime]]></content>
</entry>
<entry>
<title><![CDATA[Swift Extension]]></title>
<url>%2F2017%2F04%2F24%2FSwift-Extension%2F</url>
<content type="text"><![CDATA[扩展给已经存在的类,结构体,枚举类型和协议增加新的特性。 增加计算实例属性和计算类型属性 定义实例方法和类型方法 提供新的初始化器 定义下标 定义和使用新的内置类型 让一个存在的类型服从一个协议 扩展语法类似Objective-C中的Category,不同的是,Extension没有名字。扩展可以增加新的功能,但是不能覆盖已有的功能。123extension someType { // new functionality to add to SomeType goes here} Extension能拓展存在的类型使其遵守一个或多个协议。123extension someType: SomeProtocol, AnotherProtocol { // implementation of protocol requirements goes here} 计算属性拓展扩展可以给已经存在的类型增加计算实例属性和计算类型属性,如以下扩展了Double类型:1234567891011extension Double { var km: Double { return self * 1_000.0 } var m: Double { return self } var cm: Double { return self / 100.0 } var mm: Double { return self / 1_000.0 } var ft: Double { return self / 3.28084 }}let oneInch = 25.4.mmprint("One inch is \(oneInch) meters")let threeFeet = 3.ftprint("Three feet is \(threeFeet) meters") 构造方法拓展扩展能添加新的构造器,类只能能拓展便利构造期,不能扩展指定构造器。1234567891011121314151617struct Size { var width = 0.0 var height = 0.0}struct Point { var x = 0.0 var y = 0.0}struct Rect { var origin = Point() var size = Size()}let defaultRect = Rect()let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0), size: Size(width: 5.0, height: 5.0)) 我们可以为Rect拓展一个构造期(便利构造期):12345678extension Rect { 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) }} 方法扩展12345678910extension Int { func repetitions(task:() -> Void) { for _ in 0..<self { task() } }}3.repetitions { print("Hello")} 结果为:123HelloHelloHello 可变实例方法扩展扩展增加的实例方法可以修改实例本身。结构体和枚举类型中的方法如果想要修改实例本身或者属性的话需要用mutating来修饰方法,所以扩展这样的方法也需要加mutating。123456789extension Int{ mutating func square() { self = self * self print(self) }}var someInt = 3someInt.square() 结果为:19 下标扩展可以给存在的类型增加新的下标,顺序从右往左:12345678910extension Int { subscript(digitIndex: Int) -> Int { var decimalBase = 1 for _ in 0..<digitIndex { decimalBase *= 10 } return (self / decimalBase) % 10 }}345[0] 结果为:15 内置类型拓展能为现存的类,结构体,枚举添加内置类型下面的例子为Int添加一个新的枚举类型Kind,Kind包括正,零,负三种状态。123456789101112131415161718192021222324252627282930extension Int { enum Kind { case negative, zero, positive } var kind: Kind { switch self { case 0: return .zero case let x where x > 0: return .positive default: return .negative } }}func printIntegerKinds(_ numbers: [Int]) { for number in numbers { switch number.kind { case .negative: print("- ", terminator: "") case .zero: print("0 ", terminator: "") case .positive: print("+ ", terminator: "") } } print("")}printIntegerKinds([3, 19, -27, 0, -6, 0, 7]) 结果为:1+ + - 0 - 0 +]]></content>
</entry>
<entry>
<title><![CDATA[Jinmao Tower Tour]]></title>
<url>%2F2017%2F04%2F24%2FJinmao-Tower-Tour%2F</url>
<content type="text"><![CDATA[上海滩三大神楼,注射器、开瓶器、打蛋器。趁着春年花开,阳光明媚,和小伙伴去了 Jinmao Tower。 从注射器看打蛋器 从注射器看开瓶器 从注射器看东方明珠 从注射器向下俯瞰 站在注射器上的我 坐在注射器的我]]></content>
</entry>
<entry>
<title><![CDATA[Swift Class Struct Enum]]></title>
<url>%2F2017%2F04%2F24%2FSwift-Class-Struct-Enum%2F</url>
<content type="text"><![CDATA[相同点: 三者都可以拥有属性和方法(enum本身不能存储属性,但是可以存储在其关联的信息中,可以有计算属性)。 都可以拥有函数。 class和struct可以拥有自己的构造器。 支持扩展增加功能 可以遵循协议 不同点: 类可以继承。 类可以内省和转化。 struct和enum为值类型,class为引用类型。 class类方法用class关键字声明,enum、struct用static关键字 用mutaing关键词生命要修改struct、enum内容的方法。 结构体适合基本数据类型。 引用类型:(reference types,通常是类)被复制的时候其实复制的是一份引用,两份引用指向同一个对象。值类型:(value types)的每一个实例都有一份属于自己的数据,在复制时修改一个实例的数据并不影响副本的数据。 Enum枚举语法Swift使用enum关键字创建枚举,使用case创建每一个枚举值。和OC不同,Swift枚举不会默认创建分配值。123456enum Student { case pupilStudent case middleStudent case collegeStudent}print(Student.middleStudent) 1结果为:middleStudent 枚举值可以写在同一个case中,使用逗号分割:123enum Teacher { case mathTeacher, chineseTeacher, englishTeacher} 枚举经常和Switch结合使用:123456789var teacher = Teacher.chineseTeacherswitch teacher {case .mathTeacher: print("math teacher")case .chineseTeacher: print("chinese teacher")case .englishTeacher: print("english teacher")} 枚举原始值原始值可以理解为为枚举设置一个具体类型:12345enum Student: String { case pupilStudent = "A" case middleStudent case collegeStudent} 结果为:12AmiddleStudent 如果枚举时Int类型,则会类似于OC,枚举原始值会依次增加:12345678enum Student: Int { case pupilStudent case middleStudent = 13 case collegeStudent}print(Student.pupilStudent.rawValue)print(Student.middleStudent.rawValue)print(Student.collegeStudent.rawValue) 结果为:12301314 枚举相关值(关联值)Swift中的枚举有一个特点,可以设置一些相关值:1234567891011121314enum Student { case pupilStudent(String) case middleStudent(Int, String) case collegeStudent(Int, String)}let stu = Student.collegeStudent(7, "gameing")switch stu {case .pupilStudent(let things): print("is a pupil and \(things)")case .middleStudent(let day, let things): print("is a middle and \(day) days \(things)")case .collegeStudent(let day, let things): print("is college and \(day) days \(things)")} 结果为:1is college and 7 days gameing 枚举递归递归枚举是拥有另一个枚举作为枚举成员关联值的枚举。(递归枚举值必须使用indirect关键字修饰)123456789enum Expression { case num(Int) indirect case add(Expression, Expression) indirect case mul(Expression, Expression)}var exp1 = Expression.num(5)var exp2 = Expression.num(5)var exp3 = Expression.add(exp1, exp2)var exp4 = Expression.mul(exp1, exp3) 如果所有case都是可以递归的:12345indirect enum Expression { case num(Int) case add(Expression, Expression) case mul(Expression, Expression)} Struct结构体定义结构体是由一系列具有相同类型或不同类型的数据构成的数据集合。结构体是一种值类型的数据结构,在Swift中常常使用结构体封装一些属性甚至是方法来组成新的复杂类型,目的是简化运算。我们通过使用关键词 struct 来定义结构体。并在一对大括号内定义具体内容包括他的成员和自定义的方法(是的,Swift中的结构体有方法了),定义好的结构体存在一个自动生成的成员初始化器,使用它来初始化结构体实例的成员属性。废话不多说直接上代码:123456struct Student { var chinese :Int var math: Int var english: Int}let stu = Student(chinese: 89, math: 98, english: 84) 也可以赋初始值:123456struct Student { var chinese :Int = 60 var math: Int = 60 var english: Int = 60}let stu = Student(chinese: 89, math: 98, english: 84) 自定义初始化器123456789101112131415161718192021import Foundationstruct Student { var chinese :Int = 60 var math: Int = 60 var english: Int = 60 init(chinese: Int, math: Int, english: Int) { self.chinese = chinese self.math = math self.english = english } init(stringScore: String) { let cme = stringScore.characters.split(separator: ",") chinese = Int(atoi(String(cme.first!))) math = Int(atoi(String(cme[1]))) english = Int(atoi(String(cme.last!))) }}let stu = Student(chinese: 89, math: 98, english: 84)let stu1 = Student(stringScore: "87,56,83")print(stu1.chinese) 一旦我们自定义了初始化器,系统自动的初始化器就不起作用了,如果还需要使用到系统提供的初始化器,在我们自定义初始化器后就必须显式的定义出来。 定义其它方法如果修改某一个学生的成绩怎么处理,比如修改数学成绩为85:123456789101112struct Student { var chinese :Int = 60 var math: Int = 60 var english: Int = 60 mutating func modifyMathScore(num: Int) { self.math = num }}var stu = Student(chinese: 89, math: 98, english: 84)stu.modifyMathScore(num: 85)print(stu.math) 修改内部变量使用mutating修饰,如果使用使用类方法,使用static修饰函数。]]></content>
</entry>
<entry>
<title><![CDATA[Objective-C Runtime]]></title>
<url>%2F2017%2F04%2F21%2FObjective-C-Runtime%2F</url>
<content type="text"><![CDATA[Objective-C Runtime是一个运行时库(Rumtime Library),可以理解为一个运行时系统。主要使用C语言和汇编语言编写。 Objective-C如何和Runtime打交道? 通过Objective-C源代码(Objective-C Source Code) 通过Foundation框架中的NSObject定义的方法(NSObject Methods) 通过直接调用runtime函数(Runtime Funcations) 消息机制在大部分情况下,运行时系统(Runtime System)能够在幕后自动处理相关任务,我们仅仅需要写Objective-C代码就可以。当我们编译包含Objective-C类和方法的代码时,编译器创建数据结构和函数调用实现语言的动态性特征。其中比较重要的一个运行时机制是消息机制。 消息机制的大致流程为:首先,Runtime系统将方法调用转化为消息发送,会把1[receive message]; 转化为一个消息函数objc_msgSend:1objc_msgSend(receiver, selector) 如果含有参数,则为:1objc_msgSend(receiver, selector, arg1, arg2, ...) 此时方法调用会通过isa指针找到所属的类,然后检查是否存在相关方法,会先检查缓存中是否有方法(为了提高性能,runtime system 会缓存使用到的selector和方法地址),找到就直接调用方法。如果没有找到,会通过super_class查找父类,如果知道NSObject都没有找到,也没有做任何处理,程序会崩溃,为了防止程序崩溃,可以做消息转发。 上面提到了isa指针,isa是什么呢?OC中大部分对象都继承NSObject,NSObject定义:123@interface NSObject <NSObject> { Class isa OBJC_ISA_AVAILABILITY;} 1typedef struct objc_class *Class; 123struct objc_object { Class isa OBJC_ISA_AVAILABILITY;}; 1typedef struct objc_object *id; 我们可以看出Class是指向objc_class的一个结构体指针,而id是一个指向objc_object结构体的指针,其中的isa是一个指向objc_class结构体的指针。其中的id就是我们所说的对象,Class就是我们所说的类。objc_class定义如下:12345678910111213141516struct objc_class { Class isa OBJC_ISA_AVAILABILITY; #if !__OBJC2__ Class super_class OBJC2_UNAVAILABLE; const char *name OBJC2_UNAVAILABLE; long version OBJC2_UNAVAILABLE; long info OBJC2_UNAVAILABLE; long instance_size OBJC2_UNAVAILABLE; struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; struct objc_method_list **methodLists OBJC2_UNAVAILABLE; struct objc_cache *cache OBJC2_UNAVAILABLE; struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; #endif} OBJC2_UNAVAILABLE; 消息转发发送的消息没有被处理,程序就会出错,然而在出错前,运行时机制能给接受对象一个机会处理消息。在错误之前,runtime会发送消息给forwardInvocation:,我们实现forwardInvocation:方法,给消息一个默认的响应即可。 Runtime函数在使用OC编程的时候,大部分不需要直接写Runtime,因为在使用OC的时候,很多内部转化成了Runtime。使用Runtime函数有时能实现某些特殊的效果,下面是runtime常用的函数。runtime常用函数 总结Runtime,在应用中使用广泛,例如消息传递(系统已经帮我们实现),解决字典模型转换问题,快速归档,给对象动态添加变量,动态的添加方法,访问私有变量等。]]></content>
</entry>
<entry>
<title><![CDATA[Swift Runtime]]></title>
<url>%2F2017%2F04%2F20%2FSwift-Runtime%2F</url>
<content type="text"><![CDATA[注:这篇文章原出处来自淘宝技术团队 Objective-C具有动态性,能够通过’runtime API’调用和替换任意方法,那么Swift也具有这些动态性吗? 用例分析我们拿一个纯Swift类和一个继承自NSObject的类来做分析,这两个类里包含尽量多的Swift的类型比如Character、String、AnyObject、Tuple。 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849class TestSwiftClass { var aBool: Bool = true var aInt: Int = 0 var aFloat: Float = 12.45 var aDouble: Double = 12.2342 var aString: String = "abc" var aObject: AnyObject! = nil func testReturnVoidWithaId(aId: UIView) { }}class TestSwiftVC: UIViewController { var aBool: Bool = true var aInt: Int = 0 var aFloat: Float = 12.45 var aDouble: Double = 12.2342 var aString: String = "abc" var aObject: AnyObject! = nil override func viewDidLoad() { super.viewDidLoad() } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) } func testReturnVoidWithaId(aId: UIView) { } func testRetrunVoidWithBool(aBool: Bool, aInteger: Int, aFloat: Float, aDouble: Double, aString: String, aObject: AnyObject) { } func testReturnTuple(aBool: Bool, aInteger: Int, aFloat: Float) -> (Bool, Int, Float) { return (aBool, aInteger, aFloat) } func testReturnVoidWithCharacter(aCharacter: Character) { } func tableView(tableView: UITableView, numberOfRowsSection: Int) -> Int { return 20 }} 方法、属性动态性比较重要的一点就是能够拿到某个类所有的方法、属性,我们使用如下代码来打印方法和属性列表。1234567891011121314151617181920212223242526func showClsRuntime(cls: AnyClass) { print("start methodList") var methodNum: UInt32 = 0 let methodList = class_copyMethodList(cls, &methodNum) for index in 0..<numericCast(methodNum) { let method: Method = methodList![index]! print(String(_sel: method_getName(method))) } print("end methodList") print("start propertyList") var propertyNum: UInt32 = 0 let propertyList = class_copyPropertyList(cls, &propertyNum) for index in 0..<numericCast(propertyNum) { let property: objc_property_t = propertyList![index]! print(String.init(utf8String: property_getName(property)) ?? "") } print("end propertyList")}let aSwifterClass : TestSwiftClass = TestSwiftClass()showClsRuntime(cls: object_getClass(aSwifterClass))print("\n")let aSwiftVC: TestSwiftVC = TestSwiftVC()showClsRuntime(cls: object_getClass(aSwiftVC)) 执行上面代码结果:12345678910111213141516171819202122232425262728293031323334353637start methodListend methodListstart propertyListend propertyListstart methodList.cxx_destructinitWithNibName:bundle:viewDidAppear:viewDidLoadinitWithCoder:aBoolsetABool:aIntsetAInt:aFloatsetAFloat:aDoublesetADouble:aStringsetAString:aObjectsetAObject:testReturnVoidWithaIdWithAId:testRetrunVoidWithBoolWithABool:aInteger:aFloat:aDouble:aString:aObject:tableViewWithTableView:numberOfRowsSection:end methodListstart propertyListaBoolaIntaFloataDoubleaStringaObjectend propertyList 结论: 对于纯Swift的TestSwiftClass来说任何方法、属性都未获取到 对于TestSwiftVC,除了testReturnTuple,testReturnVoidWithCharacter两个方法外,其它都获得成功。原因: 纯Swift类的函数调用已经不再是Objective-c的运行时发消息,而是类似C++的vtable,在编译时就确定了调用哪个函数,所以没法通过runtime获取方法、属性。TestSwiftVC继承自UIViewController,基类为NSObject,而Swift为了兼容Objective-C,凡是继承自NSObject的类都会保留其动态性,所以我们能通过runtime拿到他的方法。testReturnTuple,testReturnVoidWithCharacter获取不到是因为:从Objective-c的runtime 特性可以知道,所有运行时方法都依赖TypeEncoding,而Character和Tuple是Swift特有的,无法映射到OC的类型,更无法用OC的typeEncoding表示,也就没法通过runtime获取了。 Method Swizzing动态性最常用的就是方法替换(Method Swizzling),将类的某个方法替换成自定义的方法,从而达到hook的作用。 对于纯Swift类(如TestSwiftClass)来说,无法通过objc runtime替换方法,因为由上面的测试可知拿不到这些方法、属性 对于继承自NSObject类(如TestSwiftVC)来说,无法通过runtime获取到的方法肯定没法替换了。那能通过runtime获取到的方法就都能被替换吗?我们测一把。我们替换两个可以被runtime获取到的方法:viewDidAppear和testReturnVoidWithaId替换代码:123456789101112func methodSwizze(cls: AnyClass, originalSelector: Selector, swizzledSelector: Selector) { let originalMethod = class_getInstanceMethod(cls, originalSelector) let swizzledMethod = class_getInstanceMethod(cls, swizzledSelector) let didAddMethod = class_addMethod(cls, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)) if didAddMethod { class_replaceMethod(cls, swizzledSelector ,method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)) } else { method_exchangeImplementations(originalMethod, swizzledMethod) }} viewDidAppear可以替换掉,testReturnVoidWithaId不能替换,通过代码调试,可以看到viewDidAppear会有@objc标识,testReturnVoidWithaId没有标识。@objc用来做什么的?与动态性有关吗?@objc是用来将Swift的API导出给Objective-C和Objective-C runtime使用的,如果你的类继承自Objective-c的类(如NSObject)将会自动被编译器插入@objc标识。我们在把TestSwiftClass(纯Swift类)的方法、属性前都加个@objc 试试,如图:123456789101112class TestSwiftClass { @objc var aBool: Bool = true @objc var aInt: Int = 0 @objc var aFloat: Float = 12.45 @objc var aDouble: Double = 12.2342 @objc var aString: String = "abc" @objc var aObject: AnyObject! = nil @objc func testReturnVoidWithaId(aId: UIView) { }} 查看日志可以发现加了@objc的方法、属性均可以被runtime获取到了。123456789101112131415161718192021222324start methodListaBoolsetABool:aIntsetAInt:aFloatsetAFloat:aDoublesetADouble:aStringsetAString:aObjectsetAObject:testReturnVoidWithaIdWithAId:end methodListstart propertyListaBoolaIntaFloataDoubleaStringaObjectend propertyList 加了@objc标识的方法、属性无法保证都会被运行时调用,因为Swift会做静态优化。要想完全被动态调用,必须使用dynamic修饰。使用dynamic修饰将会隐式的加上@objc标识,这也就解释了为什么testReturnVoidWithaId无法被替换,因为写在Swift里的代码直接被编译优化成静态调用了。而viewDidAppear是继承Objective-C类获得的方法,本身就被修饰为dynamic,所以能被动态替换。我们把TestSwiftVC方法前加上dynamic再测一把,从堆栈也可以看出,方法的调用前增加了@objc标识,testReturnVoidWithaId方法被替换成功了。 总结 纯Swift类没有动态性,但在方法、属性前添加dynamic修饰可以获得动态性。 继承自NSObject的Swift类,其继承自父类的方法具有动态性,其他自定义方法、属性需要加dynamic修饰才可以获得动态性。 若方法的参数、属性类型为Swift特有、无法映射到Objective-C的类型(如Character、Tuple),则此方法、属性无法添加dynamic修饰(会编译错误) Swift类在Objective-C中会有模块前缀]]></content>
</entry>
<entry>
<title><![CDATA[Swift Key-Value Observing]]></title>
<url>%2F2017%2F04%2F20%2FSwift-Key-Value-Observing%2F</url>
<content type="text"><![CDATA[KVO(Key-Value Observing)是为了其他不同实例对当前的某个属性 (严格来说是 keypath) 进行监听时使用的。其他实例可以充当一个订阅者的角色,当被监听的属性发生变化时,订阅者将得到通知。这是一个很强大的属性,通过 KVO 我们可以实现很多松耦合的结构,使代码更加灵活和强大:像通过监听 model 的值来自动更新 UI 的绑定这样的工作,基本都是基于 KVO 来完成的。在 Swift 中我们也是可以使用 KVO 的,但是仅限于在 NSObject 的子类中。这是可以理解的,因为 KVO 是基于 KVC (Key-Value Coding) 以及动态派发技术实现的,而这些东西都是 Objective-C 运行时的概念。另外由于 Swift 为了效率,默认禁用了动态派发,因此想用 Swift 来实现 KVO,我们还需要做额外的工作,那就是将想要观测的对象标记为 dynamic。 官方代码123456789101112131415161718192021222324252627282930313233class MyObjectToObserve: NSObject { dynamic var myDate = NSDate() func updateDate() { myDate = NSDate() }}private var myContext = 0class MyObserver: NSObject { var objectToObserve = MyObjectToObserve() override init() { super.init() objectToObserve.addObserver(self, forKeyPath: #keyPath(MyObjectToObserve.myDate), options: .new, context: &myContext) } override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { if context == &myContext { if let newValue = change?[.newKey] { print("Date changed: \(newValue)") } } else { super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) } } deinit { objectToObserve.removeObserver(self, forKeyPath: #keyPath(MyObjectToObserve.myDate), context: &myContext) }}let obj = MyObserver()obj.objectToObserve.updateDate() 首先是 Swift 的 KVO 需要依赖的东西比原来多。在 Objective-C 中我们几乎可以没有限制地对所有满足 KVC 的属性进行监听,而现在我们需要属性有 dynamic 进行修饰。大多数情况下,我们想要观察的类不一定是 dynamic 修饰的 (除非这个类的开发者有意为之,否则一般也不会有人愿意多花功夫在属性前加上 dynamic,因为这毕竟要损失一部分性能),并且有时候我们很可能也无法修改想要观察的类的源码。遇到这样的情况的话,一个可能可行的方案是继承这个类并且将需要观察的属性使用 dynamic 进行重写。比如刚才我们的 MyObjectToObserve 中如果 date 没有 dynamic 的话,我们可能就需要一个新的 OtherMyObjectToObserve 了:1234567891011121314151617class MyObjectToObserve: NSObject { var myDate = NSDate() func updateDate() { myDate = NSDate() }}class OtherMyObjectToObserve: MyObjectToObserve { dynamic override var myDate: NSDate { get { return super.myDate } set { super.myDate = newValue } }} KVO代码使用的是苹果官方示例]]></content>
</entry>
<entry>
<title><![CDATA[Swift Key-Value Coding]]></title>
<url>%2F2017%2F04%2F20%2FSwift-Key-Value-Coding%2F</url>
<content type="text"><![CDATA[KVC (Key-Value Coding), 它是一种用间接方式访问类的属性的机制。在 Swift 中为一个类实现 KVC 的话,需要让它继承自 NSObject。 Code12345678910class Person: NSObject { var firstName: String var lastName: String init(firstName: String, lastName: String) { self.firstName = firstName self.lastName = lastName }} let roy = Person(firstName: "roy", lastName: "wang") 直接引用属性: 1print(roy.lastName) 通过KVC机制访问:123if let newValue = roy.value(forKey: "lastName") { print(newValue)} 如果使用KVC获取一个不存在的key,会导致程序崩溃。找不到key,会调用valueForUndefinedKey,我们可以重新此方法:12345678910111213class Person: NSObject { var firstName: String var lastName: String init(firstName: String, lastName: String) { self.firstName = firstName self.lastName = lastName } override func value(forUndefinedKey key: String) -> Any? { return "" }} KVC 除了可以用单个的 key 来访问单个属性,还提供了一个叫做 keyPath 的东西。所谓 keyPath,就比如你的属性本身也有自己的属性,那么想引用这个属性,就需要用到 keyPath。咱们用一个示例来说明:12345678910111213141516171819class Province: NSObject { var proName: String init(proName: String) { self.proName = proName }}class Country: NSObject { var conName: String var conProvince: Province init(conName: String, conProvince: Province) { self.conName = conName self.conProvince = conProvince }}var con = Country(conName: "CN", conProvince: Province(proName: "HenanProvince"))print(con.value(forKeyPath: "conProvince.proName")) KVC 定义了使用 valueForKey获取属性,同样也提供了设置属性的方法 setValue:forKey1con.setValue("US", forKey: "conName") 标量值所谓标量值(Scalar Type)就是简单的类型的属性,比如int,float,这些非对象属性,关于标量值在KVC中有些地方需要注意:12345678910111213class Person: NSObject { var firstName: String var lastName: String var age: Int init(firstName: String, lastName: String, age: Int) { self.firstName = firstName self.lastName = lastName self.age = age }}let roy = Person(firstName: "roy", lastName: "wang", age: 14)roy.setValue(nil, forKey: "age") age是一个简单标量值,标量值不能为nil,虽然setValue 方法可以接受任何类型的参数作为值的设置,但 age 的底层存储确实标量值,因此我们执行上面那条 setValue 语句的时候必然会造成程序的崩溃。那么我们除了注意避免将 nil 传递给底层存储是标量类型的属性之外,还有没有其他方法呢?KVC 为我们提供了一个 setNilValueForKey 方法,每当我们要将 nil 设置给一个 key 的时候,这个方法就会被调用,所以我们可以修改一下 Person 类的定义: 1234567891011121314151617class Person: NSObject { var firstName: String var lastName: String var age: Int init(firstName: String, lastName: String, age: Int) { self.firstName = firstName self.lastName = lastName self.age = age } override func setNilValueForKey(_ key: String) { if key == "age" { self.setValue(18, forKey: "age") } }} FAQ实现KVC,为什么要继承 NSObject KVC 机制是由一个协议 NSKeyValueCoding 定义的。NSObject 帮我们实现了这个协议,所以 KVC 核心的逻辑都在 NSObject 中,我们继承 NSObject 才能让我们的类获得 KVC 的能力。(理论上说,如果你遵循 NSKeyValueCoding 协议的接口,其实也可以自己实现 KVC 的细节,完全行得通。]]></content>
</entry>
<entry>
<title><![CDATA[Swift Optional]]></title>
<url>%2F2017%2F04%2F20%2FSwift-Optional%2F</url>
<content type="text"><![CDATA[Optional定义Optional是在Swift中引入的新类型,特点是可以有值,也没有没有值,没有值为nil。Optional其实就是一个枚举: 1234567891011public enum Optional<Wrapped> : ExpressibleByNilLiteral { case none case some(Wrapped) public init(_ some: Wrapped) public func map<U>(_ transform: (Wrapped) throws -> U) rethrows -> U? public func flatMap<U>(_ transform: (Wrapped) throws -> U?) rethrows -> U? public init(nilLiteral: ()) public var unsafelyUnwrapped: Wrapped { get }} 当Optional没有值时,返回的nil其实就是Optional.None,即没有值。除了None以外,还有一个Some,当有值时就是被Some包装的真正的值,所以我们拆包的动作其实就是将Some里面的值取出来。 Optional使用我们可以声明一个 Optional 的 String 类型的变量,只需要在变量定义的时候在类型后面加上一个 ? 1var optionalString: String? 在引用的时候,我们需要强制拆包,在变量后面添加一个 ! ,相当于告诉编译器,我确信这个变量不是 nil,可以直接使用(当然,使用强制解包只代表你自己确认它不为 nil,但它还是有可能为 nil 的,如果这样的情况发生,依然会造成程序运行时崩溃)。1print(optionalString!) 使用 if let 这样的语法就可以更加安全的操作 Optional 值。只有在 name 中的值不为 nil 的时候,nameValue 变量才会被初始化成功。 这样我们的 print 语句就不会因为 nil 而崩溃。相比使用强制解包,更加安全和优雅的方式是使用 Optional Chaining:123if let newValue = optionalString { print(newValue)} Optional Chaning 陷阱123456789101112struct Name { var firstName: String = "" var lastName: String = ""}struct Person { var name: Name? var age: Int}var person: Person? = Person(name: Name(firstName: "roy", lastName: "wang"), age: 25)print(person?.name?.firstName) 上面的输入结果是:1Optional("roy") firstName不是可选类型,为什么输出结果是这样呢。因为在表达式中只要有一个是可选值,整个表达式结果都是Optional。如果person为nil,后面的引用就没有了意义,所以我们需要对这个表达式做处理。正确的引用方式应该是这样的:123if let firstName = person?.name?.firstName { print(firstName)} Optional Chaning 作为返回值也需要注意,如下:123func getName(person: Person) -> String { return person.name?.firstName} 上面的代码不会通过编辑,因为返回值是Optional Chaning,所以下面的才是正确的打开方式:123func getName(person: Person) -> String? { return person.name?.firstName} 实际应用123456789class WebViewDelegate :NSObject, UIWebViewDelegate { func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool { if let absURL = request.URL?.absoluteString { // do something.. return false } return true }} 不要天真的这样使用,编译会出错的:12if request.URL?.absoluteString == "xxx" {} 为什么要有Optional因为Swift是强类型语言。引入 Optional 机制是为了「更严格的」类型检查,使得在编译时可以发现更多问题(未预期的变量为空)。]]></content>
</entry>
<entry>
<title><![CDATA[Swift Singleton]]></title>
<url>%2F2017%2F04%2F19%2FSwift-Singleton%2F</url>
<content type="text"><![CDATA[单例必须是唯一的,在程序生命周期中只能存在一个这样的实例。为保证单例的唯一性,单例类的初始化方法必须是私有的。这样就可以避免其他对象通过单例类创建额外的实例。为保证在整个程序的生命周期中值有一个实例被创建,单例必须是线程安全的。 在Swift中单例实现比较简单:1234class TheOneAndOnly { static let shareInstance = TheOneAndOnly() private init() {}} 全局变量(还有结构体和枚举体的静态成员)的Lazy初始化方法会在其被访问的时候调用一次。类似于调用’dispatch_once’以保证其初始化的原子性。这样就有了一种很酷的’单次调用’方式:只声明一个全局变量和私有的初始化方法即可。]]></content>
</entry>
<entry>
<title><![CDATA[Swift GCD]]></title>
<url>%2F2017%2F04%2F19%2FSwift-GCD%2F</url>
<content type="text"><![CDATA[GCDGrand Central Dispatch (GCD)是Apple开发的一个多核编程的较新的解决方法。它主要用于优化应用程序以支持多核处理器以及其他对称多处理系统。它是一个在线程池模式的基础上执行的并行任务。在Mac OS X 10.6雪豹中首次推出,也可在IOS 4及以上版本使用。 基本概念 进程:正在进行中的程序被称为进程,负责程序运行的内存分配;每一个进程都有自己独立的虚拟内存空间 线程:线程是进程中一个独立的执行路径(控制单元);一个进程中至少包含一条线程,即主线程 队列:是管理线程的,相当于线程池,能管理线程什么时候执行。无论什么队列和任务,线程的创建和回收不需要程序员操作,由队列负责。 串行队列:队列中的任务只会顺序执行。 并行队列:队列中的任务通常会并发执行。 全局队列:是系统的,与并行队列类似,但调试时,无法确认操作所在队列 主队列:每一个应用程序对应唯一一个主队列,在多线程开发中,使用主队列更新UI。 串行队列同步:操作不会新建线程、操作顺序执行。 串行队列异步:操作需要一个子线程,会新建线程、线程的创建和回收不需要程序员参与,操作顺序执行,“最安全的选择”。 并行队列同步:操作不会新建线程、操作顺序执行。 并行队列异步:操作会新建多个线程(有多少任务,就开N个线程执行)、操作无序执行;队列前如果有其他任务,会等待前面的任务完成之后再执行;场景:既不影响主线程,又不需要顺序执行的操作! 全局队列异步:操作会新建多个线程、操作无序执行,队列前如果有其他任务,会等待前面的任务完成之后再执行。 全局队列同步:操作不会新建线程、操作顺序执行。 主队列:操作在主线程上顺序执行,不存在异步。 图说概念 串行队列 并行队列 全局队列 同步 不创建线程,顺序执行 不创建线程,顺序执行 不创建线程,顺序执行 异步 创建一个线程,顺序执行 创建多个线程,并发执行 创建多个线程,并发执行 代码示例在我们使用多线程的时候,主要是处理并发事件,使用下面的代码能够解决大部分场景:12345678DispatchQueue.global().async { print("开始执行异步任务") Thread.sleep(forTimeInterval: 2) print("异步任务执行完毕") DispatchQueue.main.async { print("回到UI线程") }}]]></content>
</entry>
<entry>
<title><![CDATA[MarkDown File]]></title>
<url>%2F2017%2F04%2F18%2FMarkDown-File%2F</url>
<content type="text">< 效果如下:这是百度首页 图片1 效果如下: 字体1# 字体1, ## 字体2, ### 字体3,#### 字体4,##### 字体5,###### 字体6 效果如下: 字体1,字体2字体3字体4字体5字体6斜体1*这是斜体* 效果如下:这是斜体 无序列表12* this one* this two 效果如下: this one this two 有序列表121. this is order one 2. this is order two 效果如下: this is order one this is order two 脚注1[^content] 效果如下:[^mark] : 《百科全书》 行内代码1这是行内`行内`代码 效果如下:这是行内行内代码 代码块效果如下:1这是代码块 删除线1~~删除~~ 效果如下:删除]]></content>
</entry>
<entry>
<title><![CDATA[Swift Define]]></title>
<url>%2F2017%2F04%2F18%2FSwift-Define%2F</url>
<content type="text"><![CDATA[宏定义合理使用能够使代码漂亮简洁,但是使用宏定义有时也会导致难以重构和维护,隐藏很多问题。在Swift中已经去掉了宏定义,并且苹果公司给了一些替代建议,比如使用let和get代替原来的宏定义。 没有参数的宏在OC中 12#define kScreenHeight [UIScreen mainScreen].bounds.size.height#define kScreenWidth [UIScreen mainScreen].bounds.size.width 在Swift中 12let kScreenHeight = UIScreen.mainScreen().bounds.size.heightlet kScreenWidth = UIScreen.mainScreen().bounds.size.width 接收参数的宏在OC中 1#define RGBCOLOR(r,g,b) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:1] 在Swift中 1234func RGBCOLOR(_ r:CGFloat,_ g:CGFloat,_ b:CGFloat) -> UIColor{ return UIColor(red: (r)/255.0, green: (g)/255.0, blue: }]]></content>
</entry>
<entry>
<title><![CDATA[Swift Lock]]></title>
<url>%2F2017%2F04%2F18%2FSwift-Lock%2F</url>
<content type="text"><![CDATA[在不同线程中安全的访问同一资源,需要为资源加上线程锁,否则多个线程访问同一资源,会导致结果不可预测。在OC中使用@synchronized为资源加锁,但是在Swift中这个方法已经不存在了。@synchronized幕后所做的工作是调用objc_sync中的objc_sync_enter和objc_sync_exit方法。在Swift中lock一个变量可以使用:12345func myMethod(anObj: AnyObject!) { objc_sync_enter(anObj) //在enter 和 exit 之间 anObj不会被其它线程改变 objc_sync_exit(anObj)} 加锁和解锁都需要消耗性能,我们不可能为所有方法都加上锁,并且在App中涉及多线程部分有限,也没有必要为所有东西加锁。过多的锁没有意义,不但会消耗性能,也有可能会导致死锁问题。在处理多线程时,尽量保持简单。 Swift Lock]]></content>
</entry>
<entry>
<title><![CDATA[Swift Add Variable For A Exist Class]]></title>
<url>%2F2017%2F04%2F18%2FSwift-Add-Variable-For-A-Exist-Class%2F</url>
<content type="text"><![CDATA[在Swift中如何为一个存在的类添加变量,新建一个Person.swift文件,代码如下:12345678import Foundationclass Person: NSObject { var name: NSString? func eatFood() -> Void { print("People can eat") }} 现在新建PersonExtension.swift,代码如下: 123456789101112131415import Foundationprivate var key: Void?extension Person { var gender: String? { get { return objc_getAssociatedObject(self, &key) as? String } set { return objc_setAssociatedObject(self, &key, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } }} 现在我们则为Person新添加了一个变量,可以使用一下代码访问新变量 1234let per = Person()per.name = "roy"per.gender = "boy"per.eatFood()]]></content>
</entry>
<entry>
<title><![CDATA[Swift Navi Title Offset]]></title>
<url>%2F2017%2F04%2F18%2FSwift-Navi-Title-Offset%2F</url>
<content type="text"><![CDATA[在iOS中,导航栏的标题有时不居中显示,问题的原因是前一个导航页面标题太长,进入下一个界面的时候,下个界面的标题会向后偏移,导致标题不居中显示。问题的解决方案有多种,在此说一下我的解决方案,因为项目中有基类控制器,在基类控制器的viewDidLoad方法中,添加以下代码:1234567891011NSArray *viewControllerArray = [self.navigationController viewControllers];long previousViewControllerIndex = [viewControllerArray indexOfObject:self] - 1;UIViewController *previous;if (previousViewControllerIndex >= 0) { previous = [viewControllerArray objectAtIndex:previousViewControllerIndex]; previous.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:self action:nil];}]]></content>
</entry>
<entry>
<title><![CDATA[Swift Generic Base]]></title>
<url>%2F2017%2F04%2F17%2FSwift-Generic-Base%2F</url>
<content type="text"><![CDATA[Swift创始人Chirs Lattner: 1Swift引入泛型和函数式编程的思想,极大地扩展了设计的空间。 什么是泛型?以下面两个例子说明什么是泛型 两值是否相等问题 两值交换问题 两值是否相等问题判断两值是否相等我们的常规思路如下: 123456789func equalInt(numOne: Int, numbTwo: Int) -> Bool { return numOne == numbTwo}equalInt(numOne: 4, numbTwo: 4)func equalString(strOne: String, strTwo: String) -> Bool { return strOne == strTwo}equalString(strOne: "roy", strTwo: "note") 我们需要为每一种类型写一种方法,一句话总结就是写的很不爽,下面就该泛型登场了 123456789func isEquals<T: Comparable>(a: inout T, b: inout T) -> Bool { return (a == b)}var num1 = 5var num2 = 5var str1 = "wyg"var str2 = "roy"isEquals(a: &num1, b: &num2)isEquals(a: &str1, b: &str2) 并不是所有的类型都具有可比性,必须遵守Comparable协议,在Swift中,基本数据类型和字符串都遵守Comparable协议。 两值交换问题有了上面例子铺垫,直接使用泛型来实现 12345678910func swapTwoValue<T>(a: inout T, b: inout T) { let tempValue = a a = b b = tempValue}var oneStr = "hello"var twoStr = "world"swapTwoValue(a: &oneStr, b: &twoStr)print(oneStr,twoStr) 类型参数在上面的泛型函数例子中,占位符T是类型参数的一个例子。类型参数指定并命名一个占位符类型,并用<>包裹,放在函数名后面。一旦一个参数类型确定,就可以指定参数类型,或者返回值的类型,还可以用作函数体的注释类型。在调用的时候会被实际的类型替代,如传递的是Int,就替换为Int,如果传入的是Double类型就替换为Double等等]]></content>
</entry>
<entry>
<title><![CDATA[Swift Underline]]></title>
<url>%2F2017%2F04%2F17%2FSwift-UnderLine%2F</url>
<content type="text"><![CDATA[在Swift中下划线用途多样,下面列举几种常见用法。 1.格式化数字字面量提高数字字面量的可读性。1let oneMillion = 1_000_000 //(is equal to 1,000,000) 2.忽略元组元素值使用元组时,有元素不必使用时,可以使用下划线将元素忽略。12let http404Error = (404, "Not Found")let(_, errorMessage) = http404Error 3.忽略区间值123456let base = 3let power = 10var answer = 1for _ in 1...power { answer *= base} 4.忽略外部参数名123456789func cal(num1: Int, num2: Int) -> Int { return num1 + num2}cal(num1: 15, num2: 15)func sum(_ num1: Int, _ num2: Int) -> Int { return num1 + num2}sum(15, 15) 参考:@Author:twlkyao]]></content>
</entry>
<entry>
<title><![CDATA[My Family]]></title>
<url>%2F2017%2F04%2F14%2FMy-Family%2F</url>
<content type="text"><![CDATA[用博客存储图片也不错哦。 1能认出我的人,奖励一颗超级无敌泡泡糖。 1杭州爬山。 1和家里小伙伴的合影 1这张是全家福 1这张拍于某年某月某一天的照片]]></content>
</entry>
<entry>
<title><![CDATA[iOS Books]]></title>
<url>%2F2017%2F04%2F13%2FiOS-Books%2F</url>
<content type="text"><![CDATA[刚才整理电脑,在某个很久很久没被我光顾的角落里,静静地躺着几本书。虽然这些书稍微有点落后,并且从网上也能找到相关资源,但是也厚着脸皮拿出来和大家共享一下吧,书不多,等有时间,把相关题材的书籍再统一整理,后续更新一下。 iOS开发指南:从零基础到APP Store上架,第二版)iOS网络编程与云端应用最佳实践企业级iOS应用开发实战object_c编程之道iOS设计模式解析]]></content>
</entry>
<entry>
<title><![CDATA[Python Books]]></title>
<url>%2F2017%2F04%2F13%2FPython-Books%2F</url>
<content type="text"><![CDATA[在此和大家分享几本和Python相关的书籍,其中有基本教程,核心教程,也有和网络爬虫和黑客相关的书籍。 Python网络数据采集汉化版Python网络数据采集英文版Python基础教程,第二版Python核心教程,第三版Python绝技,运用python成为顶级黑客,汉化版Python绝技,运用python称为顶级黑客,英文版]]></content>
</entry>
<entry>
<title><![CDATA[Swift Start]]></title>
<url>%2F2017%2F04%2F13%2FSwift-Start%2F</url>
<content type="text"><![CDATA[Swift语言,是苹果在2014年WWDC发布的新开发语言,是未来趋势,将来必定会替代Objective-C。但是新事物的成长历程历来是前途是光明的,道路是曲折的,所以目前开发者还是主要以使用Objective-C为主,作为一名开发者,对于新技术应该是趋之若鹜,因为公司一直使用的Objective-C语言,再加上自己的一点懒惰之心,Swift的学习到目前为止只看了官方文档,并没有进行深入学习和项目实践,为了牢记落后就没有工资的历史遗训,决定好好学习Swift语言。 有人说,苹果公司推出Swift语言,是为了弥补Objective-C的技术债,我对这样的说辞以前从不以为然,心里暗暗嘀咕,这肯定是苹果公司放出的风声,为了鼓励开发者使用Swift语言。但是身边的朋友使用Swift的渐渐多了起来,他们的反馈一致是“我已经回不了头了,Swift真的是太方便了”。我只能仰天长叹,此时不学,更待何时。 一些新事物刚出来,我们不以为然,认为不重要,但是当你真正接触了之后,就再也难以忍受失去它,就比如当时ARC推出来很多人不接受,现在大家都在使用ARC。新事物有不稳定性,脆弱性,很容易夭折,但是新事物也往往孕育着更大的生命力。1Result:近期我要深入研究Swift语言。]]></content>
</entry>
<entry>
<title><![CDATA[Hello World]]></title>
<url>%2F2017%2F04%2F12%2Fhello-world%2F</url>
<content type="text"><![CDATA[对于一枚码农,刚接触编程的时候,写的第一行代码,很有可能是打印“Hello World”,今天使用Github+Hexo实现了一个简易的博客,就用Hello World和大家打声招呼吧。 为什么要开通博客? 记录,好记性不如写博客,不管是平时的学习笔记,突然来的灵感,还是思考的一些心得体会,都可以记录下来,方便以后查阅。 分享,写日记是私有行为,写博客公有属性,把自己的一些经历或经验和大家一起分享,说不定你的分享对于某一个人大有裨益。 交流,把自己的思想和大家分享,别人对此评头论足一定是好事,人有局限性,汇聚更多的思想,才能将局限缩小。 有这么多开放平台,为什么要自己动手搭建博客? 自己动手,丰衣足食。 作为技术开发,没有自己的博客,总感觉人生缺少点什么。 有很多开源的博客,搭建博客的时间成本,技术成本,经济成本都很低。 可以装逼,这点很重要。]]></content>
</entry>
</search>