WWDC - Protocol and Value Type

字数 374阅读 28

WWDC 2016 - 419 Protocol and Value Oriented Programming in UIKit Apps

Key words
  • An associated type is like a type placeholder. The conforming type chooses the concrete type that it wants to use.
  • So associated types are a great way that you make your protocols even more powerful.
  • So the first is customization through composition instead of inheritance.
  • The second technique is to use protocols for generic reusable code.
Lucid Dreams
  • Model:
    1.struct + enum

  • View:
    1.protocol (Drawable & Layout)

  • Controller:
    1.State enum + Model struct —> maintainable & single code path
    2.withValues(...) —> state or model coalesces all UI updates

WWDC 2015 - 408 Protocol-Oriented Programming in Swift

1. Implicit Sharing
- Defensive copy
- Inefficiency
- Race Conditions
- Locks
- More Inefficiency
- DeadLock
- Complexity 

You handle something on a dispatch queue and suddenly you've got a race condition because threads are sharing a mutable state, so you start adding locks to protect your invariants.
But the locks slow the code down some more and might even lead to deadlock.
And this is all due to implicit sharing of mutable state, which is inherent to classes.

2. Inheritance Intrusive
- One superclass
- Single Inheritance weight gain
- No retroactive modeling
- Superclass may have stored properties
    - You must accept them
    - Initialization burden
    - Don’t break superclass invariants
- Know what / how to override (and when not to)

classes don't let us express this crucial type relationship between the type of self and the type of other.

A Better Abstraction Mechanism
    Supports value types (and classes)
    Supports static type relationships (and dynamic dispatch)
    Supports retroactive modeling
    Doesn’t impose instance data on models
    Doesn’t impose initialization burdens on models
    Makes clear what to implement
protocol Ordered {
    func precedes(other: Self) -> Bool

struct Number : Ordered {
    var value: Double = 0
    func precedes(other: Number) -> Bool {
        return self.value < other.value

func binarySearch<T : Ordered>(sortedKeys: [T], forKey k: T) -> Int {}

Self-requirement. Self in a protocol, it's a placeholder for the type that's going to conform to that protocol, the model type.

Once you add a Self-requirement to a protocol, it moves the protocol into a very different world, where the capabilities have a lot less overlap with classes. It stops being usable as a type.

Protocol 'Ordered' can only be used as a generic constraint because it has Self or associated type requirements

func precedes(other: Ordered) -> Bool func precedes(other: Self) -> Bool
Usable as a type Only usable as a generic constraint
Func sort(inout a: [Ordered]) func sort<T : Ordered>(inout a: [T])
extension Ordered where Self : Comparable {
    func precedes(other: Self) -> Bool { return self < other }
extension Int : Ordered {}
extension String : Ordered {}

binarySearch(sortedKeys: [1, 2, 3], forKey k: 1) 

protocol Drawable {
    func isEqualTo(other: Drawable) -> Bool
    func draw()
extension Drawable where Self : equatable {
    func isEqualTo(other: Drawable) -> Bool {
        if let o = other as? Self { return self == o }
        return false

If you really want to go and handle the heterogeneous case. Go and implement isEqualTo.

When to Use Classes
You want implicit sharing when 
    Copying or comparing instances doesn’t make sense (Window)
    instance lifetime is tied to external effects (e.g. TemporaryFile)
    instances are just “sinks” — write-only conduits to external state (e.g. CGContext)

WWDC 2015 - 414 Building Better Apps with Value Types in Swift

Uniquely Referenced Swift Objects

we can use this fact that we have this uniquely referenced property and we know for a fact that something is uniquely referenced so we can avoid making the copies if we know that that reference type is uniquely referenced.

The standard library uses that feature throughout and does a lot of great performance optimizations using that.

struct MyWrapper {
    var _object: SomeSwiftObject
    var objectForWriting: SomeSwiftObject {
        mutating get {
            if !isUniquelyReferencedNonObjC(&_object) {
                _object = _object.copy()
            return _object