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
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
giginet
PRO
September 28, 2021
Technology
400
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
590
15年メンテしてきたdotfilesから開発トレンドを振り返る 2011 - 2026
giginet
PRO
2
490
Claude Codeログ基盤の構築
giginet
PRO
7
4.7k
Inside of Swift Export
giginet
PRO
1
3.5k
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.5k
5分でわかるExplicitly Built Modules
giginet
PRO
2
1.9k
Other Decks in Technology
See All in Technology
Amazon Bedrock Agents ClassicからAmazon Bedrock AgentCoreへ移行した際、ガードレール設定が2箇所に割れた話
matsunobu
0
120
技術的負債から考える、AI時代のエンジニアリング投資 — ビズリーチの技術的負債と向き合った経験から、変更し続けられるソフトウェアを考える/ technical-debt-con2026
visional_engineering_and_design
4
3.9k
認知負荷を吸収し、プロダクトをまたぐPR Preview基盤の設計事例
taiki45
2
440
AgentCore Runtime上にAgentic Coding基盤を構築・展開する際の設計ポイントと限界点 / Design considerations and limitations when building an agentic coding platform on AgentCore Runtime
har1101
7
780
覗いてみよう 関数型ビジュアル言語×2Dグラフィックスの世界
yohyamasaki
0
130
Harness Engineering on Rails
joelq
0
660
AIエージェントの一手は 誰も見ていない - Falco拡張OSS「Prempti」とeBPFで サーバーレス実行基盤を二層防御する
keitah
1
600
Hermes Agentで、AIが調べて記事を書くブログを作った話
blueb
0
110
作って終わりじゃないサーバーレス 〜9年運用する大規模EC物流API基盤の設計・運用のリアル〜
zozotech
PRO
0
560
[Kiro Meetup #7] Kiro Crew Dive Deep
konippi
0
380
特殊変数大全
dak2
0
150
その Lambda、8分で 管理者権限まで奪われます
k1nakayama
7
3.8k
Featured
See All Featured
Abbi's Birthday
coloredviolet
4
10k
Leadership Guide Workshop - DevTernity 2021
reverentgeek
1
370
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
141
35k
The Curious Case for Waylosing
cassininazir
1
540
First, design no harm
axbom
PRO
2
1.3k
Leveraging Curiosity to Care for An Aging Population
cassininazir
1
520
The SEO identity crisis: Don't let AI make you average
varn
0
560
Building a Modern Day E-commerce SEO Strategy
aleyda
45
9.2k
How to Think Like a Performance Engineer
csswizardry
28
2.8k
More Than Pixels: Becoming A User Experience Designer
marktimemedia
3
530
Visual Storytelling: How to be a Superhuman Communicator
reverentgeek
2
690
Un-Boring Meetings
codingconduct
0
430
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