Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Dig into Value types. It's really obvious when to use them. Or not?!

Dig into Value types. It's really obvious when to use them. Or not?!

Talk by Maksym Husar.

"Мы каждый день создаем и используем классы, структуры и перечисления, иногда даже не задумываясь, когда что лучше использовать и на что это может повлиять.
Все знают что классы это ссылочный тип, а структры - тип по значению.
Но все ли так просто? Всегда ли лучше использовать value types?
Мы копнем немного глубже “популярных” тезисов и рассмотрим особенности реализации и поведения типов по значению в зависимости от содержимого, вспомним о стеке, куче, управлении памятью и о производительности."

This talk was made for CocoaHeads Kyiv #15 which took place Jul 28, 2019. (https://cocoaheads.org.ua/cocoaheadskyiv/15)

Video: https://youtu.be/8L5cUiV1qDo

CocoaHeads Ukraine

July 28, 2019
Tweet

More Decks by CocoaHeads Ukraine

Other Decks in Programming

Transcript

  1. Dig into Value types.
    It's really obvious when & how to use them. Or not?!
    Maksym Husar
    Mobile Team Lead, Indeema Software Inc.

    View Slide

  2. About me
    Creating inventive IoT solutions since 2014. We partner
    with companies worldwide, equipping them with tools to
    solve the most complex challenges.
    Maksym Husar
    ● Mobile Team Lead at Indeema Software
    ● In commercial iOS development since 2014
    ● Author of the “iOS Start: 0 to 1” course
    https://www.udemy.com/ios-start-zero-to-one-swift
    https://www.linkedin.com/in/maksymhusar/

    View Slide

  3. ● First shareable air monitoring system.
    ● Measures 5 air parameters
    ● App receives readings from UBox
    ● Alerts user when any of them is outside
    the limits
    UBreez
    For more information, scan QR Code:
    UBreez is the first shareable air
    monitoring system

    View Slide

  4. Agenda.
    A1. The Basics
    2. Common Misconceptions
    3. Recommendations

    View Slide

  5. The Basics.
    1

    View Slide

  6. Value Type — each instance keeps a unique copy of its data.
    1. The Basics
    “Pure” Value Types Reference Types
    Shared on assignment
    (Multiple owners)
    Copy on assignment
    (Single owner)
    From https://developer.apple.com/swift/blog/?id=10

    View Slide

  7. Swift Value Types != “Pure” Value Types
    Swift Value Types == Value Semantics
    1. The Basics

    View Slide

  8. *Most of the time
    class Worker {
    let name: String
    var salary: Int
    init(name: String, salary: Int) { self.name = name; self.salary = salary }
    }
    let obj = Worker(name: "Jackie", salary: 1500)
    showDescription(of: obj)
    1. The Basics
    The main advantage of (Swift)Value types:
    No unexpected data sharing*

    View Slide

  9. *Most of the time
    1. The Basics
    The main advantage of (Swift)Value types:
    No unexpected data sharing*
    class Worker {
    let name: String
    var salary: Int
    init(name: String, salary: Int) { self.name = name; self.salary = salary }
    }
    let obj = Worker(name: "Jackie", salary: 1500)
    print("1) \(obj.salary)") // 1) 1500
    showDescription(of: obj)
    print("2) \(obj.salary)") // 2) 200

    View Slide

  10. *Most of the time
    1. The Basics
    The main advantage of (Swift)Value types:
    No unexpected data sharing*
    class Worker {
    let name: String
    var salary: Int
    init(name: String, salary: Int) { self.name = name; self.salary = salary }
    }
    let obj = Worker(name: "Jackie", salary: 1500)
    print("1) \(obj.salary)") // 1) 1500
    showDescription(of: obj)
    print("2) \(obj.salary)") // 2) 200

    View Slide

  11. Consider the following recommendations to help you choose what option makes
    sense when adding a new data type to your app.
    ● Use structures by default.
    ● Use classes when you need Objective-C interoperability.
    ● Use classes when you need to control the identity of the data you're modeling.
    ● Use structures along with protocols to adopt behavior by sharing
    implementations.
    From
    https://developer.apple.com/documentation/swift/choosing_between_structu
    res_and_classes
    1. The Basics

    View Slide

  12. Memory architecture basics
    (single thread for simplicity)
    |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
    Stack
    |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
    Nothing
    (Stack and heap grow towards here)
    |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
    Heap
    |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
    Global Data
    |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
    Instructions
    |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
    ..........
    1. The Basics
    0x0 (low address)

    View Slide

  13. Stack Memory:
    ● Simple LIFO data structure
    ● Decrement stack pointer to allocate
    ● Increment stack pointer to deallocate
    ● Thread-safe — 1 Stack per Thread
    ● Not limitless — has an upper bound
    ● Variables only exist while the function that created them
    exists
    ● The cost of allocating/deallocating stack memory is
    literally the cost of assigning an integer
    Stack
    ...
    1. The Basics

    View Slide

  14. Heap Memory:
    ● Advanced data structure
    ● Search for unused block of memory to allocate
    ● Reinsert block of memory to deallocate
    ● Shared memory requires synchronization
    ● It’s large and usually limited by the available physical
    memory
    ● Requires pointers to access it
    Heap
    1. The Basics

    View Slide

  15. Common
    Misconceptions.
    2

    View Slide

  16. ● “Reference type instances are stored in the Heap and instances
    of a value type such as struct reside in the Stack”
    ● “Structs are always faster/more efficient than classes”
    ● “Unexpected data sharing for Value types doesn’t exist”
    ● “Value types provide thread safety. No Race Conditions”
    2. Common Misconceptions

    View Slide

  17. ● “Reference type instances are stored in the Heap and instances
    of a value type such as struct reside in the Stack”
    ● “Structs are always faster/more efficient than classes”
    ● “Unexpected data sharing for Value types doesn’t exist”
    ● “Value types provide thread safety. No Race Conditions”
    2. Common Misconceptions

    View Slide

  18. ● “Reference type instances are stored in the Heap and instances
    of a value type such as struct reside in the Stack”
    ● “Structs are always faster/more efficient than classes”
    ● “Unexpected data sharing for Value types doesn’t exist”
    ● “Value types provide thread safety. No Race Conditions”
    2. Common Misconceptions

    View Slide

  19. ● “Reference type instances are stored in the Heap and instances
    of a value type such as struct reside in the Stack”
    ● “Structs are always faster/more efficient than classes”
    ● “Unexpected data sharing for Value types doesn’t exist”
    ● “Value types provide thread safety. No Race Conditions”
    2. Common Misconceptions

    View Slide

  20. ● “Reference type instances are stored in the Heap and instances
    of a value type such as struct reside in the Stack”
    ● “Structs are always faster/more efficient than classes”
    ● “Unexpected data sharing for Value types doesn’t exist”
    ● “Value types provide thread safety. No Race Conditions”
    2. Common Misconceptions

    View Slide

  21. 2. Common Misconceptions
    Misconception: Reference type instances are stored in the Heap, and
    instances of a value type such as struct reside in the Stack

    View Slide

  22. 2. Common Misconceptions
    Misconception: Reference type instances are stored in the Heap, and
    instances of a value type such as struct reside in the Stack
    For Reference type it’s 100% true.
    For Value type it’s partly true but with a lot of IFs
    Precise definition: For reference types, the reference is stored on the Stack while the object it
    refers to is stored on the Heap.

    View Slide

  23. 2. Common Misconceptions
    Value type storage depending on the contents/circumstances*
    * Default behavior can be changed by compiler optimizations in specific cases
    ** Size may vary depending on Swift version
    a. Stack (fully stack allocated) b. Heap
    Swift standard library value types:
    Static size Dynamic size (Unknown actual size at compilation
    time)
    User-defined value types:
    All properties are static-sized Contained inside a reference type
    Optimization: Allocated as an abstract type with
    size ≤ 3** words
    Size cannot be determined during compilation
    time (because of a protocol/generic requirement)
    Contains recursions
    Potentially, gigantic static sized instances

    View Slide

  24. 2. Common Misconceptions
    Value type storage depending on the contents/circumstances*
    * Default behavior can be changed by compiler optimizations in specific cases
    c. Stack (mix: stack + heap)

    View Slide

  25. 2. Common Misconceptions
    Value type storage depending on the contents/circumstances*
    * Default behavior can be changed by compiler optimizations in specific cases
    c. Stack (mix: stack + heap)
    User-defined value types:
    ● Contain reference type properties
    ● Contain dynamic-sized value type properties
    Stack Heap
    b: Double
    ref: Class
    x1: Int
    x2: Int
    a: Int

    View Slide

  26. String, Array, Dictionary, and Set are value types
    that contain internal reference types. These types
    manage the storage of elements in the heap,
    allowing them to increase/decrease in size as
    needed.
    2. Common Misconceptions

    View Slide

  27. Value semantics implementations
    Copy-on-Assignment (Stack) Copy-on-Write
    (Heap)
    2. Common Misconceptions

    View Slide

  28. 2. Common Misconceptions
    The easiest way to find out the storage type of value
    instance is to create a copy and compare addresses

    View Slide

  29. 2. Common Misconceptions
    The easiest way to find out the storage type of value
    instance is to create a copy and compare addresses
    Let’s try to implement!

    View Slide

  30. 2. Common Misconceptions
    func printAddress(of object: UnsafeRawPointer) {
    let addr = Int(bitPattern: object)
    let address = String(format: "%p", addr)
    print(address)
    }
    struct User {
    var name: String
    }
    var user = User(name: "Bob")
    printAddress(of: &user) // 0x7ffee88208b0
    var clone = user
    printAddress(of: &user) // 0x7ffee88208b0
    printAddress(of: &clone)// 0x7ffee88208a0

    View Slide

  31. 2. Common Misconceptions
    var arr = [1, 2, 3]
    var arrCopy = arr
    printAddress(of: arr) // 0x6000000191a0
    printAddress(of: arrCopy) // 0x6000000191a0
    arrCopy[0] = 10
    printAddress(of: arr) // 0x6000000191a0
    printAddress(of: arrCopy) // 0x6000000196a0

    View Slide

  32. 2. Common Misconceptions
    class Container {
    var i = 10
    var user = User(name: "Bob")
    }
    let cont = Container()
    printAddress(of: &cont.user) // 0x7ffee48fd888
    var userCopy = cont.user
    printAddress(of: &userCopy) // 0x7ffee48fd888
    userCopy.name = "Ben"
    printAddress(of: &cont.user) // 0x7ffee48fd878
    printAddress(of: &userCopy) // 0x7ffee48fd888

    View Slide

  33. 2. Common Misconceptions
    Misconception: Structs are always faster/more efficient than classes

    View Slide

  34. 2. Common Misconceptions
    Misconception: Structs are always faster/more efficient than classes
    The performance of Value type depends a lot
    on the content and compiler optimizations

    View Slide

  35. 2. Common Misconceptions
    Misconception: Structs are always faster/more efficient than classes
    The performance of Value type depends a lot
    on the content and compiler optimizations
    Since Swift 4.2, performance has increased dramatically and
    the language internals have changed a lot
    Currently used Swift version: 5.0

    View Slide

  36. 2. Common Misconceptions
    Critical to understand performance:

    View Slide

  37. 2. Common Misconceptions
    Critical to understand performance:
    ● If value type contains heap-allocated property, then it will not be heap-
    allocated itself, but it will inherit reference counting overhead to be able to
    keep it's inner references alive.
    ● String, Array, Dictionary, Set are value types that contain internal reference
    types that manage the storage of elements in the heap, allowing them to
    increase/decrease in size as needed.

    View Slide

  38. 2. Common Misconceptions
    final class TinyClass { }
    final class ClassOfClasses {
    let class1 = TinyClass()
    let class2 = TinyClass()
    let class3 = TinyClass()
    }
    struct StructOfClasses {
    let class1 = TinyClass()
    let class2 = TinyClass()
    let class3 = TinyClass()
    }

    View Slide

  39. 2. Common Misconceptions
    let classOfClasses = ClassOfClasses()
    let ref1 = classOfClasses
    let ref2 = classOfClasses
    let ref3 = classOfClasses
    print(CFGetRetainCount(classOfClasses)) // 4
    print(CFGetRetainCount(classOfClasses.class1)) // 1
    print(CFGetRetainCount(classOfClasses.class2)) // 1
    print(CFGetRetainCount(classOfClasses.class3)) // 1
    let structOfClasses = StructOfClasses()
    let copy = structOfClasses
    let copy2 = structOfClasses
    let copy3 = structOfClasses
    // Doesn't compile, structs themselves don't have a
    reference count.
    // print(CFGetRetainCount(structOfClasses))
    print(CFGetRetainCount(structOfClasses.class1)) // 4
    print(CFGetRetainCount(structOfClasses.class2)) // 4
    print(CFGetRetainCount(structOfClasses.class3)) // 4

    View Slide

  40. 2. Common Misconceptions
    For some large values, creating copies could be time-consuming and
    hurt the performance of the program.

    View Slide

  41. 2. Common Misconceptions
    struct EnumsStringStruct {
    enum TestEnum: String {
    case a = "something", b, c, d
    }
    var p1 = TestEnum.a
    var p2 = TestEnum.b
    var p3 = TestEnum.c
    var p4 = TestEnum.d
    var p5 = TestEnum.a
    }
    struct StringsStruct {
    var p1 = "test1"
    var p2 = "testTestTest"
    var p3 = "random string"
    ...
    var p20 = "1234567890"
    }
    Let’s test the speed of copying items.
    Number of iterations: 10 000 000. Results in seconds.

    View Slide

  42. 2. Common Misconceptions
    Let’s test the speed of copying items.
    Number of iterations: 10 000 000. Results in seconds.
    Struct
    (Int)
    Struct
    (String)
    Class
    (Int)
    Class
    (String)
    Struct
    (Enum)
    Struct
    (Enum: String)
    5 fields 0.0187 0.274 0.349 0.34 0.0189 0.0213
    10 fields 0.0314 0.518 0.343 0.342 0.0176 0.0214
    20 fields 0.0555 1.04 0.344 0.373 0.0172 0.0194
    50 fields 0.125 2.45 0.343 0.338 0.0983 0.104
    Source code: https://github.com/maksymhusar/ValueTypePerformance.git

    View Slide

  43. 2. Common Misconceptions
    Let’s test the speed of copying items.
    Number of iterations: 10 000 000. Results in seconds.
    Struct
    (Int)
    Struct
    (String)
    Class
    (Int)
    Class
    (String)
    Struct
    (Enum)
    Struct
    (Enum: String)
    5 fields 0.0187 0.274 0.349 0.34 0.0189 0.0213
    10 fields 0.0314 0.518 0.343 0.342 0.0176 0.0214
    20 fields 0.0555 1.04 0.344 0.373 0.0172 0.0194
    50 fields 0.125 2.45 0.343 0.338 0.0983 0.104
    Source code: https://github.com/maksymhusar/ValueTypePerformance.git
    ~x15

    View Slide

  44. 2. Common Misconceptions
    Let’s test the speed of copying items.
    Number of iterations: 10 000 000. Results in seconds.
    Struct
    (Int)
    Struct
    (String)
    Class
    (Int)
    Class
    (String)
    Struct
    (Enum)
    Struct
    (Enum: String)
    5 fields 0.0187 0.274 0.349 0.34 0.0189 0.0213
    10 fields 0.0314 0.518 0.343 0.342 0.0176 0.0214
    20 fields 0.0555 1.04 0.344 0.373 0.0172 0.0194
    50 fields 0.125 2.45 0.343 0.338 0.0983 0.104
    Source code: https://github.com/maksymhusar/ValueTypePerformance.git

    View Slide

  45. 2. Common Misconceptions
    Let’s test the speed of copying items.
    Number of iterations: 10 000 000. Results in seconds.
    Struct
    (Int)
    Struct
    (String)
    Class
    (Int)
    Class
    (String)
    Struct
    (Enum)
    Struct
    (Enum: String)
    5 fields 0.0187 0.274 0.349 0.34 0.0189 0.0213
    10 fields 0.0314 0.518 0.343 0.342 0.0176 0.0214
    20 fields 0.0555 1.04 0.344 0.373 0.0172 0.0194
    50 fields 0.125 2.45 0.343 0.338 0.0983 0.104
    Source code: https://github.com/maksymhusar/ValueTypePerformance.git

    View Slide

  46. 2. Common Misconceptions
    Let’s test the speed of copying items.
    Number of iterations: 10 000 000. Results in seconds.
    Struct
    (Int)
    Struct
    (String)
    Class
    (Int)
    Class
    (String)
    Struct
    (Enum)
    Struct
    (Enum: String)
    5 fields 0.0187 0.274 0.349 0.34 0.0189 0.0213
    10 fields 0.0314 0.518 0.343 0.342 0.0176 0.0214
    20 fields 0.0555 1.04 0.344 0.373 0.0172 0.0194
    50 fields 0.125 2.45 0.343 0.338 0.0983 0.104
    Source code: https://github.com/maksymhusar/ValueTypePerformance.git

    View Slide

  47. 2. Common Misconceptions
    Let’s test the speed of copying items.
    Number of iterations: 10 000 000. Results in seconds.
    Struct
    (Int)
    Struct
    (String)
    Class
    (Int)
    Class
    (String)
    Struct
    (Enum)
    Struct
    (Enum: String)
    5 fields 0.0187 0.274 0.349 0.34 0.0189 0.0213
    10 fields 0.0314 0.518 0.343 0.342 0.0176 0.0214
    20 fields 0.0555 1.04 0.344 0.373 0.0172 0.0194
    50 fields 0.125 2.45 0.343 0.338 0.0983 0.104
    Source code: https://github.com/maksymhusar/ValueTypePerformance.git
    ~x3

    View Slide

  48. 2. Common Misconceptions
    Let’s test the speed of copying items.
    Number of iterations: 10 000 000. Results in seconds.
    Struct
    (Int)
    Struct
    (String)
    Class
    (Int)
    Class
    (String)
    Struct
    (Enum)
    Struct
    (Enum: String)
    5 fields 0.0187 0.274 0.349 0.34 0.0189 0.0213
    10 fields 0.0314 0.518 0.343 0.342 0.0176 0.0214
    20 fields 0.0555 1.04 0.344 0.373 0.0172 0.0194
    50 fields 0.125 2.45 0.343 0.338 0.0983 0.104
    Source code: https://github.com/maksymhusar/ValueTypePerformance.git
    ~x54

    View Slide

  49. 2. Common Misconceptions
    Let’s test the speed of copying items.
    Number of iterations: 10 000 000. Results in seconds.
    Struct
    (Int)
    Struct
    (String)
    Class
    (Int)
    Class
    (String)
    Struct
    (Enum)
    Struct
    (Enum: String)
    5 fields 0.0187 0.274 0.349 0.34 0.0189 0.0213
    10 fields 0.0314 0.518 0.343 0.342 0.0176 0.0214
    20 fields 0.0555 1.04 0.344 0.373 0.0172 0.0194
    50 fields 0.125 2.45 0.343 0.338 0.0983 0.104
    Source code: https://github.com/maksymhusar/ValueTypePerformance.git
    ~x5.5

    View Slide

  50. 2. Common Misconceptions
    Let’s test the speed of copying items.
    Number of iterations: 10 000 000. Results in seconds.
    Struct
    (Int)
    Struct
    (String)
    Class
    (Int)
    Class
    (String)
    Struct
    (Enum)
    Struct
    (Enum: String)
    5 fields 0.0187 0.274 0.349 0.34 0.0189 0.0213
    10 fields 0.0314 0.518 0.343 0.342 0.0176 0.0214
    20 fields 0.0555 1.04 0.344 0.373 0.0172 0.0194
    50 fields 0.125 2.45 0.343 0.338 0.0983 0.104
    Source code: https://github.com/maksymhusar/ValueTypePerformance.git

    View Slide

  51. 2. Common Misconceptions
    Let’s test the speed of copying items.
    Number of iterations: 10 000 000. Results in seconds.
    Struct
    (Int)
    Struct
    (String)
    Class
    (Int)
    Class
    (String)
    Struct
    (Enum)
    Struct
    (Enum: String)
    5 fields 0.0187 0.274 0.349 0.34 0.0189 0.0213
    10 fields 0.0314 0.518 0.343 0.342 0.0176 0.0214
    20 fields 0.0555 1.04 0.344 0.373 0.0172 0.0194
    50 fields 0.125 2.45 0.343 0.338 0.0983 0.104
    Source code: https://github.com/maksymhusar/ValueTypePerformance.git
    ~x20

    View Slide

  52. 2. Common Misconceptions
    Indirect Storage can be used for a big structs to
    improve performance

    View Slide

  53. final class Ref {
    var value: T
    init(value: T) { self.value = value }
    }
    struct Box {
    private var ref: Ref
    init(value: T) { ref = Ref(value: value) }
    var value: T {
    get { return ref.value }
    set {
    guard isKnownUniquelyReferenced(&ref) else {
    ref = Ref(value: newValue)
    return
    }
    ref.value = newValue
    }
    }
    }
    struct User { var name: String }
    let user = User(name: "Alexa")
    let box = Box(value: user)
    // boxCopy shares instance of box.ref
    var boxCopy = box
    // Creates new object for boxCopy.ref
    boxCopy.value.name = "Siri"
    print(box.value.name) // Alexa
    print(boxCopy.value.name) // Siri
    2. Common Misconceptions
    Indirect Storage with Copy-On-Write implementation:

    View Slide

  54. Performance optimization recommendations:
    ● Never do/rely on premature optimization.
    ● Research the performance-critical modules.
    ● Profile with Instruments.
    2. Common Misconceptions

    View Slide

  55. 2. Common Misconceptions
    Misconception: Unexpected data sharing for Value types doesn’t exist

    View Slide

  56. 2. Common Misconceptions
    Misconception: Unexpected data sharing for Value types doesn’t exist
    In general, it’s true but there are
    exceptions...

    View Slide

  57. 2. Common Misconceptions
    Misconception: Unexpected data sharing for Value types doesn’t exist
    var a = 10
    let printClosure = {
    print("a = \(a)")
    }
    a = 20
    printClosure()

    View Slide

  58. 2. Common Misconceptions
    Misconception: Unexpected data sharing for Value types doesn’t exist
    var a = 10
    let printClosure = {
    print("a = \(a)")
    }
    a = 20
    printClosure() // 20

    View Slide

  59. 2. Common Misconceptions
    Misconception: Unexpected data sharing for Value types doesn’t exist
    var a = 10
    let printClosure = {
    print("a = \(a)")
    }
    a = 20
    printClosure() // 20
    By default, closures capture and store references to any used constants and
    variables from the context in which they are defined.

    View Slide

  60. 2. Common Misconceptions
    Misconception: Unexpected data sharing for Value types doesn’t exist
    var a = 10
    let printClosure = {
    print("a = \(a)")
    }
    a = 20
    printClosure() // 20
    var a = 10
    let printClosure = { [a] in
    print("a = \(a)")
    }
    a = 20
    printClosure() // 10
    By default, closures capture and store references to any used constants and
    variables from the context in which they are defined.

    View Slide

  61. 2. Common Misconceptions
    Misconception: Unexpected data sharing for Value types doesn’t exist
    By default, closures capture and store references to any used constants and
    variables from the context in which they are defined.
    struct User { var name: String }
    var user = User(name: "Ben")
    let printNameClosure = {
    print("name = \(user.name)")
    }
    user.name = "John"
    printNameClosure() // name = John
    var user = User(name: "Ben")
    let printNameClosure = { [user] in
    print("name = \(user.name)")
    }
    user.name = "John"
    printNameClosure() // name = Ben

    View Slide

  62. 2. Common Misconceptions
    Misconception: Value types provide thread safety. No Race Conditions

    View Slide

  63. 2. Common Misconceptions
    Misconception: Value types provide thread safety. No Race Conditions
    Thread safety: Only for constants

    View Slide

  64. 2. Common Misconceptions
    Example 1:
    struct PlainStruct { var a = 0; var b = 0 }
    var item = PlainStruct()
    DispatchQueue.global().async {
    for i in 0...9999 {
    item.a = i; item.b = -i
    if item.a != -item.b { print("*** a = \(item.a); b = \(item.b) ***") }
    }
    }
    DispatchQueue.global().async {
    for i in 0...9999 {
    item.a = -i; item.b = i
    if item.b != -item.a { print("### a = \(item.a); b = \(item.b) ###") }
    }
    }
    DispatchQueue.global().async { print("1) a = \(item.a); b = \(item.b)") }
    DispatchQueue.global().asyncAfter(deadline: .now() + 0.2) {
    print("2) a = \(item.a); b = \(item.b)")
    }

    View Slide

  65. 2. Common Misconceptions
    Console output:
    1) a = 2019; b = -2143
    ### a = 2002; b = -2142 ###
    ....
    ### a = 5098; b = -5102 ###
    *** a = -62; b = 336 ***
    ### a = 5156; b = -5160 ###
    *** a = -478; b = 482 ***
    ### a = 5218; b = -5221 ###
    ....
    ### a = 5270; b = -5273 ###
    *** a = -534; b = 538 ***
    ### a = 5322; b = -5326 ###
    .....
    ### a = 9974; b = -9978 ###
    2) a = -9999; b = 9999

    View Slide

  66. 2. Common Misconceptions
    Example 2:
    var balance = 1200
    struct ATM {
    let tag: String
    func withdraw(value: Int) {
    print("\(self.tag): checking if balance contains sufficient money")
    if balance > value {
    print("\(self.tag): Balance is sufficient, please wait while processing withdrawal")
    // sleeping for some random time, simulating a long process
    Thread.sleep(forTimeInterval: Double.random(in: 0...2))
    balance -= value
    print("\(self.tag): Done: \(value) has been withdrawn")
    print("\(self.tag): current balance is \(balance)")
    } else { print("\(self.tag): Can't withdraw: insufficient balance") }
    }
    }
    let queue = DispatchQueue(label: "WithdrawalQueue", attributes: .concurrent)
    queue.async { let first = ATM(tag: "firstATM"); first.withdraw(value: 1000) }
    queue.async { let second = ATM(tag: "secondATM"); second.withdraw(value: 800) }

    View Slide

  67. 2. Common Misconceptions
    Console output:
    firstATM: checking if balance contains sufficient amount of money
    secondATM: checking if balance contains sufficient amount of money
    firstATM: Balance is sufficient, please wait while processing withdrawal
    secondATM: Balance is sufficient, please wait while processing withdrawal
    firstATM: Done: 1000 have been withdrawn
    firstATM: current balance is 200
    secondATM: Done: 800 have been withdrawn
    secondATM: current balance is -600

    View Slide

  68. 2. Common Misconceptions
    Misconception: Value types provide thread safety. No Race Conditions

    View Slide

  69. 2. Common Misconceptions
    Misconception: Value types provide thread safety. No Race Conditions
    Use appropriate thread-safe programming
    techniques!

    View Slide

  70. Recommendations.
    3

    View Slide

  71. 3. Recommendations
    ● Make properties immutable by default
    ● Never do/rely on premature optimization. Profile with
    Instruments!
    ● Use common semantics for logically related types (Do not force
    user to check every time is it a value or reference type)
    ● For time-consuming modules, Indirect Storage can be used for
    big structs to improve performance
    ● Don’t forget using appropriate thread-safe programming
    techniques when needed
    ● Use Nested Types
    ● Use Enums and type safety as much as possible

    View Slide

  72. 3. Recommendations
    ● Make properties immutable by default
    ● Never do/rely on premature optimization. Profile with
    Instruments!
    ● Use common semantics for logically related types (Do not force
    user to check every time is it a value or reference type)
    ● For time-consuming modules, Indirect Storage can be used for
    big structs to improve performance
    ● Don’t forget using appropriate thread-safe programming
    techniques when needed
    ● Use Nested Types
    ● Use Enums and type safety as much as possible

    View Slide

  73. 3. Recommendations
    ● Make properties immutable by default
    ● Never do/rely on premature optimization. Profile with
    Instruments!
    ● Use common semantics for logically related types (Do not force
    user to check every time is it a value or reference type)
    ● For time-consuming modules, Indirect Storage can be used for
    big structs to improve performance
    ● Don’t forget using appropriate thread-safe programming
    techniques when needed
    ● Use Nested Types
    ● Use Enums and type safety as much as possible

    View Slide

  74. 3. Recommendations
    struct Controller1ViewModel {
    ...
    }
    class Controller2ViewModel {
    ...
    }
    ● Use common semantics for logically related types (Do not force
    user to check every time is it a value or reference type)

    View Slide

  75. 3. Recommendations
    ● Make properties immutable by default
    ● Never do/rely on premature optimization. Profile with
    Instruments!
    ● Use common semantics for logically related types (Do not force
    user to check every time is it a value or reference type)
    ● For time-consuming modules, Indirect Storage can be used for
    big structs to improve performance
    ● Don’t forget using appropriate thread-safe programming
    techniques when needed
    ● Use Nested Types
    ● Use Enums and type safety as much as possible

    View Slide

  76. 3. Recommendations
    ● Make properties immutable by default
    ● Never do/rely on premature optimization. Profile with
    Instruments!
    ● Use common semantics for logically related types (Do not force
    user to check every time is it a value or reference type)
    ● For time-consuming modules, Indirect Storage can be used for
    big structs to improve performance
    ● Don’t forget using appropriate thread-safe programming
    techniques when needed
    ● Use Nested Types
    ● Use Enums and type safety as much as possible

    View Slide

  77. 3. Recommendations
    ● Make properties immutable by default
    ● Never do/rely on premature optimization. Profile with
    Instruments!
    ● Use common semantics for logically related types (Do not force
    user to check every time is it a value or reference type)
    ● For time-consuming modules, Indirect Storage can be used for
    big structs to improve performance
    ● Don’t forget using appropriate thread-safe programming
    techniques when needed
    ● Use Nested Types
    ● Use Enums and type safety as much as possible

    View Slide

  78. 3. Recommendations
    ● Make properties immutable by default
    ● Never do/rely on premature optimization. Profile with
    Instruments!
    ● Use common semantics for logically related types (Do not force
    user to check every time is it a value or reference type)
    ● For time-consuming modules, Indirect Storage can be used for
    big structs to improve performance
    ● Don’t forget using appropriate thread-safe programming
    techniques when needed
    ● Use Nested Types
    ● Use Enums and type safety as much as possible

    View Slide

  79. 3. Recommendations
    ● Make properties immutable by default
    ● Never do/rely on premature optimization. Profile with
    Instruments!
    ● Use common semantics for logically related types (Do not force
    user to check every time is it a value or reference type)
    ● For time-consuming modules, Indirect Storage can be used for
    big structs to improve performance
    ● Don’t forget using appropriate thread-safe programming
    techniques when needed
    ● Use Nested Types
    ● Use Enums and type safety as much as possible

    View Slide

  80. https://developer.apple.com/wwdc15/409
    https://developer.apple.com/wwdc15/414
    https://developer.apple.com/wwdc16/416
    https://developer.apple.com/documentation/swift/choosing_between_structures_and_classes
    https://github.com/apple/swift/blob/master/docs/OptimizationTips.rst#the-cost-of-large-swift-values
    https://swiftrocks.com/memory-management-and-performance-of-value-types.html
    https://academy.realm.io/posts/goto-mike-ash-exploring-swift-memory-layout/
    https://www.mikeash.com/pyblog/friday-qa-2015-07-17-when-to-use-swift-structs-and-classes.html
    https://forums.swift.org/t/class-vs-structures/6230/2
    Useful Links

    View Slide