Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
今日から使える実践的Swift Concurrency / Introducing Swift...
Search
giginet
PRO
September 28, 2021
Technology
410
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
今日から使える実践的Swift Concurrency / Introducing Swift Concurrency
giginet
PRO
September 28, 2021
More Decks by giginet
See All by giginet
🀄️ on swiftc
giginet
PRO
0
610
15年メンテしてきたdotfilesから開発トレンドを振り返る 2011 - 2026
giginet
PRO
2
500
Claude Codeログ基盤の構築
giginet
PRO
7
4.7k
Inside of Swift Export
giginet
PRO
1
3.6k
Swiftビルド弾丸ツアー - Swift Buildが作る新しいエコシステム
giginet
PRO
0
3k
SwiftでMCPサーバーを作ろう!
giginet
PRO
3
490
Creating Intuitive Developer Tool in Swift
giginet
PRO
0
1.7k
Mergeable Libraryで 高速なアプリ起動を実現しよう!
giginet
PRO
2
8.6k
5分でわかるExplicitly Built Modules
giginet
PRO
2
1.9k
Other Decks in Technology
See All in Technology
UniFi(ユニファイ)AIカメラの現場活用
ludicmind
1
440
Classmethod Company Deck
satoshi7
1
680
Persisting Massive Data with Rails ~Designing for 60 billion rows, and fighting the bottlenecks~ Railsで挑む大量データ永続化 〜600億件を永続化するための設計とボトルネックとの戦い〜
occhi
0
730
PHPカンファレンス関西の招待状 / Invitation to PHP Conference Kansai
ykanoh
0
110
AI駆動で「他アプリの音を加工できる」アプリを作った
satomasahiro2005
0
150
Metadata - Section 1: HTML Basics
mohab1
0
120
しろおびから始める!ログ活用の第一歩
shumei_ito
0
950
AI コーディングと人間の認知スループット / CTO Night & Day 2026 Morning Session / AI Coding and Human Cognitive Throughput
prog893
4
1k
How to Design Configuration for Rails Applications
willnet
1
770
DGX Spark でどこまでハッキングができるのか?
kash1064
3
1.3k
10年後、Engineering 行為を剥奪されるとして、わたしたちは、どう、何を創ればいいだろうか。 / If Engineering Is Taken Away in 10 Years, How and What Should We Create?
bitkey
PRO
1
190
スキマバイトという雇用形態は存在しないので、雇用期間1日のアルバイト契約を事業者とワーカーが結ぶ手続きを支えるのがタイミーなんです
akitoshiga
0
200
Featured
See All Featured
職位にかかわらず全員がリーダーシップを発揮するチーム作り / Building a team where everyone can demonstrate leadership regardless of position
madoxten
69
67k
Six Lessons from altMBA
skipperchong
29
4.5k
Ruling the World: When Life Gets Gamed
codingconduct
0
400
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
659
62k
Hiding What from Whom? A Critical Review of the History of Programming languages for Music
tomoyanonymous
3
1.3k
Digital Ethics as a Driver of Design Innovation
axbom
PRO
1
460
Measuring Dark Social's Impact On Conversion and Attribution
stephenakadiri
2
310
The Language of Interfaces
destraynor
162
27k
Money Talks: Using Revenue to Get Sh*t Done
nikkihalliwell
0
520
Visual Storytelling: How to be a Superhuman Communicator
reverentgeek
2
710
[SF Ruby Conf 2025] Rails X
palkan
3
1.5k
The browser strikes back
jonoalderson
0
1.8k
Transcript
ࠓ͔Β͑Δ࣮ફతSwift Concurrency @giginet 1
ࣗݾհ • @giginet • Core Contributor of Carthage/fastlane/ XcodeGen etc...
• https://github.com/giginet • https://twitter.com/giginet • ΫοΫύου(2015/4~) ϞόΠϧج൫ ෦ • ؾܰʹmention͍ͯͩ͘͠͞ʂ 2
iOSDC ͠·ͨ͠ େنͳΞϓϦͷϚϧνϞδϡʔϧߏͷ࣮ફ by giginet | τʔΫ | iOSDC Japan
2021 #iosdc - fortee.jp 3
ٕज़ސgiginet͞ΜΠϯλϏϡʔ ίϛϡχςΟ׆ಈOSS׆ಈͷ ൿ݃Λฉ͍ͯΈ·ͨ͠ | Money Forward Engineers' Blog 4
Agenda • Swift Concurrency·ͱΊ • جຊฤ • Structured Concurrency •
actor 5
• ࣮ફతSwift Concurrency • طଘͷΞϓϦͷҠߦ • Α͋͘Δ࣮ΛConcurrencyԽͯ͠ΈΑ͏ • callbackύλʔϯ •
Delegateύλʔϯ • RxSwiftͱͷڠௐ • σΟεΧογϣϯ 6
Swift Concurrency·ͱΊ ͍Ζ͍Ζࢿྉ͕͋ΔͷͰجຊͦͪΒΛݟΕྑ͍ɻࠓճ͠ ͍ํ͚ʹ͜ΕΛϕʔεʹ؆୯ʹઆ໌͍͖ͯ͠·͢ɻ • WWDC 2021ͷSwiftͷฒߦॲཧؔ࿈ͷηογϣϯҰཡ - Qiita •
async/awaitactorͰiOSΞϓϦ։ൃ͕Ͳ͏มΘΔ͔ Before & Afterͷ۩ମྫͰֶͿ - Speaker Deck • Swift Concurrency νʔτγʔτ 7
Swift Concurrencyجຊฤ 8
• Α͋͘ΔͭɻcallbackΛड͚औͬͯResultͰΒ͏ Before func downloadData(from url: URL, completion:@escaping (Result<Data, Error>)
-> Void) downloadData(from: url) { result in switch result { case .success(let data): // data Λ͏ॲཧ case .failure(let error): // ΤϥʔϋϯυϦϯά } } 9
After func downloadData(from url: URL) async throws -> Data do
{ let data = try await downloadData(from: url) // data Λ͏ॲཧ } catch { // ΤϥʔϋϯυϦϯά } 10
ྫ2 • ·ͣϢʔβʔσʔλͷJSONΛΒͬͯɺͦͷJSONΛσίʔ υɺͦͷதʹ͋ΔURL͔Βը૾Λऔಘ͢Δ 11
func fetchUserIcon(for id: User.ID, completion: @escaping (Result<Data, Error>) -> Void)
{ let url: URL = .init(string: "https://koherent.org/fake-service/api/user?id=\(id)")! downloadData(from: url) { data in // (1) do { let data = try data.get() let user = try JSONDecoder().decode(User.self, from: data) downloadData(from: user.iconURL) { icon in // (2) do { let icon = try icon.get() completion(.success(icon)) } catch { completion(.failure(error)) } } } catch { completion(.failure(error)) } } } 12
After func fetchUserIcon(for id: User.ID) async throws -> Data {
let url: URL = .init(string: "https://koherent.org/fake-service/api/user?id=\(id)")! let data = try await downloadData(from: url) let user = try JSONDecoder().decode(User.self, from: data) let icon = try await downloadData(from: user.iconURL) return icon } • Θ͔Γ͍͢ • खଓ͖తʹॻ͚Δ 13
ྫ3 • JSONͷதʹ2ͭͷը૾URLؚ͕·Ε͍ͯΔ(smallIconImage, largeIconImage) • ·ͣJSONΛऔಘ͠ɺؚ·ΕΔը૾Λฒߦ(Parallel)ͯ͠μϯ ϩʔυ͍ͨ͠ 14
• JSONͷऔಘޙʹը૾ͷऔಘʢґଘؔˠίʔϧόοΫͷ࿈ʣ • 2ͭͷը૾Parallelʹऔಘ͢Δඞཁ͕͋Δ • GCDͷDispatchGroupͳͲΛར༻ 15
! func fetchUserIcons(for id: User.ID, completion: @escaping (Result<(small: Data, large:
Data), Error>) -> Void) { downloadData(from: url) { data in // (1) do { let data = try data.get() let user = try JSONDecoder().decode(User.self, from: data) let group: DispatchGroup = .init() var smallIcon: Result<Data, Error>! group.enter() downloadData(from: user.smallURL) { icon in smallIcon = icon group.leave() } var largeIcon: Result<Data, Error>! group.enter() downloadData(from: user.largeURL) { icon in largeIcon = icon group.leave() } group.notify(queue: .global()) { do { let icons = try (small: smallIcon.get(), large: largeIcon.get()) completion(.success(icons)) } catch { completion(.failure(error)) } } } catch { completion(.failure(error)) } } } 16
After let url: URL = .init(string: "https://koherent.org/fake-service/api/user?id=\(id)")! let data =
try await downloadData(from: url) let user = try JSONDecoder().decode(User.self, from: data) async let smallIcon = try await downloadData(from: user.smallURL) async let largeIcon = try await downloadData(from: user.largeURL) let icons = try await (small: smallIcon, large: largeIcon) 17
Structured Concurrency • ෳͷasyncॲཧΛߏԽ࣮ͯ͠ߦ͢ΔΈ • Task • ্هͷྫͷΑ͏ʹෳͷλεΫΛฒߦͯ͠ߦͬͨΓ • ࢠλεΫΛ࡞ͬͨΓ
• Ωϟϯηϧͨ͠Γ • ͕ՄೳʹͳΔ 18
• swift-evolution/0304-structured-concurrency.md at main · apple/swift-evolution • Explore structured concurrency
in Swift - WWDC21 - Videos - Apple Developer 19
async-let • 1ͭͷTaskͰෳͷඇಉظॲཧ(ࢠλεΫ)ΛΒͤͯͪड͚ Ͱ͖Δ async let smallIcon = try await
downloadData(from: user.smallIconURL) // (foo) async let largeIcon = try await downloadData(from: user.largeIconURL) // (bar) // ͳΜ͔͍ॲཧ (Task) let icons = try await (small: smallIcon, large: largeIcon) 20
21
Task • ඇಉظؔTaskͰϥοϓ͢Δ͜ͱͰಉظͷੈք͔ΒͰ࣮ߦ Ͱ͖Δ • ಉظੈքͱඇಉظੈքͷڮ͠ʹ͑Δ func syncMethod() { Task
{ await doSomething() } } 22
Cancel func syncMethod() { let task = Task { await
doSomething() } task.cancel() } • TaskcancelͰ͖Δ • ϘλϯΛԡͨ͠ΒTaskΛൃՐ͢Δ͕ɺऴΘΔલʹΩϟϯηϧϘ λϯΛԡͨ͠ΒऴྃΈ͍ͨͳͷ͕ग़དྷΔ • ࢠλεΫ·ͱΊͯΩϟϯηϧ͞ΕΔ 23
AsyncSequence • asyncʹ͕औΕΔiterator • ϑΝΠϧI/Oͱ͔ • streamͱ͔(WebSocketͱ͔) • ࢠλεΫΛಈతʹ૿ͯ͠ɺiteratorͷΑ͏ʹѻ͑Δ Meet
AsyncSequence - WWDC21 - Videos - Apple Developer 24
actor • ඇಉظͷσʔλڝ߹Λղܾ͢Δ͘͠Έ • ॲཧΛඇಉظʹͨ͠ΓɺΩϡʔΛ੍ͨ͠ΓɺϩοΫͷऔಘΛ ࣗಈతʹͬͯ͘ΕΔ 25
σʔλڝ߹ͱ • ҎԼͷ߹ɺյΕͯ͠·͏ let counter: Counter = .init() DispatchQueue.global().async {
print(counter.increment()) // ? } DispatchQueue.global().async { print(counter.increment()) // ? } 26
actor Counter { private var count: Int = 0 func
increment() -> Int { count += 1 return count } } 27
• actorͷશͯͷϓϩύςΟϝιου͕ඇಉظʹͳΔͨΊɺ σʔλڝ߹͔ΒकΒΕΔ • actor෦ʹઐ༻ͷΩϡʔ(serial executor)Λอ͓࣋ͯ͠ΓɺӅ ṭ͞ΕΔ await counter.increment() 28
• GCDͰ࣮ݱ͠Α͏ͱ͢ΔͱɺϩοΫsemaphore͕ඞཁʹͳΔ final class Counter { private let queue: DispatchQueue
= .init(label: ...) private var count: Int = 0 func increment(completion: @escaping (Int) -> Void) { queue.async { [self] in count += 1 completion(count) } } } 29
Global Actor • actorͷ༻ΩϡʔΛಛఆͷΩϡʔʹ੍ݶ͢ΔͨΊͷ͘͠Έ • SEͰҙΩϡʔ͕ఏҊ͞Ε͍ͯΔ͕ɺݱࡏMainActorͷ Έ 30
@MainActor final class MyViewController: UIViewController { func doSomething() { }
} • actorͰͳ͘ɺطଘͷΫϥεʹ@MainActorΛ͚Δͱϝϯό͕ શ෦asyncʹͳΓϝΠϯεϨουͰ࣮ߦ͞ΕΔ 31
• ֎͔ΒϝιουϓϩύςΟ͕asyncʹݟ͑Δ • MainActorʹ͍Δͷͷத͔Β͔͋ͨಉظతʹݺΔ • MainActorҎ֎͔Βͷ࣮ߦΛ͋Δఔ͙͜ͱ͕ग़དྷΔ • UIͷߋ৽ͳͲɺϝΠϯεϨουҎ֎Ͱ࣮ߦ͞Εͯཉ͘͠ͳ͍ ͷΛؒҧͬͯݺͳ͍Α͏ʹίϯύΠϧ࣌ʹอޢ͢Δ͜ͱ ͕Ͱ͖Δ
32
@MainActor func reloadData() { // ϝΠϯεϨουͰ࣮ߦͯ͠ཉ͍͠ॲཧ } @MainActor final class
SomeViewController: UIViewController { override func viewDidLoad() { reloadData() } } func callReloadData() { let vc = SomeViewController() vc.reloadData() // ϝΠϯεϨουอূ͕ͳ͍ͷͰasync͡Όͳ͍ͱݺͳ͍ } 33
UIViewController • υΩϡϝϯτΛݟΔײ͡ɺUIViewControllerશͯ@MainActor ʹͳ͍ͬͯΔ • https://developer.apple.com/documentation/uikit/ uiviewcontroller?changes=latest_minor • ࣮ࡍʹ͜ΕΛΔͱଟ͘ͷϓϩμΫτͰޓੑ͕ҡ࣋Ͱ͖ͳ͘ ͳΔΑͳͱࢥ͍ͬͯΔɻͳΜ͔Έͨײ͡దԠ͞Εͯແͦ͞͏
ͰΑ͘Θ͔Βͳ͍ ! 34
35
class User { var name: String var age: Int }
actor Session { var currentUser: User? } 36
func run() async { let session = Session() let user
= await session.currentUser user?.age += 1 } 37
38
ͳͥʁ • UserࢀরܕͰࢀরઌͰ͕มΘΓ͏Δ • ෳͷλεΫͰࢀর͍ͯ͠Δؒʹ1ܦͬͯage͕૿͑ΔՄೳੑ ͕͋Δ • σʔλڝ߹ͷةݥੑ͕͋Δ 39
Sendable • ͜ͷܕ͕actorؒͰΓͱΓͰ͖Δ͜ͱΛprotocolʹ௨͢Δ protocol(marker protocol) • swift-evolution/0302-concurrent-value-and-concurrent- closures.md at main
· apple/swift-evolution 40
class User: Sendable { let user: String let age: Int
} • Sendableʹద߹͢Δ͜ͱͰίϯύΠϥʹ҆શʹड͚͠Ͱ͖Δ ܕͰ͋Δ͜ͱΛ௨ • ্هͷΑ͏ͳΠϛϡʔλϒϧΫϥε(varΛ࣋ͨͣʹҰ࡞ͬͨ ΒมΘΒͳ͍)SendableʹͰ͖Δ 41
• ͜ͷܯࠂXcode 13.0(Swift 5.5)ͷ࣌Ͱແޮʹͳ͍ͬͯΔ (؇͔ͳҠߦͷͨΊ) • Swift 6.0͔Βܯࠂ͕ඪ४Ͱग़ΔΑ͏ʹͳΔͷͰিܸʹඋ͑Δ • -warn-concurencyΦϓγϣϯͰܯࠂΛ༗ޮԽͰ͖Δ
42
• Swift ConcurrencyͷwithTaskCancellationHandlerͱSendable - cockscomblog? • Concurrency in Swift 5
and 6 - Evolution / Discussion - Swift Forums 43
Concurrencyͷར • ॲཧͷߏԽ • ॲཧͷྲྀΕΛಡΈԼ͍͍ͤͯ͢ • ݕࠪྫ֎ͷੵۃతͳ༻ • σʔλڝ߹ͷίϯύΠϧ࣌νΣοΫ •
࣮ߦεϨουͷίϯύΠϧ࣌νΣοΫ 44
Special Thanks • ͜͜·ͰͷεϥΠυͷ΄ͱΜͲ @koher ͞Μͷࢿྉͷ௨ΓͰ ͢ ! • ૉΒ͍͠ͷͰαϙʔτ͠·ͨ͠ɻ͋Γ͕ͱ͏͍͟͝·͢
45
໌͔Β͑ΔSwift Concurrency • طଘͷΞϓϦΛ෦తʹSwift ConcurrencyରԠ͍ͯ͘͠ςΫ χοΫ 46
Swift Concurrency backport • ݱࡏɺConcurrencyiOS 15Ҏ্Ͱ͔͠༻Ͱ͖ͳ͍͕ɺiOS 13 Ҏ্Ͱ༻Ͱ͖ΔΑ͏ʹ͢Δܭը͕͋Δ(backport) • swiftcʹϚʔδ͞Ε͍ͯΔ͕ɺXcode
13.0ͷஈ֊Ͱ༻ Ͱ͖ͳ͍ • ΞϓϦʹConcurrencyʹඞཁͳϥϯλΠϜΛΈࠐΜͰ͠· ͍ɺiOS 15ະຬͰ࣮ߦͰ͖ΔΑ͏ʹ͍ͯ͠ΔΒ͍͠ Add an option to build the concurrency library for back 47
Xcode 13 Release Notes | Apple Developer Documentation 48
طଘͷΞϓϦͷ࣮ફతͳasync/awaitԽ ͜ͷηογϣϯ͕ྑ͔ͬͨ Swift concurrency: Update a sample app - WWDC21
- Videos - Apple Developer 49
ߟ͑Δ͜ͱ • εϨουຖʹactorΛ • ϝΠϯεϨουͰͷ࣮ߦΛڧ੍͍ͨ͠ॲཧ(UI) • backgroundʹڲਖ਼͍ͨ͠ॲཧʢI/Oॏ͍ॲཧʣ • Ͳ͜ͰΑ͍ॲཧ •
asyncΠϯλʔϑΣΠεରԠ 50
51
52
53
• UIViewMainActorʹ • ֎෦ͱͷI/O(͜͜ͰHealthKit)actorʹͯ͠ඇಉظͰѻ͏ • ͲͷεϨουͰॲཧ͖͔͢Λ೦಄ʹΈସ͑Δͷ͕େࣄ 54
طଘͷ࣮ͷasyncԽ • Α͋͘Δૉͳඇಉظϝιου(completion) struct ImageDownloader { func downloadImage(url: URL, completion:
@escaping (Result<UIImage, Error>) -> Void) { var request = URLRequest(url: url) request.httpMethod = "GET" let task = URLSession.shared.dataTask(with: request) { (data, response, error) in if let error = error { completion(.failure(error)) } else { let image = data.flatMap(UIImage.init(data:)) ?? UIImage() completion(.success(image)) } } task.resume() } } 55
• withCheckedContinuation asyncͰͳ͍࣮Λϥοϓͯ͠؆୯ ʹasyncΠϯλʔϑΣΠεʹมͰ͖Δ public func withCheckedContinuation<T>(_ body: (CheckedContinuation<T, Never>)
-> Void) async -> T public func withCheckedThrowingContinuation<T>(_ body: (CheckedContinuation<T, Error>) -> Void) async throws -> T 56
@available(iOS 15.0, *) extension ImageDownloader { func downloadImage(url: URL) async
throws -> UIImage { try await withCheckedThrowingContinuation { continuation in downloadImage(url: url) { result in continuation.resume(with: result) } } } } 57
ࣗಈੜͯ͘͠ΕΔ • Editor > Refactor > Add Async Wrapper •
completion൛ͷϝιουʹasync interfaceΛੜ͢ • Editor > Refactor > Add Async Alternative • async൛ͷϝιουʹલํޓ༻ͷcompletion handlerΛੜ ͢ 58
59
Delegateύλʔϯͷasync/awaitԽ • جຊతʹಉ͡ͰCheckedContinuationΛอ࣋͢Δͱྑͦ͞͏ ྫɿURLSessionDataDelegateͷasync/awaitԽ 60
struct HTTPRedirectResolver { func resolve(_ url: URL) async throws ->
URL? { try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<URL?, Error>) in let delegate = Delegate(activeContinuation: continuation) let urlSession = URLSession(configuration: .default, delegate: delegate, delegateQueue: nil) let request = URLRequest(url: url) urlSession.dataTask(with: request).resume() } } private class Delegate: NSObject, URLSessionDataDelegate { private let activeContinuation: CheckedContinuation<URL?, Error> init(activeContinuation: CheckedContinuation<URL?, Error>) { self.activeContinuation = activeContinuation } func urlSession(_ session: URLSession, task: URLSessionTask, willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest, completionHandler: @escaping (URLRequest?) -> Void) { let newURL = request.url activeContinuation.resume(returning: newURL) completionHandler(nil) } func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { activeContinuation.resume(returning: dataTask.currentRequest?.url) } func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { guard let error = error else { return activeContinuation.resume(returning: nil) } activeContinuation.resume(throwing: error) } } } 61
let resolver = URLRedirectResolver() let url = "http://moved-url.com" // redirect͢Δ
let redirectedURL = try await resolver.resolve(url) 62
RxͰͷ༻ • async/awaitΛObservableʹมͯ͠ڠௐͯ͑͠ΔΑ͏ʹ͢Δ 63
extension ObservableType { public static func create(throwingAsync callee: @escaping ()
async throws -> Element) -> Observable<Element> { Observable<Element>.create { observer in let task = Task { do { let result = try await callee() observer.onNext(result) } catch { observer.onError(error) } } return Disposables.create { task.cancel() } } } } 64
Observable.create { try async doSomething() } // Observable<T> .asDriver(onErrorJustReturn: [])
.drive(onNext: { _ in }) .dispose(by: disposeBag) • TaskΛ͏͜ͱͰasyncΛӅṭ͍ͯ͠Δɻಉظͷੈք͔Β͑Δ 65
ײɺٞ • ͍͑͟ΔΑ͏ʹͳͬͨΒ෦తʹऔΓೖΕ͍ͯ͘ͷ༰қͦ ͏ • VS Rx • ௨৴ͷͪड͚ͳͲʹRxΛར༻͍ͯ͠Δ߹ͳͲasync/ awaitʹஔ͖͑ͯγϯϓϧʹॻ͚ͦ͏͕ͩɺશʹஔ͖͑
ΒΕΔΘ͚Ͱແͦ͞͏ 66
• Actor • ࠓҎ্ʹUIͱͦΕҎ֎ͷͷ֊Խ͕ॏཁʹࢥ͑Δ • Viewશͯ MainActor ʹͯ͠͠·͍ɺnonisolatedͳॲཧ ࣋ͨͤͳ͍ੈք͕ΩϨΠͦ͏ 67
• ࠓback portͷ͕͋ͬͯɺطଘͷΞϓϦͰࠓ͙͢͏ͷ ͍͠ • Xcode 13.1ͱ͔ૣ͍ஈ֊Ͱಈ͘Α͏ʹͳΓͦ͏ͳ༧ײ • Development TargetΛiOS
15ʹͯ͠༡Ϳ͜ͱͰ͖ͨ 68
͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠ 69