Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Speaker Deck
PRO
Sign in
Sign up for free
Swift Zoomin' #8
Yuta Koshizawa
October 23, 2021
Programming
2
430
Swift Zoomin' #8
Yuta Koshizawa
October 23, 2021
Tweet
Share
More Decks by Yuta Koshizawa
See All by Yuta Koshizawa
Swift Concurrency時代のiOSアプリの作り方
koher
13
3.8k
async/awaitやactorでiOSアプリ開発がどう変わるか Before & Afterの具体例で学ぶ
koher
8
5.3k
Swift Language Updates - Learn Languages 2021
koher
8
1.3k
先取り! Swift 6 の async/await
koher
13
3.2k
SwiftUIで勘違いした話
koher
1
2k
iOSエンジニアのための、SwiftからPythonのライブラリを使って機械学習する方法 / Machine Learning using Python from Swift for iOS Engineer
koher
5
9.9k
Generalized existentialが内部的にすでに存在しているか調べてみた / A Dive to Find Inner Generalized Existential Containers
koher
2
190
RPGで学ぶSwiftの値型 / Learning Value Types in Swift with RPG
koher
1
550
具体例とクイズで学ぶ、Swiftの4種類のエラーの使い分け
koher
3
5.8k
Other Decks in Programming
See All in Programming
%q is for Quine
koic
0
410
Becoming an Android Librarian (Android World Wide 2023 Jan)
skydoves
1
190
ちょうぜつ改め21世紀ふつうのソフトウェア設計
tanakahisateru
7
6.4k
tidy_rpart
bk_18
0
590
Findy - エンジニア向け会社紹介 / Findy Letter for Engineers
findyinc
2
42k
量子コンピュータ時代のプログラミングセミナー / 20221222_Amplify_seminar _route_optimization
fixstars
0
250
Remix + Cloudflare Pages + D1 で ポケモン SV のレンタルチームを検索できるアプリを作ってみた
kuroppe1819
4
1.3k
ipa-medit: Memory search and patch tool for IPA without Jailbreaking/ipa-medit-bh2022-europe
tkmru
0
130
Functional Data Engineering - A Blueprint for adopting functional principles in data pipeline
vananth22
0
180
OSC大阪 パスワード認証は人類には早すぎる ~ IDaaSを使ったソーシャルログインのすすめ ~
authyasan
7
1.4k
Jetpack Compose 完全に理解した
mkeeda
1
460
Spring BootとKubernetesで実現する今どきのDevOps入門
xblood
0
340
Featured
See All Featured
Designing for humans not robots
tammielis
245
24k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
6
840
Statistics for Hackers
jakevdp
785
210k
Product Roadmaps are Hard
iamctodd
38
7.7k
Designing the Hi-DPI Web
ddemaree
273
32k
Build The Right Thing And Hit Your Dates
maggiecrowley
22
1.4k
How GitHub (no longer) Works
holman
298
140k
Robots, Beer and Maslow
schacon
154
7.3k
The MySQL Ecosystem @ GitHub 2015
samlambert
240
11k
Why You Should Never Use an ORM
jnunemaker
PRO
49
7.9k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
351
21k
Fantastic passwords and where to find them - at NoRuKo
philnash
32
1.8k
Transcript
Swi$ Zoomin' #8 Actor ϋϯζΦϯ
Actor ͱ
ɾɾɾͷલʹ
Actor async/await Λଟ༻͢Δ
async/await ͷ؆୯ͳ෮श
ίʔϧόοΫʹΑΔඇಉظؔ // એݴ func downloadData(from url: URL, completion: @escaping (Data)
-> Void) // ར༻ downloadData(from: url) { data in // data Λ͏ॲཧ }
async/await ʹΑΔඇಉظؔ // એݴ func downloadData(from url: URL) async ->
Data // ར༻ let data = await downloadData(from: url) // data Λ͏ॲཧ
ίʔϧόοΫʹΑΔඇಉظؔ // એݴ func downloadData(from url: URL, completion: @escaping (Result<Data,
Error>) -> Void) // ར༻ downloadData(from: url) { result in do { let data = try result.get() // data Λ͏ॲཧ } catch { /* ΤϥʔϋϯυϦϯά */ } }
async/await ʹΑΔඇಉظؔ // એݴ func downloadData(from url: URL) async throws
-> Data // ར༻ do { let data = try await downloadData(from: url) // data Λ͏ॲཧ } catch { /* ΤϥʔϋϯυϦϯά */ }
࿅श༻ϦϙδτϦ h"ps:/ /github.com/koher/swi4-zoomin-8
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
Q1: ղྫ
async ؔ async ؔͷதͰ͔͠ݺͼग़ͤͳ͍ func foo() async func bar() async
{ await foo() // ✅ OK }
async ؔ async ؔͷதͰ͔͠ݺͼग़ͤͳ͍ func foo() async func bar() {
await foo() // ⛔ NG }
async ؔ async ؔͷதͰ͔͠ݺͼग़ͤͳ͍ func foo() async func bar() async
{ await foo() // ✅ OK } func baz() async { await bar() // ✅ OK } ...
async ؔΛ࠷ॳʹݺͼग़͢ͱ͖ʁ
ಉظ͔ؔΒͷ async ؔͷݺͼग़͠ override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated)
await foo() // ⛔ NG }
ಉظ͔ؔΒͷ async ؔͷݺͼग़͠ override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated)
Task { await foo() // ✅ OK } }
ಉظ͔ؔΒͷ async ؔͷݺͼग़͠ override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated)
Task(operation: { await foo() // ✅ OK }) }
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
Q2: UserViewController.viewDidAppear UserViewController User ͷใΛऔಘͯ͠දࣔ͢Δ View Controller Ͱ͢ɻ viewDidAppear
ͷதͰ downloadData ؔΛ ͍ɺ User ͱΞΠίϯΛऔಘͯ͠දࣔ͠·͢ɻ ͜ΕΛɺ async/await Ͱ࣮͞Εͨ downloadData ؔΛར ༻ͯ͠࠶࣮ͯ͠Լ͍͞ɻ
Q2: ղྫ
Actor ͱ
Actor ΫϥεʹࣅͨࢀরܕͰɺσʔλڝ߹ Λ͙ͨΊͷϘΠϥʔϓϨʔτ͕ෆཁʹͳ Δɻ ͦͷͨΊɺίʔυ͕؆͔ܿͭ҆શʹͳΔɻ
σʔλڝ߹ͱ
Χϯλʔͷσʔλڝ߹ final class Counter { private var count: Int =
0 func increment() -> Int { count += 1 return count } }
Χϯλʔͷσʔλڝ߹ let counter: Counter = .init() Task { print(counter.increment()) //
? } Task { print(counter.increment()) // ? }
Χϯλʔͷσʔλڝ߹ let counter: Counter = .init() Task { print(counter.increment()) //
1 } Task { print(counter.increment()) // 2 }
Χϯλʔͷσʔλڝ߹ let counter: Counter = .init() Task { print(counter.increment()) //
2 } Task { print(counter.increment()) // 1 }
Χϯλʔͷσʔλڝ߹ let counter: Counter = .init() Task { print(counter.increment()) //
2 } Task { print(counter.increment()) // 2 }
Χϯλʔͷσʔλڝ߹ final class Counter { private var count: Int =
0 func increment() -> Int { count += 1 Thread.sleep(forTimeInterval: 1.0) return count } }
Χϯλʔͷσʔλڝ߹ let counter: Counter = .init() Task { print(counter.increment()) //
1 } Task { print(counter.increment()) // 1 }
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 } }
σʔλڝ߹ͷରԠํ๏ • ϩοΫ • ʢ DispatchQueue ͷʣγϦΞϧΩϡʔ
γϦΞϧΩϡʔʹΑΔσʔλڝ߹ͷղফ 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) } } }
γϦΞϧΩϡʔʹΑΔσʔλڝ߹ͷղফ let counter: Counter = .init() Task { counter.increment {
print($0) } // 1 } Task { counter.increment { print($0) } // 2 }
γϦΞϧΩϡʔʹΑΔσʔλڝ߹ͷղফ 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) } } }
σʔλڝ߹ͷରԠํ๏ • ϩοΫ • ʢ DispatchQueue ͷʣγϦΞϧΩϡʔ • Actor
Actor ʹΑΔσʔλڝ߹ͷղফ final class Counter { private var count: Int
= 0 func increment() -> Int { count += 1 return count } }
Actor ʹΑΔσʔλڝ߹ͷղফ actor Counter { private var count: Int =
0 func increment() -> Int { count += 1 return count } }
Actor ʹΑΔσʔλڝ߹ͷղফ let counter: Counter = .init() Task { print(await
counter.increment()) } Task { print(await counter.increment()) }
Q3: BankAccount.deposit ࣍ͷΑ͏ͳ BankAccount ΫϥεΛ࣮͠ɺฒߦʹ༬ۚ͢Δ͜ͱ Ͱσʔλڝ߹͕ൃੜ͢Δ͜ͱΛ֬ೝͯ͠Լ͍͞ɻͦͷޙɺ BankAccount Λ actor ʹมߋ͠ɺσʔλڝ߹͕ࢭ͞ΕΔ͜ͱ
Λ֬ೝͯ͠Լ͍͞ɻ final class BankAccount { // ۜߦޱ࠲ var balance: Int = 0 // ༬ۚߴ func deposit(_ amount: Int) -> Int // ೖۚʢΓߴʣ }
Q3: ղྫ
Actor ͷಉҰΠϯελϯεͰͷϝιουݺͼग़͠ actor A { func foo() { ... }
func bar() { foo() // ಉظ } } await foo() // ඇಉظ
Q4: BankAccount.getInterest BankAccount ʹ getInterest ϝιουΛ࣮ͯ͠Լ͍͞ɻ getInterest ϝιου࣍ͷΑ͏ͳγάωνϟΛ࣋ͪɺҾͰ རΛड͚औΓɺͦΕΛݩʹརଉΛܭࢉͯ͠༬ۚߴΛมಈͤ͞ ্ͨͰɺ৽͍͠༬ۚߴΛฦ͢ͷͱ͠·͢ɻͨͩ͠ɺ༬ۚߴ
ͷมಈʹ deposit ϝιουΛར༻͢Δͷͱ͠·͢ɻ func getInterest(with rate: Double) -> Int
Q4: ղྫ
Actor ͷΠϯελϯεؒͰͷϝιουݺͼग़͠ actor A { func foo() { ... }
} actor B { func bar(a: A) async { await a.foo() // ඇಉظ } }
Actor ͷΠϯελϯεؒͰͷϝιουݺͼग़͠ actor A { func foo() { ... }
func bar(a: A) async { foo() // ಉظ await a.foo() // ඇಉظ } }
Actor ͷΠϯελϯεؒͰͷϝιουݺͼग़͠ actor A { func foo() { ... }
func bar(a: A) async { self.foo() // ಉظ await a.foo() // ඇಉظ } }
Actor ͷΠϯελϯεؒͰͷϝιουݺͼग़͠ actor A { func foo() { ... }
func bar(a: A) async { foo() // ಉظ await a.foo() // ඇಉظ } }
Q5: BankAccount.transfer BankAccount ʹ࣍ͷೋͭͷϝιουΛՃͯ͠Լ͍͞ɻ • ޱ࠲͔Βग़ۚ͢ΔͨΊͷ withdraw ϝιου • ޱ࠲ؒͰૹۚ͢ΔͨΊͷ
transfer ϝιου // ߴ͕Γͳ͍߹ΤϥʔΛ throw func withdraw(_ amount: Int) throws -> Int // Γߴ func transfer(_ amount: Int, to account: BankAccount) async throws
Q5: ղྫ
Sendable
Sendable 3 actor ڥքΛ͑ͯΓͱΓͰ͖Δ͜ͱΛද͢ϓϩτίϧ 3 h$ps:/ /github.com/apple/swi6-evolu9on/blob/main/proposals/0302-concurrent-value-and-concurrent- closures.md
final class User { let id: ID var name: String
var age: Int ... }
actor A { let user: User = ... } let
a: A = ... Task { let user = await a.user user.age += 1 // ⛔ } Actor ͷΩϡʔͷ֎Ͱ Actor ͷ෦ঢ়ଶΛมߋͰ͖ͯ͠·͏
final class User: Sendable { let id: ID var name:
String var age: Int ... } ⛔ ࢀরܕͰ var ϓϩύςΟΛ࣋ͭͷͰίϯύΠϧΤϥʔ
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
public struct User: Sendable { public let id: ID public
var name: String public var age: Int ... } public ͩͱ໌ࣔతʹద߹ͤ͞Δඞཁ͋Γ
struct User: Sendable { let id: ID var name: NSString
var age: Int ... } ⛔ Sendable Ͱͳ͍ϝϯόʔΛ࣋ͭͷͰίϯύΠϧΤϥʔ
final class User: Sendable { let id: ID let name:
String let age: Int ... } ✅ ࢀরܕͰΠϛϡʔλϒϧͳΒ OK
final class User: Sendable { let id: ID let firstName:
String let familyName: String let age: Int var name: String { "\(firstName) \(familyName)" } ... } ✅ Computed Property Λ͍࣋ͬͯͯΠϛϡʔλϒϧͳͷͰ OK
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! } ... }
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! } ... }
actor Counter { private var count: Int = 0 func
increment() -> Int { count += 1 return count } } actor ҉తʹ Sendable ʹద߹
ϓϩτίϧʹద߹ͤ͞ΒΕͳ͍߹ʁ
actor Foo { func bar(_ f: (String) -> Int) {
// // ... } }
actor Foo { func bar(_ f: @Sendable (String) -> Int)
{ // // ... } } Ϋϩʔδϟϓϩτίϧʹద߹Ͱ͖ͳ͍ͷͰ @Sendable Λ͏
final class User { var name: String ... } let
user: User = ... Task { let name = user.name // } @Sendable Ϋϩʔδϟ Sendable ͳ͔͠ΩϟϓνϟͰ͖ͳ ͍ʢ Task.init ʹ͢Ϋϩʔδϟ @Sendable ʣ
var a = 42 Task { print(a) // ⛔ }
@Sendable ΫϩʔδϟมΛΩϟϓνϟͰ͖ͳ͍
var a = 42 Task { [a] in print(a) //
✅ } by-value ΩϟϓνϟՄೳ
None
Actor-isolated Context ͷҾܧ͗
@nonescaping,
[email protected]
ʢ context ΛҾ͖ܧ͙ʣ actor A { func foo(values:
[Int]) { // A values.forEach { value in // A } } }
@escaping,
[email protected]
ʢ context ΛҾ͖ܧ͙ʣ actor A { func foo(value: Int)
{ // A let square: (Int) -> Int = { // @escaping // A $0 * $0 } print(square(value)) print(square(square(value))) } }
@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 Ͱͳ͍
Task.initʢ context ΛҾ͖ܧ͙ʣ actor A { func foo() { //
A Task { // @Sendable // A } } }
@_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
Task.initʢ context ΛҾ͖ܧ͙ʣ actor A { func foo() { //
A Task { // @Sendable // A } } }
Task.detachedʢ context ΛҾ͖ܧ͕ͳ͍ʣ actor A { func foo() { //
A Task.detached { // @Sendable // not A } } }
Actor-isolated Context ͷҾܧ͗ͷؔ͢Δ swi2-developers-japan ͰͷΓͱΓ11 11 h$ps:/ /discord.com/channels/291054398077927425/291054454793306112/890858321693769748
UserViewController
None
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) } } }
View Controller ʹϩδοΫΛॻ͖ͨ͘ͳ͍
ViewModel తͳΫϥεΛಋೖ
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 } }
Q6: UserViewState UserViewState ʹ loadUser ϝιουΛՃ͠ɺϢʔβʔ͓Α ͼϢʔβʔΞΠίϯΛऔಘ͢ΔίʔυΛ UserViewController ͔Βͯ͠Լ͍͞ɻ UserViewController
ʹ UserViewState ܕͷ state ϓϩύςΟΛ࣋ͨͤɺ state ͷঢ় ଶΛ View ʹө͢ΔΑ͏ʹͯ͠Լ͍͞ɻͳ͓ɺ͜ͷ Actor ʹؔ͋Γ·ͤΜɻ ! state.user Ͱͳ͘ state.$user ͱॻ͘͜ͱͰɺ user Λ Publisher ͱͯ͠औಘ͢Δ͜ͱ͕Ͱ͖·͢ɻ
Q6: ղྫ
loadUser Λฒߦʹݺͼग़ͯ͠େৎ͔ʁ
loadUser Ͱى͜ΓಘΔσʔλڝ߹ let data = try await downloadData(from: url) let
user = try JSONDecoder().decode(User.self, from: data) self.user = user // ͕͜͜ಉ࣌ʹ࣮ߦ͞ΕΔͱʁ
UserViewState Λ actor ʹ͍ͨ͠
Q7: actor UserViewState UserViewState Λ Actor ʹͯ͠Լ͍͞ɻ
Q7: ղྫ
Swi$ Concurrency Λͬͯ Publisher ΛߪಡͰ͖Δ
Publisher.values 5 for await value in publisher.values { ... }
5 h$ps:/ /developer.apple.com/documenta6on/combine/publisher/values-1dm9r
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
Q8: Publisher.values UserViewState ͷ user iconImage ͷߪಡΛɺ for await
Λ͏ܗͰॻ͖͑ͯԼ͍͞ɻ
Q8: ղྫ
await state ͷ await ͕ᓔಃ͍͠ for await user in await
state.$user.values { ... }
state ͷΩϡʔ͕ϝΠϯΩϡʔͳΒɾɾɾ
ܕΛ͑ͯΩϡʔͰอޢ͍ͨ͠
Global Actor 7 7 h$ps:/ /github.com/apple/swi6-evolu9on/blob/main/proposals/0316-global-actors.md
MainActor 8 8 h$ps:/ /github.com/apple/swi6-evolu9on/blob/main/proposals/0316-global-actors.md#the-main-actor
@MainActor @MainActor func foo() { // isolated // ϝΠϯεϨου }
@MainActor ͕༩͞ΕͨؔϝιουɺϓϩύςΟ MainActor ʹ actor-isolated
@MainActor @MainActor final class Foo { var value: Int =
42 // isolated func a() { // isolated // ϝΠϯεϨου } } @MainActor ͕༩͞ΕͨܕͷϝιουɺϓϩύςΟ MainActor ʹ actor-isolated
Q9: @MainActor UserViewState ʹ @MainActor Λ༩ͯ͠ɺ UserViewController ͱͷΓͱΓΛ MainActor ͷॲཧͱ
ͯ͠ѻ͑ΔΑ͏ʹͯ͠Լ͍͞ɻ
Q9: ղྫ
ObservableObject
UserViewState Λ ObservableObject ʹ final class UserViewState: ObservableObject { ...
}
UserViewState Λ ObservableObject ʹ • Swi%UI ͷҠߦΛݟਾ͑ͯ • Computed Property
@Published ʹͰ͖ͳ͍
UserViewState Λ ObservableObject ʹ • user iconImage Λݸผʹߪಡ͢ΔͷͰͳ͘ɺ objectWillChange9
Λߪಡ͢Δ 9 h$ps:/ /developer.apple.com/documenta6on/combine/observableobject/objectwillchange-2oa5v
Q10: ObservableObject UserViewState Λ ObservableObject ʹద߹ͤ͞ɺ user, iconImage Λݸผʹߪಡ͢ΔͷͰͳ͘ɺ objectWillChange
Λߪಡͯ͠ View ʹө͢Δܗʹमਖ਼ͯ͠Լ͍͞ɻ
Q10: ղྫ
Q10 Ͱ࣮ͨ͠ UserViewState Swi(UI Ͱͦͷ··ར༻Մೳ
Q11: Swi(UI UserViewController ͱಉ༷ͷݟͨɾڍಈΛ࣮ݱ͢Δ UserView Λ Swi%UI Ͱ࣮ͯ͠Լ͍͞ɻ ! @StateObject
Ͱ UserViewState Λอ࣋͠·͢ɻ @StateObject private var state: UserViewState
Q11: ղྫ