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
350
Swift Zoomin' #8
Yuta Koshizawa
October 23, 2021
Tweet
Share
More Decks by Yuta Koshizawa
See All by Yuta Koshizawa
async/awaitやactorでiOSアプリ開発がどう変わるか Before & Afterの具体例で学ぶ
koher
7
4.5k
Swift Language Updates - Learn Languages 2021
koher
8
1.1k
先取り! Swift 6 の async/await
koher
13
3k
SwiftUIで勘違いした話
koher
1
1.6k
iOSエンジニアのための、SwiftからPythonのライブラリを使って機械学習する方法 / Machine Learning using Python from Swift for iOS Engineer
koher
5
8.6k
Generalized existentialが内部的にすでに存在しているか調べてみた / A Dive to Find Inner Generalized Existential Containers
koher
2
160
RPGで学ぶSwiftの値型 / Learning Value Types in Swift with RPG
koher
1
500
具体例とクイズで学ぶ、Swiftの4種類のエラーの使い分け
koher
3
5.7k
Swiftで書かれたプログラムを1000倍速くした話
koher
6
2.9k
Other Decks in Programming
See All in Programming
JSのウェブフレームワークで高速なルーターを実装する方法
usualoma
1
1.9k
git on intellij
hiroto_kitamura
0
170
GoogleI/O2022 LT報告会資料
shinsukefujita1126
0
410
Modern Android Developer ~ 안내서
pluu
1
670
Angular-basierte Micro Frontends mit Module Federation @API Summit
manfredsteyer
PRO
0
120
エンジニアによる事業指標計測のススメ
doyaaaaaken
1
190
「混ぜるな危険」を推進する設計
minodriven
8
2.2k
[월간 데이터리안 세미나 6월] 스스로 성장하는 분석가 커리어 이야기
datarian
0
250
What's new in Android development tools まとめ
mkeeda
0
410
Android Compose Component - mapping.
taehwandev
0
140
Running Laravel/PHP on AWS (AWS Builders Day Taiwan 2022)
dwchiang
0
180
Cybozu GoogleI/O 2022 LT会 - Input for all screens
jaewgwon
0
400
Featured
See All Featured
A Modern Web Designer's Workflow
chriscoyier
689
180k
How GitHub (no longer) Works
holman
296
140k
No one is an island. Learnings from fostering a developers community.
thoeni
9
1.3k
Three Pipe Problems
jasonvnalue
89
8.7k
GraphQLの誤解/rethinking-graphql
sonatard
28
6.6k
YesSQL, Process and Tooling at Scale
rocio
157
12k
Documentation Writing (for coders)
carmenhchung
48
2.6k
Gamification - CAS2011
davidbonilla
75
3.9k
KATA
mclloyd
7
8.7k
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
119
28k
Designing for humans not robots
tammielis
241
23k
Code Review Best Practice
trishagee
43
9.3k
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, non-@Sendable ʢ context ΛҾ͖ܧ͙ʣ actor A { func foo(values:
[Int]) { // A values.forEach { value in // A } } }
@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))) } }
@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: ղྫ