Save 37% off PRO during our Black Friday Sale! »

Swift Zoomin' #8

Swift Zoomin' #8

3c031541ed3d92869414857dfef853de?s=128

Yuta Koshizawa

October 23, 2021
Tweet

Transcript

  1. Swi$ Zoomin' #8 Actor ϋϯζΦϯ

  2. Actor ͱ͸

  3. ɾɾɾͷલʹ

  4. Actor ͸ async/await Λଟ༻͢Δ

  5. async/await ͷ؆୯ͳ෮श

  6. ίʔϧόοΫʹΑΔඇಉظؔ਺ // એݴ func downloadData(from url: URL, completion: @escaping (Data)

    -> Void) // ར༻ downloadData(from: url) { data in // data Λ࢖͏ॲཧ }
  7. async/await ʹΑΔඇಉظؔ਺ // એݴ func downloadData(from url: URL) async ->

    Data // ར༻ let data = await downloadData(from: url) // data Λ࢖͏ॲཧ
  8. ίʔϧόοΫʹΑΔඇಉظؔ਺ // એݴ func downloadData(from url: URL, completion: @escaping (Result<Data,

    Error>) -> Void) // ར༻ downloadData(from: url) { result in do { let data = try result.get() // data Λ࢖͏ॲཧ } catch { /* ΤϥʔϋϯυϦϯά */ } }
  9. async/await ʹΑΔඇಉظؔ਺ // એݴ func downloadData(from url: URL) async throws

    -> Data // ར༻ do { let data = try await downloadData(from: url) // data Λ࢖͏ॲཧ } catch { /* ΤϥʔϋϯυϦϯά */ }
  10. ࿅श໰୊༻ϦϙδτϦ h"ps:/ /github.com/koher/swi4-zoomin-8

  11. Q1: downloadData ίʔϧόοΫͰ݁ՌΛฦ͢ downloadData ؔ਺͕ Download.swi, ʹ࣮૷͞Ε͍ͯ·͢ɻ async/await Λར༻͠ ͯɺ໭Γ஋Ͱ݁ՌΛฦ͢

    downloadData ؔ਺Λ࣮૷ͯ͠Լ͞ ͍ɻ ! iOS 15 ͔Β URLSession ʹ௥Ճ͞Εͨ࣍ͷϝιου͕໾ʹཱ ͪ·͢ɻ1 func data(from url: URL) async throws -> (Data, URLResponse) 1 h$ps:/ /developer.apple.com/documenta6on/founda6on/urlsession/3767353-data
  12. Q1: ղ౴ྫ

  13. async ؔ਺͸ async ؔ਺ͷதͰ͔͠ݺͼग़ͤͳ͍ func foo() async func bar() async

    { await foo() // ✅ OK }
  14. async ؔ਺͸ async ؔ਺ͷதͰ͔͠ݺͼग़ͤͳ͍ func foo() async func bar() {

    await foo() // ⛔ NG }
  15. async ؔ਺͸ async ؔ਺ͷதͰ͔͠ݺͼग़ͤͳ͍ func foo() async func bar() async

    { await foo() // ✅ OK } func baz() async { await bar() // ✅ OK } ...
  16. async ؔ਺Λ࠷ॳʹݺͼग़͢ͱ͖͸ʁ

  17. ಉظؔ਺͔Βͷ async ؔ਺ͷݺͼग़͠ override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated)

    await foo() // ⛔ NG }
  18. ಉظؔ਺͔Βͷ async ؔ਺ͷݺͼग़͠ override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated)

    Task { await foo() // ✅ OK } }
  19. ಉظؔ਺͔Βͷ async ؔ਺ͷݺͼग़͠ override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated)

    Task(operation: { await foo() // ✅ OK }) }
  20. Task.init 2 @discardableResult init( priority: TaskPriority? = nil, operation: @escaping

    @Sendable () async throws -> Success ) 2 h$ps:/ /developer.apple.com/documenta6on/swi9/task/3856790-init
  21. Q2: UserViewController.viewDidAppear UserViewController ͸ User ͷ৘ใΛऔಘͯ͠දࣔ͢Δ View Controller Ͱ͢ɻ viewDidAppear

    ͷதͰ downloadData ؔ਺Λ ࢖͍ɺ User ͱΞΠίϯΛऔಘͯ͠දࣔ͠·͢ɻ ͜ΕΛɺ async/await Ͱ࣮૷͞Εͨ downloadData ؔ਺Λར ༻ͯ͠࠶࣮૷ͯ͠Լ͍͞ɻ
  22. Q2: ղ౴ྫ

  23. Actor ͱ͸

  24. Actor ͸ΫϥεʹࣅͨࢀরܕͰɺσʔλڝ߹ Λ๷͙ͨΊͷϘΠϥʔϓϨʔτ͕ෆཁʹͳ Δɻ ͦͷͨΊɺίʔυ͕؆͔ܿͭ҆શʹͳΔɻ

  25. σʔλڝ߹ͱ͸

  26. Χ΢ϯλʔͷσʔλڝ߹ final class Counter { private var count: Int =

    0 func increment() -> Int { count += 1 return count } }
  27. Χ΢ϯλʔͷσʔλڝ߹ let counter: Counter = .init() Task { print(counter.increment()) //

    ? } Task { print(counter.increment()) // ? }
  28. Χ΢ϯλʔͷσʔλڝ߹ let counter: Counter = .init() Task { print(counter.increment()) //

    1 } Task { print(counter.increment()) // 2 }
  29. Χ΢ϯλʔͷσʔλڝ߹ let counter: Counter = .init() Task { print(counter.increment()) //

    2 } Task { print(counter.increment()) // 1 }
  30. Χ΢ϯλʔͷσʔλڝ߹ let counter: Counter = .init() Task { print(counter.increment()) //

    2 } Task { print(counter.increment()) // 2 }
  31. Χ΢ϯλʔͷσʔλڝ߹ final class Counter { private var count: Int =

    0 func increment() -> Int { count += 1 Thread.sleep(forTimeInterval: 1.0) return count } }
  32. Χ΢ϯλʔͷσʔλڝ߹ let counter: Counter = .init() Task { print(counter.increment()) //

    1 } Task { print(counter.increment()) // 1 }
  33. final class Counter { private var count: Int = 0

    func increment() -> Int { let count = self.count Thread.sleep(forTimeInterval: 1.0) self.count = count + 1 return self.count } }
  34. σʔλڝ߹΁ͷରԠํ๏ • ϩοΫ • ʢ DispatchQueue ͷʣγϦΞϧΩϡʔ

  35. γϦΞϧΩϡʔʹΑΔσʔλڝ߹ͷղফ final class Counter { private let queue: DispatchQueue =

    .init(label: "Counter") private var count: Int = 0 func increment(completion: @escaping (Int) -> Void) { queue.async { [self] in count += 1 completion(count) } } }
  36. γϦΞϧΩϡʔʹΑΔσʔλڝ߹ͷղফ let counter: Counter = .init() Task { counter.increment {

    print($0) } // 1 } Task { counter.increment { print($0) } // 2 }
  37. γϦΞϧΩϡʔʹΑΔσʔλڝ߹ͷղফ final class Counter { private let queue: DispatchQueue =

    .init(label: "Counter") private var count: Int = 0 func increment(completion: @escaping (Int) -> Void) { queue.async { [self] in count += 1 completion(count) } } }
  38. σʔλڝ߹΁ͷରԠํ๏ • ϩοΫ • ʢ DispatchQueue ͷʣγϦΞϧΩϡʔ • Actor

  39. Actor ʹΑΔσʔλڝ߹ͷղফ final class Counter { private var count: Int

    = 0 func increment() -> Int { count += 1 return count } }
  40. Actor ʹΑΔσʔλڝ߹ͷղফ actor Counter { private var count: Int =

    0 func increment() -> Int { count += 1 return count } }
  41. Actor ʹΑΔσʔλڝ߹ͷղফ let counter: Counter = .init() Task { print(await

    counter.increment()) } Task { print(await counter.increment()) }
  42. Q3: BankAccount.deposit ࣍ͷΑ͏ͳ BankAccount ΫϥεΛ࣮૷͠ɺฒߦʹ༬ۚ͢Δ͜ͱ Ͱσʔλڝ߹͕ൃੜ͢Δ͜ͱΛ֬ೝͯ͠Լ͍͞ɻͦͷޙɺ BankAccount Λ actor ʹมߋ͠ɺσʔλڝ߹͕๷ࢭ͞ΕΔ͜ͱ

    Λ֬ೝͯ͠Լ͍͞ɻ final class BankAccount { // ۜߦޱ࠲ var balance: Int = 0 // ༬ۚ࢒ߴ func deposit(_ amount: Int) -> Int // ೖۚʢ໭Γ஋͸࢒ߴʣ }
  43. Q3: ղ౴ྫ

  44. Actor ͷಉҰΠϯελϯε಺Ͱͷϝιουݺͼग़͠ actor A { func foo() { ... }

    func bar() { foo() // ಉظ } } await foo() // ඇಉظ
  45. Q4: BankAccount.getInterest BankAccount ʹ getInterest ϝιουΛ࣮૷ͯ͠Լ͍͞ɻ getInterest ϝιου͸࣍ͷΑ͏ͳγάωνϟΛ࣋ͪɺҾ਺Ͱ ར཰Λड͚औΓɺͦΕΛݩʹརଉΛܭࢉͯ͠༬ۚ࢒ߴΛมಈͤ͞ ্ͨͰɺ৽͍͠༬ۚ࢒ߴΛฦ͢΋ͷͱ͠·͢ɻͨͩ͠ɺ༬ۚ࢒ߴ

    ͷมಈʹ͸ deposit ϝιουΛར༻͢Δ΋ͷͱ͠·͢ɻ func getInterest(with rate: Double) -> Int
  46. Q4: ղ౴ྫ

  47. Actor ͷΠϯελϯεؒͰͷϝιουݺͼग़͠ actor A { func foo() { ... }

    } actor B { func bar(a: A) async { await a.foo() // ඇಉظ } }
  48. Actor ͷΠϯελϯεؒͰͷϝιουݺͼग़͠ actor A { func foo() { ... }

    func bar(a: A) async { foo() // ಉظ await a.foo() // ඇಉظ } }
  49. Actor ͷΠϯελϯεؒͰͷϝιουݺͼग़͠ actor A { func foo() { ... }

    func bar(a: A) async { self.foo() // ಉظ await a.foo() // ඇಉظ } }
  50. Actor ͷΠϯελϯεؒͰͷϝιουݺͼग़͠ actor A { func foo() { ... }

    func bar(a: A) async { foo() // ಉظ await a.foo() // ඇಉظ } }
  51. Q5: BankAccount.transfer BankAccount ʹ࣍ͷೋͭͷϝιουΛ௥Ճͯ͠Լ͍͞ɻ • ޱ࠲͔Βग़ۚ͢ΔͨΊͷ withdraw ϝιου • ޱ࠲ؒͰૹۚ͢ΔͨΊͷ

    transfer ϝιου // ࢒ߴ͕଍Γͳ͍৔߹͸ΤϥʔΛ throw func withdraw(_ amount: Int) throws -> Int // ໭Γ஋͸࢒ߴ func transfer(_ amount: Int, to account: BankAccount) async throws
  52. Q5: ղ౴ྫ

  53. Sendable

  54. Sendable 3 actor ڥքΛ௒͑ͯ΍ΓͱΓͰ͖Δ͜ͱΛද͢ϓϩτίϧ 3 h$ps:/ /github.com/apple/swi6-evolu9on/blob/main/proposals/0302-concurrent-value-and-concurrent- closures.md

  55. final class User { let id: ID var name: String

    var age: Int ... }
  56. actor A { let user: User = ... } let

    a: A = ... Task { let user = await a.user user.age += 1 // ⛔ } Actor ͷΩϡʔͷ֎Ͱ Actor ͷ಺෦ঢ়ଶΛมߋͰ͖ͯ͠·͏
  57. final class User: Sendable { let id: ID var name:

    String var age: Int ... } ⛔ ࢀরܕͰ var ϓϩύςΟΛ࣋ͭͷͰίϯύΠϧΤϥʔ
  58. struct User { let id: ID var name: String var

    age: Int ... } public Ͱͳ͍஋ܕ͸҉໧తʹద߹4 4 h$ps:/ /github.com/apple/swi6-evolu9on/blob/main/proposals/0302-concurrent-value-and-concurrent- closures.md#implicit-structenum-conformance-to-sendable
  59. public struct User: Sendable { public let id: ID public

    var name: String public var age: Int ... } public ͩͱ໌ࣔతʹద߹ͤ͞Δඞཁ͋Γ
  60. struct User: Sendable { let id: ID var name: NSString

    var age: Int ... } ⛔ Sendable Ͱͳ͍ϝϯόʔΛ࣋ͭͷͰίϯύΠϧΤϥʔ
  61. final class User: Sendable { let id: ID let name:

    String let age: Int ... } ✅ ࢀরܕͰ΋ΠϛϡʔλϒϧͳΒ OK
  62. final class User: Sendable { let id: ID let firstName:

    String let familyName: String let age: Int var name: String { "\(firstName) \(familyName)" } ... } ✅ Computed Property Λ͍࣋ͬͯͯ΋ΠϛϡʔλϒϧͳͷͰ OK
  63. final class User: Sendable { let id: ID let firstName:

    String let familyName: String let age: Int private var _name: String? // var name: String { if _name == nil { _name = "\(firstName) \(familyName)" } return _name! } ... }
  64. final class User: @unchecked Sendable { let id: ID let

    firstName: String let familyName: String let age: Int private var _name: String? // var name: String { if _name == nil { _name = "\(firstName) \(familyName)" } return _name! } ... }
  65. actor Counter { private var count: Int = 0 func

    increment() -> Int { count += 1 return count } } actor ͸҉໧తʹ Sendable ʹద߹
  66. ϓϩτίϧʹద߹ͤ͞ΒΕͳ͍৔߹͸ʁ

  67. actor Foo { func bar(_ f: (String) -> Int) {

    // // ... } }
  68. actor Foo { func bar(_ f: @Sendable (String) -> Int)

    { // // ... } } Ϋϩʔδϟ͸ϓϩτίϧʹద߹Ͱ͖ͳ͍ͷͰ @Sendable Λ࢖͏
  69. final class User { var name: String ... } let

    user: User = ... Task { let name = user.name // } @Sendable Ϋϩʔδϟ͸ Sendable ͳ஋͔͠ΩϟϓνϟͰ͖ͳ ͍ʢ Task.init ʹ౉͢Ϋϩʔδϟ͸ @Sendable ʣ
  70. var a = 42 Task { print(a) // ⛔ }

    @Sendable Ϋϩʔδϟ͸ม਺ΛΩϟϓνϟͰ͖ͳ͍
  71. var a = 42 Task { [a] in print(a) //

    ✅ } by-value Ωϟϓνϟ͸Մೳ
  72. None
  73. Actor-isolated Context ͷҾܧ͗

  74. @nonescaping, non-@Sendable ʢ context ΛҾ͖ܧ͙ʣ actor A { func foo(values:

    [Int]) { // A values.forEach { value in // A } } }
  75. @escaping, non-@Sendableʢ context ΛҾ͖ܧ͙ʣ actor A { func foo(value: Int)

    { // A let square: (Int) -> Int = { // @escaping // A $0 * $0 } print(square(value)) print(square(square(value))) } }
  76. @Sendable ʢ context ΛҾ͖ܧ͕ͳ͍ʣ actor A { func foo() {

    // A let foo: Foo = .init() // Foo: Sendable DispatchQueue.global().async { [foo] in // @Sendable // not A foo.value = 2 } foo.value = 3 print(foo.value) } } ※ͨͩ͠ɺ Swi% 5.5 ݱࡏ͸ @Sendable Ͱͳ͍
  77. Task.initʢ context ΛҾ͖ܧ͙ʣ actor A { func foo() { //

    A Task { // @Sendable // A } } }
  78. @_inheritActorContext extension Task where Failure == Never { ... @discardableResult

    @_alwaysEmitIntoClient public init( priority: TaskPriority? = nil, @_inheritActorContext @_implicitSelfCapture operation: __owned @Sendable @escaping () async -> Success ) { ... } } 10 h%ps:/ /github.com/apple/swi7/blob/main/stdlib/public/Concurrency/Task.swi7
  79. Task.initʢ context ΛҾ͖ܧ͙ʣ actor A { func foo() { //

    A Task { // @Sendable // A } } }
  80. Task.detachedʢ context ΛҾ͖ܧ͕ͳ͍ʣ actor A { func foo() { //

    A Task.detached { // @Sendable // not A } } }
  81. Actor-isolated Context ͷҾܧ͗ͷؔ͢Δ swi2-developers-japan Ͱͷ΍ΓͱΓ11 11 h$ps:/ /discord.com/channels/291054398077927425/291054454793306112/890858321693769748

  82. UserViewController

  83. None
  84. override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) Task { do

    { // User ͷ JSON ͷऔಘ let url: URL = ... let data = try await downloadData(from: url) ... // View ΁ͷ൓ө iconImageView.image = iconImage } catch { // ΤϥʔϋϯυϦϯά print(error) } } }
  85. View Controller ʹϩδοΫΛॻ͖ͨ͘ͳ͍

  86. ViewModel తͳΫϥεΛಋೖ

  87. UserViewState final class UserViewState { let id: User.ID @Published private(set)

    var user: User? @Published private(set) var iconImage: UIImage? init(id: User.ID) { self.id = id } }
  88. Q6: UserViewState UserViewState ʹ loadUser ϝιουΛ௥Ճ͠ɺϢʔβʔ͓Α ͼϢʔβʔΞΠίϯΛऔಘ͢ΔίʔυΛ UserViewController ͔Β෼཭ͯ͠Լ͍͞ɻ UserViewController

    ʹ͸ UserViewState ܕͷ state ϓϩύςΟΛ࣋ͨͤɺ state ͷঢ় ଶΛ View ʹ൓ө͢ΔΑ͏ʹͯ͠Լ͍͞ɻͳ͓ɺ͜ͷ໰୊͸ Actor ʹؔ܎͋Γ·ͤΜɻ ! state.user Ͱ͸ͳ͘ state.$user ͱॻ͘͜ͱͰɺ user Λ Publisher ͱͯ͠औಘ͢Δ͜ͱ͕Ͱ͖·͢ɻ
  89. Q6: ղ౴ྫ

  90. loadUser Λฒߦʹݺͼग़ͯ͠΋େৎ෉͔ʁ

  91. loadUser Ͱى͜ΓಘΔσʔλڝ߹ let data = try await downloadData(from: url) let

    user = try JSONDecoder().decode(User.self, from: data) self.user = user // ͕͜͜ಉ࣌ʹ࣮ߦ͞ΕΔͱʁ
  92. UserViewState Λ actor ʹ͍ͨ͠

  93. Q7: actor UserViewState UserViewState Λ Actor ʹͯ͠Լ͍͞ɻ

  94. Q7: ղ౴ྫ

  95. Swi$ Concurrency Λ࢖ͬͯ Publisher ΛߪಡͰ͖Δ

  96. Publisher.values 5 for await value in publisher.values { ... }

    5 h$ps:/ /developer.apple.com/documenta6on/combine/publisher/values-1dm9r
  97. AsyncSequence 6 protocol AsyncSequence { associatedtype AsyncIterator: AsyncIteratorProtocol where AsyncIterator.Element

    == Element associatedtype Element func makeAsyncIterator() -> AsyncIterator } protocol AsyncIteratorProtocol { associatedtype Element mutating func next() async throws -> Element? } 6 h$ps:/ /github.com/apple/swi6-evolu9on/blob/main/proposals/0298-asyncsequence.md
  98. Q8: Publisher.values UserViewState ͷ user ΍ iconImage ͷߪಡΛɺ for await

    Λ࢖͏ܗͰॻ͖׵͑ͯԼ͍͞ɻ
  99. Q8: ղ౴ྫ

  100. await state ͷ await ͕ᓔಃ͍͠ for await user in await

    state.$user.values { ... }
  101. state ͷΩϡʔ͕ϝΠϯΩϡʔͳΒɾɾɾ

  102. ܕΛ௒͑ͯΩϡʔͰอޢ͍ͨ͠

  103. Global Actor 7 7 h$ps:/ /github.com/apple/swi6-evolu9on/blob/main/proposals/0316-global-actors.md

  104. MainActor 8 8 h$ps:/ /github.com/apple/swi6-evolu9on/blob/main/proposals/0316-global-actors.md#the-main-actor

  105. @MainActor @MainActor func foo() { // isolated // ϝΠϯεϨου }

    @MainActor ͕෇༩͞Εͨؔ਺΍ϝιουɺϓϩύςΟ౳͸ MainActor ʹ actor-isolated
  106. @MainActor @MainActor final class Foo { var value: Int =

    42 // isolated func a() { // isolated // ϝΠϯεϨου } } @MainActor ͕෇༩͞ΕͨܕͷϝιουɺϓϩύςΟ౳͸ MainActor ʹ actor-isolated
  107. Q9: @MainActor UserViewState ʹ @MainActor Λ෇༩ͯ͠ɺ UserViewController ͱͷ΍ΓͱΓΛ MainActor ಺ͷॲཧͱ

    ͯ͠ѻ͑ΔΑ͏ʹͯ͠Լ͍͞ɻ
  108. Q9: ղ౴ྫ

  109. ObservableObject

  110. UserViewState Λ ObservableObject ʹ final class UserViewState: ObservableObject { ...

    }
  111. UserViewState Λ ObservableObject ʹ • Swi%UI ΁ͷҠߦΛݟਾ͑ͯ • Computed Property

    ͸ @Published ʹͰ͖ͳ͍
  112. UserViewState Λ ObservableObject ʹ • user ΍ iconImage Λݸผʹߪಡ͢ΔͷͰ͸ͳ͘ɺ objectWillChange9

    Λߪಡ͢Δ 9 h$ps:/ /developer.apple.com/documenta6on/combine/observableobject/objectwillchange-2oa5v
  113. Q10: ObservableObject UserViewState Λ ObservableObject ʹద߹ͤ͞ɺ user, iconImage Λݸผʹߪಡ͢ΔͷͰ͸ͳ͘ɺ objectWillChange

    Λߪಡͯ͠ View ʹ൓ө͢Δܗʹमਖ਼ͯ͠Լ͍͞ɻ
  114. Q10: ղ౴ྫ

  115. Q10 Ͱ࣮૷ͨ͠ UserViewState ͸ Swi(UI Ͱ΋ͦͷ··ར༻Մೳ

  116. Q11: Swi(UI UserViewController ͱಉ༷ͷݟͨ໨ɾڍಈΛ࣮ݱ͢Δ UserView Λ Swi%UI Ͱ࣮૷ͯ͠Լ͍͞ɻ ! @StateObject

    Ͱ UserViewState Λอ࣋͠·͢ɻ @StateObject private var state: UserViewState
  117. Q11: ղ౴ྫ