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.
  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/
  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
  4. 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
  5. *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*
  6. *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
  7. *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
  8. 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
  9. 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)
  10. 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
  11. 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
  12. • “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
  13. • “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
  14. • “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
  15. • “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
  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
  17. 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
  18. 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.
  19. 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
  20. 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)
  21. 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
  22. 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
  23. 2. Common Misconceptions The easiest way to find out the

    storage type of value instance is to create a copy and compare addresses
  24. 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!
  25. 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
  26. 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
  27. 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
  28. 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
  29. 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
  30. 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.
  31. 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() }
  32. 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
  33. 2. Common Misconceptions For some large values, creating copies could

    be time-consuming and hurt the performance of the program.
  34. 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.
  35. 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
  36. 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
  37. 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
  38. 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
  39. 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
  40. 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
  41. 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
  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 ~x5.5
  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
  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 ~x20
  45. final class Ref<T> { var value: T init(value: T) {

    self.value = value } } struct Box<T> { private var ref: Ref<T> 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:
  46. Performance optimization recommendations: • Never do/rely on premature optimization. •

    Research the performance-critical modules. • Profile with Instruments. 2. Common Misconceptions
  47. 2. Common Misconceptions Misconception: Unexpected data sharing for Value types

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

    doesn’t exist var a = 10 let printClosure = { print("a = \(a)") } a = 20 printClosure()
  49. 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
  50. 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.
  51. 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.
  52. 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
  53. 2. Common Misconceptions Misconception: Value types provide thread safety. No

    Race Conditions Thread safety: Only for constants
  54. 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)") }
  55. 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
  56. 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) }
  57. 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
  58. 2. Common Misconceptions Misconception: Value types provide thread safety. No

    Race Conditions Use appropriate thread-safe programming techniques!
  59. 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
  60. 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
  61. 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
  62. 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)
  63. 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
  64. 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
  65. 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
  66. 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
  67. 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