https://developer.apple.com/videos/play/wwdc2015/408/?time=2295
protocol Ordered {
func precedes(other: Self) -> Bool
}
// Protocol 'Ordered' can only be used as a generic constraint
// because it has Self or associated type requirements
func binarySearch(_ sortedKeys: [T], forKey k: T) -> Int {
var lo = 0
var hi = sortedKeys.count
while hi > lo {
let mid = lo + (hi - lo) / 2
if sortedKeys[mid].precedes(other: k) { lo = mid + 1 }
else { hi = mid }
}
return lo
}
extension Ordered where Self: Comparable {
func precedes(other: Self) -> Bool {
return self < other
}
}
extension Int: Ordered { }
extension String: Ordered { }
let array = [1, 2, 3, 4, 5, 6, 7]
print(binarySearch(array, forKey: 4)) // 3
1.Ordered协议中precedes方法other的类型Self,这是一种Self-requirement 用法,Int类型遵守了Ordered协议,在调用precedes方法是,Self自动变为Int,String类型同理
2.使用Self-requirement 后,Ordered只能用于泛型约束
3.使用Self-requirement,模型在交互上自由性更高,静态分配,优化性更好
// 渲染 可以看做是一种能力
protocol Renderer {
func moveTo(_ p: CGPoint)
func lineTo(_ p: CGPoint)
func arcAt(center: CGPoint, radius: CGFloat,
startAngle: CGFloat, endAngle: CGFloat)
}
// CGContext遵守了Renderer协议,就拥有了描绘的能力,
extension CGContext : Renderer {
func moveTo(_ p: CGPoint) {
moveTo(p)
}
func lineTo(_ p: CGPoint) {
addLine(to: p)
}
func arcAt(center: CGPoint, radius: CGFloat,
startAngle: CGFloat, endAngle: CGFloat) {
let arc = CGMutablePath()
addArc(center: center, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: true)
self.addPath(arc)
}
}
struct TestRenderer : Renderer {
func moveTo(_ p: CGPoint) { print("moveTo(\(p.x), \(p.y))") }
func lineTo(_ p: CGPoint) { print("lineTo(\(p.x), \(p.y))") }
func arcAt(center: CGPoint, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat) {
print("arcAt(\(center), radius: \(radius)," + " startAngle: \(startAngle), endAngle: \(endAngle))")
}
}
// 比例渲染
struct ScaledRenderer: Renderer {
let base: Renderer
let scale: CGFloat
func moveTo(_ p: CGPoint) {
base.moveTo(CGPoint(x: p.x * scale, y: p.y * scale))
}
func lineTo(_ p: CGPoint) {
base.lineTo(CGPoint(x: p.x * scale, y: p.y * scale))
}
func arcAt(center: CGPoint, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat) {
// 拦截arcAt方法,修改center,调用base协议的方法
let scaledCenter = CGPoint(x: center.x * scale, y: center.y * scale)
base.arcAt(center: scaledCenter, radius: radius * scale, startAngle: startAngle, endAngle: endAngle)
}
}
// 比例
struct Scaled : Drawable {
var scale: CGFloat
var subject: Base
func draw(renderer: Renderer) {
subject.draw(renderer: ScaledRenderer(base: renderer, scale: scale))
}
}
// 绘画能力
protocol Drawable {
func draw(renderer: Renderer)
}
// 多边形
struct Polygon : Drawable {
var corners: [CGPoint] = []
func draw(renderer: Renderer) {
renderer.moveTo(corners.last!)
for p in corners {
renderer.lineTo(p)
}
}
}
// 圆形
struct Circle : Drawable {
var center: CGPoint
var radius: CGFloat
func draw(renderer: Renderer) {
renderer.arcAt(center: center, radius: radius,
startAngle: 0.0, endAngle: CGFloat(2 * M_PI))
}
}
// 图表
struct Diagram : Drawable {
var elements: [Drawable] = []
func draw(renderer: Renderer) {
for f in elements {
f.draw(renderer: renderer)
} }
}
------测试------
let circle = Circle(center:CGPoint(x: 187.5, y: 333.5),radius: 93.75)
let triangle = Polygon(corners: [CGPoint(x: 187.5, y: 427.25),CGPoint(x: 268.69, y: 286.625),CGPoint(x: 106.31, y: 286.625)])
var diagram = Diagram(elements: [circle, triangle])
diagram.elements.append(Scaled(scale: 0.3, subject: diagram))
diagram.draw(renderer: TestRenderer())
------输出------
arcAt((187.5, 333.5), radius: 93.75, startAngle: 0.0, endAngle: 6.28318530717959)
moveTo(106.31, 286.625)
lineTo(187.5, 427.25)
lineTo(268.69, 286.625)
lineTo(106.31, 286.625)
arcAt((56.25, 100.05), radius: 28.125, startAngle: 0.0, endAngle: 6.28318530717959)
moveTo(31.893, 85.9875)
lineTo(56.25, 128.175)
lineTo(80.607, 85.9875)
lineTo(31.893, 85.9875)
1️⃣.系统的CGContext类型可以通过协议扩展,拥有Renderer的能力,实现其中的方法,成为自己的方法。
2️⃣.struct Scaled
3️⃣. Diagram的elements属性表示为一个遵守Drawable协议的数组,可以向数组中添加任意遵守Drawable协议的对象
补充1:
“The word static refers to things that happen at compile time and link time when the program is constructed—as opposed to load time or run time when the program is actually started.”
“The term dynamic refers to things that take place when a program is loaded and executed. ”
static dispatch 静态分配发生在程序的编译和链接的时候,
dynamic dispatch 动态分配发生在程序的加载和执行的时候
堆是动态分配的,栈既有动态分配又有静态分配,静态分配由编译器完成,动态分配由alloca进行分配,用完后马上就释放,无需程序员手动管理。
补充2:
关于self-requirement的资料
http://stackoverflow.com/questions/35270120/swift-overriding-self-requirement-is-allowed-but-causes-runtime-error-why
补充3:
generics 泛型
let animalAges: Array = [2,5,7,9]
let intNames: Dictionary = [42: "forty-two"]
此时animalAges的类型是Array
由于Swift自带类型推断,在声明属性的时候,省略了类型,上面数组推断为[Int],字典推断为[Int, String],但这不影响他们是一种特殊的泛型。
灵活使用泛型,可以使得代码简化,扩展性高。
例如,动物园中有很多种动物,每一种动物需要一个饲养员
class Cat {}
class Dog {}
class Keeper {} // 一个通用的饲养员类
let keeperCat = Keeper() // 创建了一个针对Cat的饲养员
let keeperDog = Keeper() // 创建了一个针对Dog的饲养员
// 在Keeper中添加各种方法,用Animal表示通用的类型,他会在在实际用的时候转化为Cat或者Dog类型
// 这样就可以添加更多种动物
泛型的用法还有很多,在代码中可以理解为一种类型站位符号或者一种类型约束符号。