<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.0">Jekyll</generator><link href="https://pofat.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://pofat.github.io/" rel="alternate" type="text/html" /><updated>2022-01-09T17:22:41+00:00</updated><id>https://pofat.github.io/feed.xml</id><title type="html">Pofat’s Zoo</title><subtitle>Funny, techie and Swifty</subtitle><author><name>true</name></author><entry><title type="html">any Protocol Is Now Existential</title><link href="https://pofat.github.io/2021/12/18/any-Protocol-Is-Now-Existential.html" rel="alternate" type="text/html" title="any Protocol Is Now Existential" /><published>2021-12-18T00:00:00+00:00</published><updated>2021-12-18T00:00:00+00:00</updated><id>https://pofat.github.io/2021/12/18/any-Protocol-Is-Now-Existential</id><content type="html" xml:base="https://pofat.github.io/2021/12/18/any-Protocol-Is-Now-Existential.html">&lt;p&gt;在 Swift 3 後， source code breaking change 已經少有， 最近有個 &lt;a href=&quot;https://github.com/apple/swift-evolution/blob/main/proposals/0335-existential-any.md&quot;&gt;evolution&lt;/a&gt; 可能會引來部分的 breaking change 卻依然值得， 那就是用 protocol 標識為一個物件的型別時， 需要加上 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;any&lt;/code&gt; 的前綴， 比如原本是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;let foo: FooProtocol = FooConformance()&lt;/code&gt; 會改成  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;let foo: any FooProtocol = FooConformance()&lt;/code&gt;。&lt;/p&gt;

&lt;h2 id=&quot;萬惡-pat&quot;&gt;萬惡 PAT&lt;/h2&gt;

&lt;p&gt;把 protocol 這樣使用， 其實並不是在告訴 compiler &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo&lt;/code&gt; 這個變數是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FooProtocol&lt;/code&gt; 這個型別， 而是讓 compiler 建了一個 existential container，但使用上和指示型別一模一樣，這樣引起了不少混淆， 因為它們其實代表的意義不同， 況且如果 protocol 裡有 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Self&lt;/code&gt; 或&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;associatedtype&lt;/code&gt; 在 &lt;a href=&quot;https://github.com/apple/swift-evolution/blob/main/proposals/0309-unlock-existential-types-for-all-protocols.md&quot;&gt;SE-309&lt;/a&gt; 之前也不能這樣用， compiler 會跟你抱怨 ` XXX can only be used as a generic constraint because it has “Self or associated type requirements”&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;，常常讓使用者搞混情況和撞牆，常見的解法是照 standard library 裡面教我們的 type erasure ， 做出如同 &lt;/code&gt;AnyHashable` 之類的包裝來使用， 我們在以前的&lt;a href=&quot;https://pofat.dev/2019/05/21/重新檢視-swift-的-protocol-二.html&quot;&gt;文章&lt;/a&gt;有討論過。&lt;/p&gt;

&lt;p&gt;總之，雖然 Swift 帶給我們 Protocol Oriented Programming 的思維， 也允許我們在只需要關心變數間的值時不需要在乎它們之間的型別關係，同時卻也帶來了混淆和多種限制， 曾見大膽發文說要是 Swift Protocol 不能全部都可做 existential container 就不是真正有用啦！&lt;/p&gt;

&lt;h2 id=&quot;解放&quot;&gt;解放&lt;/h2&gt;

&lt;p&gt;所以 &lt;a href=&quot;https://github.com/apple/swift-evolution/blob/main/proposals/0309-unlock-existential-types-for-all-protocols.md&quot;&gt;SE-309 Unlock existentials for all protocols&lt;/a&gt; 總算不負眾望地解決了這件事，實作的思想大致上可以說以前開發者做 type erasure 的工作由 compiler 來幫忙完成， 連因為有 Self 或 associated type 而不能建立existential container 這個臭名昭彰的 compile error 都被&lt;a href=&quot;https://github.com/apple/swift/pull/33767/files#r640209183&quot;&gt;拿掉了&lt;/a&gt;，可喜可賀啊（我也一樣會想念這個訊息的）！現在我們可以這樣寫了：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;kd&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Copyable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;copy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Self&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Copyable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;copy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Protocol 的束縛至此完全被解放，不過使用上混淆的狀況仍未解決， 比如從目前使用語法上的角度來看， &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;func foo(s: Sequence)&lt;/code&gt; 與 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;func foo&amp;lt;S: Sequence&amp;gt;(s: S)&lt;/code&gt; 幾乎感覺不出差異， 其實 existential type 和 generic parameter 本意上是大不相同：前者的本意是做為值層面的抽象 (value-level abastraction），你可以只關注它的值而不用理解型別之間的關係， 型別資訊等於被抺除了；而後者則是類型的抽象。&lt;/p&gt;

&lt;h2 id=&quot;語法影響想法&quot;&gt;語法影響想法&lt;/h2&gt;

&lt;p&gt;一旦 SE-309 出來， 大家能夠更輕鬆地寫出 existential container （因為語法也沒變， 你可以用的場景變多而已）， 這固然是好事， 不過也可能造成過度濫用， 只因這樣寫實在太輕鬆了，可能會導致大家心智模型被其簡便性引誘， 而在「該權衡抽象與否或何種抽象的時候」直接略過思考而採用 existential container，這就是 &lt;a href=&quot;https://github.com/apple/swift-evolution/blob/main/proposals/0335-existential-any.md&quot;&gt;SE-335 Introduce existential any&lt;/a&gt; 的任務， 在&lt;strong&gt;語法層面上就做出區別&lt;/strong&gt;，比如接續上面的例子， 我們可以寫出 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;let copy: any Copyable = SomeConformance()&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;雖然語法上有 breaking change ，但這個改變相對單純，是能夠做出migration tool 來自動轉移的， 為了緩和這個過程，目前打算 Swift 5.6 開始可以接受  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;any&lt;/code&gt; 的寫法， 在 Swift 6 才強制要在 existential type 加上 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;any&lt;/code&gt; ，值得一提的是這個語法可以使用在 typealias 或 associated type 裡：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;kd&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;P&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Requirements&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;associatedtype&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;A&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;S1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Requirements&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;typealias&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;A&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;any&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;P&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// okay&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;metatype&quot;&gt;Metatype&lt;/h2&gt;

&lt;p&gt;除此之外還會影響到 metadata type的寫法， metadata 指的是用來描述 type 的相關資訊， 比如某 struct 裡有什麼 property ，其類型是 struct 等， 先回憶一下現有相關的語法：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.Type&lt;/code&gt; ：即 Swift metadata 的 Type，比如 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Int.Type&lt;/code&gt; 就是 Int 它 metadata 的型別；比較特別的是 protocol， 同樣的寫法並不是指 protocol 的 metatype 而是任何 conform 該 protocol 的 metatype ，Apple 稱其為 &lt;strong&gt;existential metatype&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.self&lt;/code&gt;：靜態的 metatype (相對的是使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;type(of:)&lt;/code&gt; 來取得動態的 metatype），舉例說明： &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;let intMetatype: Int.Type = Int.self&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.Protocol&lt;/code&gt; ：如果我們需要的是 &lt;strong&gt;protocol 本身的 metatype&lt;/strong&gt;，則要使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MyProtocol.Protocol&lt;/code&gt; 來取得&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;引入 any後， 假如我們有一個 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;protocol Q {}&lt;/code&gt; ，上述各表達方式會變成：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Q.Type&lt;/code&gt; -&amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;any Q.Type&lt;/code&gt; ，因為它是 existential (any) metatype (.Type)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Q.self&lt;/code&gt; -&amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(any Q).self&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Q.Protocol&lt;/code&gt; -&amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(any Q).Type&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;額外討論&quot;&gt;額外討論&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Any&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AnyObject&lt;/code&gt; 其實也都是 protocol， 最常見的使用情境都是用做 existential container ， 基於 evolution 的討論， 如果要讓其語法更一致的可能要將其各改名為 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;any Value&lt;/code&gt; 與 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;any Object&lt;/code&gt; ， 只是這會造成更大範圍的 breaking change ，而這兩個特殊存在想必大家也都用得很習慣， 對它的心智模型也沒有歪到哪去， 目前仍傾向保留現況。&lt;/p&gt;

&lt;p&gt;另外就是更簡潔的 protocol with associated value extension 寫法&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;kd&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Array&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;mutating&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;contentsOf&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;newElements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Sequence&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Element&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;至此， Swift protocol 的最後一哩路也算是終於打通了吧！
用一張圖(&lt;a href=&quot;https://twitter.com/jckarter/status/1453397244334329856?s=20&quot;&gt;出處&lt;/a&gt;)來表示整個 evolution 就是：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/xYSMQcC.jpg&quot; alt=&quot;Protocols are the same&quot; /&gt;&lt;/p&gt;</content><author><name>Pofat</name></author><category term="Swift" /><category term="Protocol" /><summary type="html">在 Swift 3 後， source code breaking change 已經少有， 最近有個 evolution 可能會引來部分的 breaking change 卻依然值得， 那就是用 protocol 標識為一個物件的型別時， 需要加上 any 的前綴， 比如原本是let foo: FooProtocol = FooConformance() 會改成 let foo: any FooProtocol = FooConformance()。</summary></entry><entry><title type="html">[WWDC21] Demystify SwiftUI</title><link href="https://pofat.github.io/2021/06/13/WWDC21-Demystify-SwiftUI.html" rel="alternate" type="text/html" title="[WWDC21] Demystify SwiftUI" /><published>2021-06-13T00:00:00+00:00</published><updated>2021-06-13T00:00:00+00:00</updated><id>https://pofat.github.io/2021/06/13/WWDC21-Demystify-SwiftUI</id><content type="html" xml:base="https://pofat.github.io/2021/06/13/WWDC21-Demystify-SwiftUI.html">&lt;p&gt;WWDC 21 的 Keynote 看來格外平淡，但沒想到 session 公佈後卻意外地精彩絕倫，各種新東西、解密以及實務探討，我一眼就看到今年最不容錯過的 session - &lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2021/10022/&quot;&gt;Demystify SwiftUI&lt;/a&gt;，姑且翻為 SwiftUI 解密，事後看完雖然沒有解多少密，但該注意的使用方式以避免效能或穩定性問題都有點出來。而且今年首度舉辦 Digital Lounge ，報名後一群開發者會被拉入 Slack，第二三天都有一起看的活動，其中第三天一起看的就是 SwiftUI 解密，重點是該 session 的講者也同時在 Slack 裡面即時回答大家的問題，事後也有留時間給大家額外提問、討論，在那裡又獲得不少寶藏啊！&lt;/p&gt;

&lt;p&gt;相信此 session 絕不會在接下陸續發佈的 WWDC 精選解說文中缺席，這篇文就作為我個人的學習筆記，其中包含了 lounge 裡的討論與提問，和各位分享。&lt;/p&gt;

&lt;h2 id=&quot;identity&quot;&gt;Identity&lt;/h2&gt;

&lt;p&gt;當有人和我討論 Swift 裡 value 與 reference type 選擇的準則是什麼，我的回答都是：「在一般情況下，以需要 identity 與否做為要不要用 reference type 的依據」。在 SwiftUI 中，我們知道 View 都是 value type ，而 identity 則是給 SwiftUI 使用的。對於一個 render view 的 framework，需要知道它要不要更新「這個」 view ，所以身份識別是必要的。識別的方式分別為開發者主動給予識別資訊的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Explicit identity&lt;/code&gt; 與 SwiftUI 從程式碼結構中取得的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Structural identity&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;補充：基本的元件如 Text Button 在 runtime 用 lldb 觀察可得知都是一個 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UIView&lt;/code&gt; 的 subclass &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SwiftUI.DisplayList.ViewUpdate.Platform.CGDrawingView&lt;/code&gt;&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;_TtCOCV7SwiftUI11DisplayList11ViewUpdater8Platform13CGDrawingView&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$46&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  _TtC7SwiftUI15_UIGraphicsView &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    UIView &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
      UIResponder &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        NSObject &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
          isa &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; SwiftUI.DisplayList.ViewUpdater.Platform.CGDrawingView
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;explicit-identity&quot;&gt;Explicit Identity&lt;/h3&gt;

&lt;p&gt;為了讓 compiler 可以約束何時由開發者提供身份資訊 (identifier)，因此定義了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Identifiable&lt;/code&gt; 這個 protocol ，它要求你提供一個名為 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;id&lt;/code&gt; 的 property，且此 property 必需為 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Hashable&lt;/code&gt; ，不難想像這些東西會被 SwiftUI 放在 Dictionary 或 Set 這樣的集合中以在 O(1) 的時間複雜度下查找，實例如下：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;colors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;black&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;white&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;blue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;VStack&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;ForEach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;colors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;值得注意的是，如果你直接送給 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ForEach()&lt;/code&gt; 一個動態長度的 collection 且不給 ID 會得到 warning （以前會是個 error ）；另外你也可以使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.id(xxx)&lt;/code&gt; 來主動給某個 view identifier，如 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Text(&quot;headerSection&quot;).id(headerID)&lt;/code&gt;&lt;/p&gt;

&lt;h3 id=&quot;structrual-identity&quot;&gt;Structrual Identity&lt;/h3&gt;

&lt;p&gt;透過 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@ViewBuilder&lt;/code&gt; 這個 annotation ，可以在 compile time 知道有哪些 view 的組合，這也是若你去觀察 SwiftUI 的型別會發現很難閱讀的原因，因為都是一堆泛型的組成，這是在安全與效率下的 trade off ，比如最常見的 if-else branch :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;someCondition&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;FooView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;BarView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 以上會被轉成如下&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_ConditionalContent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;FooView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;BarView&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;注意 body 本身自帶 ViewBuilder ，只是沒有明示。&lt;/p&gt;

&lt;p&gt;這樣的做法讓 view 在 runtime 有更多資訊知道誰是誰，畢竟「改變一個 view 的狀態」和「轉換成另一個 view」是兩種截然不同的成本。由此可見 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AnyView&lt;/code&gt; 這樣的 type-erasure view 是要謹慎使用的，因為 SwiftUI 並無法在 compile time 確定 AnyView 背後到底是誰， Apple 工程師給出使用 AnyView 時機的準則是：「盡量用在包極少或完全不會變化的 view 上，若使用在 state 頻繁改變的場景可能引起 performance issue」。&lt;/p&gt;

&lt;h3 id=&quot;identifier-state-view-and-depedencies&quot;&gt;Identifier, State, View and Depedencies&lt;/h3&gt;

&lt;p&gt;首先 view, state 與 identifier 的關係是：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;View 的 lifetime 由 identifier 決定&lt;/li&gt;
  &lt;li&gt;State 的 lifetime 會跟著 view 同生同死&lt;/li&gt;
  &lt;li&gt;每當 identifier 開始出現，SwiftUI 會 allocate 一塊記憶體用來存放 state (即 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@State&lt;/code&gt; 與 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@StateObject&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;上述的記憶體由對應的 identifier 來做 index&lt;/li&gt;
  &lt;li&gt;在整個 lifetime 中，state 可能會被改變，state 的前一組值會保存下來用來比較是否有改變，不同的話才會重新 render&lt;/li&gt;
  &lt;li&gt;有點神奇的是你的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@State&lt;/code&gt; 不用是 Equatable ，SwiftUI 也能理解值有沒有變化，估計是在 runtime 時利用 Metadata 去挖 properties 做比較&lt;/li&gt;
  &lt;li&gt;既然 state 是要被保存的，所以會在 heap 裡&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Dependencies 是從 view 外部來決定需不需要重 render 的準則，一旦變化了，會先重建該 view 的 body ，然後再 recursivley traverse 其所有 childe view 。SwiftUI 會利用 identity 與其 lifetime 來建立一個 dependency 的 directed acyclic graph (但從 code level 看整個 view 是個 tree)，如此的做法可以避免過度的 render ，比如 view 的結構是 A -&amp;gt; B -&amp;gt; C，當唯 A C 共享的 dependency 改變時，僅 A C 會被 render ，而 B 不會。但其實當你若有把 property 從 A 傳到 B ，那也代表有 dependency ，所以常見情況應該都還是會全部都有 dependency 因而全都 re-render。&lt;/p&gt;

&lt;p&gt;至於 dependency cycle 是不被允許發生的，一旦發生了會被 system trap，可以理解這個檢查是發生在 runtime 。&lt;/p&gt;

&lt;h3 id=&quot;來自-apple-工程師的愛新的-debug-tool&quot;&gt;來自 Apple 工程師的愛，新的 debug tool&lt;/h3&gt;

&lt;p&gt;說這麼多，我們又不是 compiler 也不是 SwiftUI，我只是想知道到底什麼時候 view 被重畫，又為什麼會被重畫而已啊！你的心聲 Apple 工程師&lt;a href=&quot;https://twitter.com/luka_bernardi/status/1402045202714435585?s=20&quot;&gt;聽到了&lt;/a&gt;，現在只要加上 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Self._printChanges()&lt;/code&gt; 在你的 content view 裡，就能夠看到 identity、dependency 與 state 是否有被設值以及是否有變化（以上任一變化都會發生 render），具體使用方式如下：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;_printChanges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ... your views ...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;或者打個 breakpoint 在 body 裡並新增 action &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;po Self._printChanges()&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/breakpoint_printChanges.png&quot; alt=&quot;breakpoint_example&quot; /&gt;
如此一來當發生變化時你會在 console 中看到&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;ContentView: @self, @identity, _someState changed.&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;以上例子代表該 view 的 dependency （外部餵進 self 的參數）、identity 以及 state 都發生了變化，其實這裡就是第一次初始化後印出來的啦。&lt;/p&gt;

&lt;h3 id=&quot;總結設計注意事項&quot;&gt;總結設計注意事項&lt;/h3&gt;

&lt;p&gt;可以知道 identifier 這東西大大地決定了整個 view render 的時機，lifetime 與 state 的 lifetime ，誤用時可能造成災難，因此要注意兩種不同 identifier 的使用方式：&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explicit identifier 需要唯一且穩定&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;不同的 id 就會被視為不同 view 了，除了 overhead 外還有可能導致狀態的遺失，比如我們用 if else 控制同 view 的不同狀態&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dayTime&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;FooView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;FooView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;nightTimeMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;其中， if 裡的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FooView&lt;/code&gt; 和 else 裡的會被視為兩個不同的 view ，當 dayTime 為 true ，會建立一塊記憶體保存其 state （稱為 state F)，當 dayTime 變  false，則又要了另一塊記憶體保存狀態 (state F’)，當又回到 true branch 時，又一個新的記憶體建立（ state F’‘），是故你的狀態可能因此遺失了。&lt;/p&gt;

&lt;p&gt;另外常見的可能誤用是以 collection 的 index 做 id，這是不穩定的 id ，因為如果你插入一個元素，其往後的 index 全都會變化（+1），SwiftUI 自然也會把它們視為新 view 重新 render 和 reset state 了；若使用 hash value 也要注意你使用的 hash 是不是會同值但卻有不同的 hash value 。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;使用 structural identifier 時先考慮能否換成 inert modifier&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;這個應易懂：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// prefer&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;FooView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;condition&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Do not&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;condition&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;FooView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;FooView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;總之就是減少無意義的 branch 或用 inert modifier 代替，因為它成本極低。不過問題來了，哪些 modifier 才是 inert ？有在 lounge 提問，但這題並沒有被回答到，session 中只舉了三個例子： &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;opacity()&lt;/code&gt; 、 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;padding(0)&lt;/code&gt; 與 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;transformEnvironemt(...) {}&lt;/code&gt;，但其它的也不知道是還不是，這裡還有望知道的人指點。&lt;/p&gt;

&lt;h3 id=&quot;其他有趣的討論&quot;&gt;其他有趣的討論&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;SwiftUI 不做 view diff ，只要 dependency 改變就會重 redner&lt;/li&gt;
  &lt;li&gt;與 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@State&lt;/code&gt; 與 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@StateObject&lt;/code&gt; 不同，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@ObservedObject&lt;/code&gt; 的 lifetime 由 developer 掌控，它可以存活在任何地方&lt;/li&gt;
  &lt;li&gt;Group 很便宜，能用盡量用&lt;/li&gt;
  &lt;li&gt;在 office hour 的小題目，以下例子中，可以使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@State&lt;/code&gt; 、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@StateObject&lt;/code&gt; 或 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@ObservedObject&lt;/code&gt; 哪些？為何？&lt;/li&gt;
&lt;/ol&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;AppState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ObservableObject&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;shared&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;AppState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;manualPauseOfSpeaking&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Bool&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;SceneTitleView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;@StateObject&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;appState&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;AppState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shared&lt;/span&gt;
	&lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;</content><author><name>Pofat</name></author><category term="Swift" /><category term="SwiftUI" /><summary type="html">WWDC 21 的 Keynote 看來格外平淡，但沒想到 session 公佈後卻意外地精彩絕倫，各種新東西、解密以及實務探討，我一眼就看到今年最不容錯過的 session - Demystify SwiftUI，姑且翻為 SwiftUI 解密，事後看完雖然沒有解多少密，但該注意的使用方式以避免效能或穩定性問題都有點出來。而且今年首度舉辦 Digital Lounge ，報名後一群開發者會被拉入 Slack，第二三天都有一起看的活動，其中第三天一起看的就是 SwiftUI 解密，重點是該 session 的講者也同時在 Slack 裡面即時回答大家的問題，事後也有留時間給大家額外提問、討論，在那裡又獲得不少寶藏啊！</summary></entry><entry><title type="html">台北的道地 iOS 小旅行</title><link href="https://pofat.github.io/2021/03/14/An-Authentic-Trip-To-iOS-Developers-In-Taipei.html" rel="alternate" type="text/html" title="台北的道地 iOS 小旅行" /><published>2021-03-14T00:00:00+00:00</published><updated>2021-03-14T00:00:00+00:00</updated><id>https://pofat.github.io/2021/03/14/An-Authentic-Trip-To-iOS-Developers-In-Taipei</id><content type="html" xml:base="https://pofat.github.io/2021/03/14/An-Authentic-Trip-To-iOS-Developers-In-Taipei.html">&lt;p&gt;身為一個 iOS 工程師，在沒有面對程式碼時能在台北進行什麼有意思的休閒活動呢？今天就和大家推薦一個在台北進行的道地 iOS 之旅，用的吃的學的玩的都有兼顧到，值得實地體會一次。&lt;/p&gt;

&lt;h2 id=&quot;第一站mac-擁有者不能忽略的-ikea-將於-2021426-歇業的敦北店&quot;&gt;第一站：Mac 擁有者不能忽略的 IKEA （將於 2021/4/26 歇業的敦北店）&lt;/h2&gt;

&lt;p&gt;在 wak self &lt;a href=&quot;https://weakself.dev/episodes/64&quot;&gt;64集&lt;/a&gt;裡和大家推薦了 IKEA 紙巾架是 CP 值非常高的 MacBook 支架，在 13 &lt;a href=&quot;https://twitter.com/ethanhuang13/status/1352453957138882560?s=21&quot;&gt;轉推&lt;/a&gt;後隨即引起一陣旋風，IKEA 官網在那幾天甚至賣到缺貨，另外還有聰明推友做出小改裝，套上橡皮圈後便有了&lt;a href=&quot;https://twitter.com/ethanhuang13/status/1353545658280501248&quot;&gt;自動展開&lt;/a&gt;的功能，身為 iOS 開發者或 MacBook、Mac Mini 的擁有者，還不快入手幾組嗎？&lt;/p&gt;

&lt;p&gt;因此第一站來到位於台北市區的迷你 IKEA，你可以慢慢地逛，時間不夠的話也可以在走到 B1 後直接往右轉，通過餐廳上樓到餐具區取得一個不到 100 元的&lt;del&gt;筆電架&lt;/del&gt;餐巾紙架，基本上 10 分鐘內可以搞定。&lt;/p&gt;

&lt;p&gt;&lt;em&gt;2021/3/15 慟註：在此文發佈數小時內，IKEA 官方粉絲團隨即發佈敦北店即將營業至 2021/4/26 的&lt;a href=&quot;https://www.facebook.com/IKEA.Taiwan/photos/a.140770119312232/3744285105627364/?type=3&amp;amp;theater&quot;&gt;重磅消息&lt;/a&gt;，想體驗小旅行的只剩 4/8 一次機會了啊～～～～&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;第二站ios-工程師不可錯過的笑貓咖哩&quot;&gt;第二站：iOS 工程師不可錯過的「笑貓咖哩」&lt;/h2&gt;

&lt;p&gt;從 IKEA 敦北店往南京復興走約十分鐘，在八德路靠近捷運站段，坐落著一間日式風格的咖哩小店，目前店內只有七個位置，每天提供兩種不同口味的咖哩以及偶有的期間限定，那這樣的小店特色是什麼呢？一個 iOS 工程師走入店內，便能享用到「&lt;a href=&quot;https://twitter.com/search?q=%23iOS工程師煮給iOS工程師的咖哩&amp;amp;src=typed_query&quot;&gt;iOS 工程師煮給 iOS 工程師的咖哩&lt;/a&gt;」，老闆本來是個全職的 iOS 工程師，不過可能是因為受到不少傷害，所以目前把部分心力都投注到咖哩中，堅持手炒洋蔥做為基底的咖哩，底調帶著甘，很適合在店裡點上一盤帶辣味的咖哩，配上啤酒，好不過癮。就是這樣的氛圍下，那天我們一行五位工程師幾乎要把店內位置佔滿，邊吃咖哩邊聊隕石與 Swift ，聊技術時特別地來勁，分享傷害時的取暖感也格外地暖，以後來問老闆能不能不定時舉辦技術交流餐聚好了。&lt;/p&gt;

&lt;p&gt;酒足飯飽後，往最終站移動。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/korean_chicken _curry.jpg&quot; alt=&quot;韓式辣雞咖哩&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;第三站-apple-開發者們的老牌聚會---cocoaheads-taipei&quot;&gt;第三站： Apple 開發者們的老牌聚會 - CocoaHeads Taipei&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://www.facebook.com/groups/cocoaheads.taipei&quot;&gt;CocoaHeads Taipei&lt;/a&gt; 是每個月的第二個禮拜四晚上舉辦的例行聚會，大家都會在那分享技術、新知與職場五四三，本來身為真。邊緣 iOS 子的我，也是到了這裡後才開始接觸到其它開發圈友人並踏入社群，社群真的也是開發者留在圈子內繼續奮戰的一股力量吧，不管是任何年資的開發者們，總能在裡面獲得什麼，做為小旅行的終點站，和舊識閒聊，聽聽新知，認識其他新朋友，很是滿足。最近推特上說 iOS 開發者們看起來都好快樂，也許原因正是有這些機緣與活動吧。&lt;/p&gt;

&lt;p&gt;Zonble 在 2021 年三月的聚會介紹 Dart 的新語法： Null safety 
&lt;img src=&quot;/assets/cocoaheads_zonble_dart.jpg&quot; alt=&quot;Zonble talks about Dart&quot; /&gt;&lt;/p&gt;

&lt;p&gt;現場 Ruby 實作費式數列教學
&lt;img src=&quot;/assets/learn_ruby.jpg&quot; alt=&quot;Ruby live coding&quot; /&gt;&lt;/p&gt;

&lt;p&gt;最後也要幫忙廣告 CocoaHead Taipei 的 &lt;a href=&quot;https://www.youtube.com/channel/UCEJ92IKffNoDrDIJ5dus6lg&quot;&gt;YouTube channel&lt;/a&gt;，因為每次活動目前都有直播，但由於 YouTube 限制要訂閱數高於 1000 才能開直播，目前都只能在 FB 上直播，請大家多多幫忙按下訂閱，這樣以後活動不能到也能夠方便地收看聚會分享內容。&lt;/p&gt;

&lt;p&gt;以上，就是 iOS 的道地台北小旅行，希望大家都能有快樂回憶。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/greja_in_piccollage.jpg&quot; alt=&quot;Greja in Pic Collage&quot; /&gt;&lt;/p&gt;</content><author><name>Pofat</name></author><category term="Swift" /><category term="iOS" /><category term="Taipei" /><summary type="html">身為一個 iOS 工程師，在沒有面對程式碼時能在台北進行什麼有意思的休閒活動呢？今天就和大家推薦一個在台北進行的道地 iOS 之旅，用的吃的學的玩的都有兼顧到，值得實地體會一次。</summary></entry><entry><title type="html">macOS App Notarization</title><link href="https://pofat.github.io/2021/03/14/macOS-App-Notarization.html" rel="alternate" type="text/html" title="macOS App Notarization" /><published>2021-03-14T00:00:00+00:00</published><updated>2021-03-14T00:00:00+00:00</updated><id>https://pofat.github.io/2021/03/14/macOS-App-Notarization</id><content type="html" xml:base="https://pofat.github.io/2021/03/14/macOS-App-Notarization.html">&lt;p&gt;自從 2019 年六月一號後，所有新發佈到 macOS 10.15 以上版本的 App 都註定需要先&lt;del&gt;交保護費給水果&lt;/del&gt;註冊 Apple Developer Program，因為一定要先經過一個叫做 App Notarization 的手續，而其中會需要由合法 Apple Developer Prgoram 所註冊的 Developer ID 憑證來簽你所發佈的 App。&lt;/p&gt;

&lt;p&gt;先前情提要一下，在 macOS 上發佈 App 有兩種方式，一個是透過 App Store，基本上跟 iOS 大同小異；另一個則是直接把檔案丟給別人讓別人去裝，不透過 App Store 下載，但這種方式預設需要用一種叫做 Developer ID 的憑證去簽署散佈的 App/安裝檔，App Notarization 就是針對後者，你在發佈要先通過這個流程，別人拿到你的檔案時才不會打不開。因為 macOS 10.15 後上面有個 Gatekeeper 會阻擋未通過 Notarization 的 App 安裝/執行，況且系統偏好設定裡的「安全性與隱私權」中，允許執行的 App 來源選預設只剩下 App Store 與「已識別的開發者」，這裡指的就是通過 Notarization 的 App。&lt;/p&gt;

&lt;p&gt;註：你仍能夠強制在安全性設定允許系統執行任何來源的 App ，只要在 Terminal 裡執行 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo spctl --master-disable&lt;/code&gt;，再進一次安全性設定就會看到多出「從任何來源」的選項，反之要復原預設則是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo spctl --master-enable&lt;/code&gt;。&lt;/p&gt;

&lt;h2 id=&quot;準備工作&quot;&gt;準備工作&lt;/h2&gt;

&lt;p&gt;基本上 Xcode 裡附的 Archive Organizer 已經可以幫你處理絕大多數的工作，詳可參考 Apple &lt;a href=&quot;https://help.apple.com/xcode/mac/current/#/dev88332a81e&quot;&gt;文件&lt;/a&gt;，有滿大的可能你並不是用 Xcode 來開發或 build 出最終的成品，而是仰賴 command line 來達成，所以介紹一下整個流程以及完整執行這項工作的 script。基本上主要工作就三項：上傳、確認驗證結果以及裝訂（Staple），每個步驟我們都可以用 script 來完成，不過在作業前需要準備以下幾件事：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;準備 Developer ID 的憑證，App 和裡面各個 Plug-In 與 binary 都要用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Developer ID Application&lt;/code&gt;簽，如果有 pkg 則使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Devloper ID Installer&lt;/code&gt; 來簽&lt;/li&gt;
  &lt;li&gt;請先確認 Xcode Command Line Tool 已經安裝完成（由 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xcode-select -p&lt;/code&gt;）&lt;/li&gt;
  &lt;li&gt;先到 Apple ID 帳號管理頁面生成 &lt;a href=&quot;https://support.apple.com/zh-tw/HT204397&quot;&gt;「App 專用密碼」&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;為避免把密碼明文寫在你的 script 裡，我們可以把密碼存到 Keychain 先：&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xcrun altool --store-password-in-keychain-item &quot;AC_PASSWORD&quot; -u &quot;$your_applie_id&quot; -p &quot;$your_one_time_password&quot;&lt;/code&gt;，請注意這裡的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AC_PASSWORD&lt;/code&gt; 是用於存在 Keycahin 裡的 item 名稱，你也可以指名其它，以下提到都統一使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AC_PASSWORD&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;取得 Provider Short Name： 執行 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xcrun altool --list-providers -u &quot;$your_apple_id&quot; -p &quot;@keychain:AC_PASSWORD&quot;&lt;/code&gt; 後在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ProviderShortName&lt;/code&gt; 欄位裡的值，通常和你的 Team ID 相同，請注意要選對 Team&lt;/li&gt;
  &lt;li&gt;請確認 App 裡所有的 Plug-In，執行檔與其它所有 Binary 都務必用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Developer ID Application&lt;/code&gt; 憑證正確簽署&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;xcode-的設定&quot;&gt;Xcode 的設定&lt;/h2&gt;

&lt;p&gt;首先自己下 script build 的話，要先到 Xcode 裡 Project 的 Build Settings 中，在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Other Code Signing Flags&lt;/code&gt; 中加入 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--timestamp&lt;/code&gt;，因為你必需把簽署的時間帶入才能通過 Notarization。另外值得一提的是，如果你的專案是在 Xcode 10 之前建立的，你需要再多加一個 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--options=runtime&lt;/code&gt; 以啟用在 macOS 10.14 加入的 Hardened Runtime，Xcode 10 後預設是會幫你開的就可以不用，沒開的話不會過。&lt;/p&gt;

&lt;h2 id=&quot;上傳你的-app&quot;&gt;上傳你的 App&lt;/h2&gt;

&lt;p&gt;首先，要先搞清楚要上傳什麼檔，一般 macOS App 發佈可能是個 MyApp.zip，MyApp.pkg 或 App 或 pkg 放在 DMG 裡，無論如何上傳最外層的那個檔案就對了。接下來上傳只要執行：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;xcrun altool &lt;span class=&quot;nt&quot;&gt;--notarize-app&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
		&lt;span class=&quot;nt&quot;&gt;--primary-bundle-id&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$some_bundlie_id&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
		&lt;span class=&quot;nt&quot;&gt;--username&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$your_applie_id&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
		&lt;span class=&quot;nt&quot;&gt;--password&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;@keychain:AC_PASSWORD&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;--asc-provider&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$provider_short_name&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;--file&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;MyApp.dmg&quot;&lt;/span&gt; &amp;amp;&amp;gt; notarization_log&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;注意這裡的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;prmiary-bundle-id&lt;/code&gt; 其實可以隨便填，只是給個人識別用，成功後你會在輸出 log 的檔案裡取得一個 UUID，請握好它。&lt;/p&gt;

&lt;h2 id=&quot;確認結果&quot;&gt;確認結果&lt;/h2&gt;

&lt;p&gt;經過我的測試幾乎上傳後兩三分鐘內都會完成，執行 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xcrun altool --notarization-info &quot;$uuid&quot; -u &quot;$your_applie_id&quot; -p &quot;@keychain:AC_PASSWORD&quot;&lt;/code&gt;，可以得到結果，可能是還在進行中，成功(success)或失敗 (invalid)，如果失敗可以在回應的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LogFileURL&lt;/code&gt; 裡看到失敗的原因，可以寫個 script ，不時回傳檢查結果。&lt;/p&gt;

&lt;h2 id=&quot;裝訂-app&quot;&gt;裝訂 App&lt;/h2&gt;

&lt;p&gt;完成後可以用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xcrun stapler staple &quot;YourApp.dmg&quot;&lt;/code&gt; 來裝訂成果，請記得一樣是裝訂最外層容器那個檔案即可，接下來就能安心散佈了，只是每次發佈新版前都要再走一次這流程。&lt;/p&gt;

&lt;h2 id=&quot;一次完成&quot;&gt;一次完成&lt;/h2&gt;

&lt;p&gt;建立一個 notarize 的 shell script file 如下，用法 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./notarize $your_apple_id $prvoider_short_name $app_file_path&lt;/code&gt;&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/sh&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Upload &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$3&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; for notarization&quot;&lt;/span&gt;

xcrun altool &lt;span class=&quot;nt&quot;&gt;--notarize-app&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
		&lt;span class=&quot;nt&quot;&gt;--primary-bundle-id&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$some_bundlie_id&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
		&lt;span class=&quot;nt&quot;&gt;--username&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
		&lt;span class=&quot;nt&quot;&gt;--password&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;@keychain:AC_PASSWORD&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;--asc-provider&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$2&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;--file&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$3&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &amp;amp;&amp;gt; an_log

&lt;span class=&quot;nv&quot;&gt;uuid&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cat &lt;/span&gt;an_log | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-Eo&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'\w{8}-(\w{4}-){3}\w{12}$'&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;while &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do
    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Checking notarization for &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$3&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;...&quot;&lt;/span&gt;
 
    xcrun altool &lt;span class=&quot;nt&quot;&gt;--notarization-info&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$uuid&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$2&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;@keychain:AC_PASSWORD&quot;&lt;/span&gt; &amp;amp;&amp;gt; result_tmp 
    &lt;span class=&quot;nv&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cat &lt;/span&gt;result_tmp&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$result&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;success&quot;&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$result&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;invalid&quot;&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
        &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Notarization done!&quot;&lt;/span&gt;
        xcrun stapler staple &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$3&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Stapler done!&quot;&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;rm &lt;/span&gt;result_tmp
        &lt;span class=&quot;nb&quot;&gt;break
    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fi
    if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$i&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
        &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Failed!&quot;&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$result&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;rm &lt;/span&gt;result_tmp
        &lt;span class=&quot;k&quot;&gt;return &lt;/span&gt;1
    &lt;span class=&quot;k&quot;&gt;fi
    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Processing, check again after 30 seconds.&quot;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;sleep &lt;/span&gt;30
&lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;參考資料&quot;&gt;參考資料&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.logcg.com/archives/3222.html&quot;&gt;自動化 Notarization 腳本&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><author><name>Pofat</name></author><category term="Swift" /><category term="macOS" /><category term="Notarize" /><summary type="html">自從 2019 年六月一號後，所有新發佈到 macOS 10.15 以上版本的 App 都註定需要先交保護費給水果註冊 Apple Developer Program，因為一定要先經過一個叫做 App Notarization 的手續，而其中會需要由合法 Apple Developer Prgoram 所註冊的 Developer ID 憑證來簽你所發佈的 App。</summary></entry><entry><title type="html">The Journey Of Swift Concurrency - Atomics</title><link href="https://pofat.github.io/2020/10/18/The-Journey-Of-Swift-Concurrency-Atomics.html" rel="alternate" type="text/html" title="The Journey Of Swift Concurrency - Atomics" /><published>2020-10-18T00:00:00+00:00</published><updated>2020-10-18T00:00:00+00:00</updated><id>https://pofat.github.io/2020/10/18/The-Journey-Of-Swift-Concurrency-Atomics</id><content type="html" xml:base="https://pofat.github.io/2020/10/18/The-Journey-Of-Swift-Concurrency-Atomics.html">&lt;p&gt;由於在 weak self &lt;a href=&quot;https://weakself.dev/episodes/56&quot;&gt;56集&lt;/a&gt;聊到了這陣子 Apple 一周一 framework，蓬勃地產出&lt;del&gt;Bug&lt;/del&gt;，我認為這個 &lt;a href=&quot;https://github.com/apple/swift-atomics&quot;&gt;Swift Atomics&lt;/a&gt; 是整個 Swift Concurrency 大業的一個分支，之後會陸續就我的看法，將  Swift Concurrency 的起頭，以及目前已經實現的部分寫文探討。本來 Atomics 應該是整個系列的第三或第四集，但因為 Podcast 節目要出了，迫在眉睫、褲子著火，只好先聊 Atomics （疑又是第三集先出啊)，也放棄了 Integer 型別的系列 index，以增加&lt;del&gt;惡補的可能性&lt;/del&gt;彈性。&lt;/p&gt;

&lt;h2 id=&quot;為何要有-swift-atomics&quot;&gt;為何要有 Swift Atomics?&lt;/h2&gt;

&lt;p&gt;相信 iOS 開發者都很熟悉，如果要在不同的 thread 間讀寫共享的變數，特別是 value type 如 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Array&lt;/code&gt; 或 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dictionary&lt;/code&gt;，我們都會用個 GCD queue 限制讀寫都只在同一個 queue 上或者用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NSLock&lt;/code&gt; 之類的 lock 來保證讀寫順序與完整性。&lt;/p&gt;

&lt;p&gt;然而對 Swift 而言，希望能成為系統層級可使用的語言以及具有更高層次抽象的 Concurrency 做法（比如 Coroutines），需要更底層的界面讓開發者能夠直接宣告一個可以安全訪問的同步結構。&lt;/p&gt;

&lt;p&gt;簡而言之就是你能宣告一個型別，就可以直接在不同的 thread 間讀寫它而不會出事，你不需要再去考慮該用 GCD 或 lock 來保證它的安全性，畢竟這兩種方法都有它的缺點與限制：&lt;/p&gt;

&lt;p&gt;使用 GCD，當數據或對象增加時，容易變得複雜，你很難一下明白是「哪一個 queue」在保護「哪一個數據」； 有的情況使用 NSLock根本無用，比如在一個 mutating method 裡放 lock，mutating 本身實作概念是會先 copy 一份出去修改，再寫回原本的變數中。除以此外，lock 的做法可能還會有效能的疑慮。&lt;/p&gt;

&lt;p&gt;所以，Swift 應該搞個自己的東西讓我們直接用才對，基本上目標會是：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;一個新的類型，會在語義上明顯地標示這是一個 atomic 操作，最要緊的就是 concurrent mutation，一個 Swift 以前沒有的概念。&lt;/li&gt;
  &lt;li&gt;操作時要明確地給予 memory order 參數，以明白讀寫共享的 memory 時順序是如何地保證。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;當然如同介紹與提案說的，這是個滿是坑的領域，跟 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Unsafe-&lt;/code&gt; 家族一樣，希望少用更或者別用。這裡只是實現了真正的 concurrent mutation 的可能性（因為以前是不可能的），實質上 Atomics 物件可以視作 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UnsafePointer&lt;/code&gt; 的 wrapper 而已，用起來就是怕。&lt;/p&gt;

&lt;p&gt;實際上做的事大概是兩部分：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;與 C/C++ 相似的同步記憶體模型以及橋接 C/C++的記憶體順序&lt;/li&gt;
  &lt;li&gt;基於 C Atomic 實現核心功能與 class-based 的 Atomic 物件給 Swift 開發者使用&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;swift-atomics-為何是個-package&quot;&gt;Swift Atomics 為何是個 Package&lt;/h2&gt;

&lt;p&gt;Atomics 的提案是 &lt;a href=&quot;https://github.com/apple/swift-evolution/blob/main/proposals/0282-atomics.md&quot;&gt;SE-0282&lt;/a&gt;，然而這個提案是經過翻修的，原本從底層要做的橋接 C++/C 之記憶體模型到開發者可以建立的 Atomic 類型都有定義。這個目標原本是希望在，不過在考慮對於 API/ABI 穩定的影響，最後在 &lt;a href=&quot;https://forums.swift.org/t/se-0282-low-level-atomic-operations/35382/59&quot;&gt;Joe 的建議&lt;/a&gt;下，這個提案修改為針對記憶體模型的改進和與此相關的底層 api，而最上層的 class-based 的 Atomics 物件則用 package 來推出。&lt;/p&gt;

&lt;p&gt;關於第一版的提案可見&lt;a href=&quot;https://github.com/apple/swift-evolution/blob/3a358a07e878a58bec256639d2beb48461fc3177/proposals/0282-atomics.md&quot;&gt;此&lt;/a&gt;，推友&lt;a href=&quot;https://twitter.com/kemchenj&quot;&gt;四娘&lt;/a&gt;有針對原始版寫了一篇&lt;a href=&quot;https://kemchenj.github.io/2020-10-02/&quot;&gt;譯文&lt;/a&gt;，英文苦手可參考。&lt;/p&gt;

&lt;h2 id=&quot;怎麼用&quot;&gt;怎麼用&lt;/h2&gt;

&lt;p&gt;最常見的 thread-unsafe access 例子是&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;kd&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Dispatch&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Counter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;mutating&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;increment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;DispatchQueue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;concurrentPerform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;iterations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&amp;lt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;counter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;increment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;counter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 會很奇怪不是 10_000&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;如果用 Atomics 就會得到正確的結果&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;kd&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Atomics&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Dispatch&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;counter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ManagedAtomic&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;DispatchQueue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;concurrentPerform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;iterations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;..&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;counter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;wrappingIncrement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ordering&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;relaxed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;counter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ordering&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;relaxed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// ⟹ 10_000&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;支援的型別&quot;&gt;支援的型別&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;各類整數們： &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Int, Int64, Int32, Int16, Int8&lt;/code&gt; 與 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UInt, UInt64, UInt32, UInt16, UInt8&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Bool&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;標準的 Pointer 型別： &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UnsafeRawPointer, UnsafeMutableRawPointer, UnsafePointer&amp;lt;T&amp;gt;, UnsafeMutablePointer&amp;lt;T&amp;gt;, Unmanaged&amp;lt;T&amp;gt;&lt;/code&gt;，以及它們的 Optional 型態&lt;/li&gt;
  &lt;li&gt;任何 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RawValue&lt;/code&gt; 是 atomic type 的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RawRepresentable&lt;/code&gt;，比如&lt;/li&gt;
&lt;/ul&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;kd&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;AtomicEnum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;AtomicValue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bar&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;aEnum&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ManagedAtomic&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;AtomicEnum&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;ul&gt;
  &lt;li&gt;Reference type 要 confrom AtomicReference protocol 才行，如一個 LinkedList 裡最常用的 Node 結構&lt;/li&gt;
&lt;/ul&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;AtomicReference&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ManagedAtomic&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;對現有的衝擊&quot;&gt;對現有的衝擊&lt;/h2&gt;

&lt;p&gt;上面的 concurrent DispatchQueue 例子顯然違反了當前的 Swift memery exclusive access rules ，不能同時有 read/write 或 write/write access，所以這個規則被改寫了為 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;除了 read 或 atomic access，不得有在同一個 variable 上有重複的 access&lt;/code&gt;，然後 atomic access 就是我們可以使用的這些 api 如 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;func load(ordering: AtomicLoadOrdering) -&amp;gt; Value&lt;/code&gt; 等&lt;/p&gt;

&lt;p&gt;至於記憶體管理，目前提供了兩種形態的 class，當然還是鼓勵大家用 ARC 的版本，除非你完全知道自己在幹嘛&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ManagedAtomic&amp;lt;T&amp;gt;&lt;/code&gt;： 仰賴 ARC 管理記憶體&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UnsafeAtomic&amp;lt;T&amp;gt;&lt;/code&gt;： 顧名思義，你要手動自己管理記憶體，可視為 UnsafePointer 再包一層&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;這兩種物件都提供了六種可用的操作&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ordering&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;AtomicLoadOrdering&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;desired&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ordering&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;AtomicStoreOrdering&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;exchange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;desired&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ordering&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;AtomicUpdateOrdering&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;compareExchange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;expected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;desired&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;ordering&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;AtomicUpdateOrdering&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;exchanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;original&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;compareExchange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;expected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;desired&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;successOrdering&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;AtomicUpdateOrdering&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;failureOrdering&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;AtomicLoadOrdering&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;exchanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;original&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;weakCompareExchange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;expected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;desired&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;successOrdering&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;AtomicUpdateOrdering&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;failureOrdering&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;AtomicLoadOrdering&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;exchanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;original&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;基本上這個操作和 C/C++ 是相似的，且 memory ordering 更是完全 1:1 對應到 C/C++ &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::memory_order&lt;/code&gt; 的定義。&lt;/p&gt;

&lt;h2 id=&quot;lock-free-但不是-wait-free&quot;&gt;Lock-Free 但不是 Wait-Free&lt;/h2&gt;

&lt;p&gt;Swift Atomics 保證用這物件可以完全不會被擋住 (non-blocking)，但不代表一定不用等，實際上的 atomic 操作會因對應的執行平台而異，如果是 compare and exchange loop 的話，你可能會因為不同的排程策略而需要等比較久才能完成操作，compare and exchange 的概念可能可以這樣大略表示&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;compareAndSwap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;old&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Bool&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;old&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;old&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// assgin new value to real address&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// The loop&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;repeat&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;oldValue&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// fetch old value&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;newValue&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newValue&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// value we want to set&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;success&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;compareAndSwap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;old&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                 &lt;span class=&quot;nv&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                 &lt;span class=&quot;nv&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; 
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;success&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;app-開發者該怎麼做&quot;&gt;App 開發者該怎麼做？&lt;/h2&gt;

&lt;p&gt;拿 Swift Atomics 的官方例子和 NSLock 做 increment 的效率粗略比較一下(做 10 次取平均），可以看到差距&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Atomics: 2.7975
NSLock: 4.8439
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;基本上這個 pakcage 可能絕大多數的 iOS 開發者是完全用不到的，至少要到 &lt;a href=&quot;https://github.com/belozierov/SwiftCoroutine&quot;&gt;SwiftCoroutine&lt;/a&gt; 這樣程度的 API 才有可能被大家舒服地使用的，或許有興趣的人可以加入幫忙處理一些底層未實現的功能，或著試著用它搭建更高層可以給一般 app 開發者使用的套件。&lt;/p&gt;

&lt;p&gt;真的要用的話，試著舉個例子，假如我們有一個物件會被多個 thread 使用，我們想在不同 thread 對它做事，但這件事只能做一次（比如 clean up），可以這麼寫&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;kd&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Atomics&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Foundation&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;SomeObject&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;done&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ManagedAtomic&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Bool&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Can only clear once&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ordering&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;relaxed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;expected&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;compareExchange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;expected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;desired&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ordering&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;relaxed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;got cleared only once&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;SomeObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;DispatchQueue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;concurrentPerform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;iterations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;some&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 只會被 clear 一次&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;</content><author><name>Pofat</name></author><category term="Swift" /><category term="Concurrency" /><summary type="html">由於在 weak self 56集聊到了這陣子 Apple 一周一 framework，蓬勃地產出Bug，我認為這個 Swift Atomics 是整個 Swift Concurrency 大業的一個分支，之後會陸續就我的看法，將 Swift Concurrency 的起頭，以及目前已經實現的部分寫文探討。本來 Atomics 應該是整個系列的第三或第四集，但因為 Podcast 節目要出了，迫在眉睫、褲子著火，只好先聊 Atomics （疑又是第三集先出啊)，也放棄了 Integer 型別的系列 index，以增加惡補的可能性彈性。</summary></entry><entry><title type="html">波肥的軟工相談室 @ iPlayground</title><link href="https://pofat.github.io/2020/09/29/pofats-chat-room-iplayground.html" rel="alternate" type="text/html" title="波肥的軟工相談室 @ iPlayground" /><published>2020-09-29T00:00:00+00:00</published><updated>2020-09-29T00:00:00+00:00</updated><id>https://pofat.github.io/2020/09/29/pofats-chat-room-iplayground</id><content type="html" xml:base="https://pofat.github.io/2020/09/29/pofats-chat-room-iplayground.html">&lt;h1 id=&quot;pofat-的軟工相談室&quot;&gt;Pofat 的軟工相談室&lt;/h1&gt;

&lt;p&gt;大家好，小弟是開發傷害累積很深的 iOS 清道夫工程師，念在一路走來接了不少炮彈、隕石，故在 iPlayground 2020 重磅推出個人相談室服務，望大家在未來的工程師之道上能少點傷害，走得舒服些，也有機會和大家交流蕉流。&lt;/p&gt;

&lt;p&gt;如果你在團隊、公司或個人開發中有以下幾類的困惑或疑問，歡迎填表單預約，讓 Pofat 我為你想想辧法！&lt;/p&gt;

&lt;h2 id=&quot;a-程式架構與效能類問題&quot;&gt;A. 程式架構與效能類問題&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;是否感覺面對的專案都在疊床架屋，每日開發如履薄冰，想處理卻不知從何下手？&lt;/li&gt;
  &lt;li&gt;面對大規模的重構，不知怎麼開始，以及開始後如何盡量別把專案搞爆。&lt;/li&gt;
  &lt;li&gt;想要開始寫測試，但是怎麼看好像都測不動啊…&lt;/li&gt;
  &lt;li&gt;是否有難以解決的效能問題，或是如何確保效能在開發迭代中不逐漸失去控制？&lt;/li&gt;
  &lt;li&gt;基本上如果你對下圖有既視感，歡迎來取暖 QAQ
&lt;img src=&quot;https://i.imgur.com/ayVC4Mn.png&quot; alt=&quot;legacy code&quot; /&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;p.s. 請為企業工作的大家屆時務必注意是否有不適當地揭露公司智慧財產權與營業祕密的情況。&lt;/p&gt;

&lt;h2 id=&quot;b-開發技術與自我成長&quot;&gt;B. 開發技術與自我成長&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;是否為了成為資深 iOS 工程師而想累積傷害值（無誤），卻不知道該往哪些面向學習與研究？&lt;/li&gt;
  &lt;li&gt;開發上是否遇到了開發問題很久沒能解決？&lt;/li&gt;
  &lt;li&gt;面試時不清楚如何給面試官留下印象以及如何展現自己？&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;c-團隊合作與工作流程&quot;&gt;C. 團隊合作與工作流程&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;是否想引入 code review 卻又不清楚到底該 review 什麼？&lt;/li&gt;
  &lt;li&gt;把 code 送給 reviewer 前自己又該有什麼自覺？&lt;/li&gt;
  &lt;li&gt;如何改善或確保工作流程能夠確保或提昇團隊產出的正確性？&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;what-to-expect&quot;&gt;What To Expect&lt;/h1&gt;

&lt;p&gt;給不認識我的人，我從 2012 年開始做為一個軟體工程師而工作，主要專長是 iOS，中間參與新創時期有劈腿參與前期的產品開發，累積了一些 user reserach 與快速打造 MVP 驗證想法的經驗。而這些年主要做的事是帶領一個團隊做 iOS app 的架構規畫、效能改善與監控、建立文件及團隊工作流程和處理各種開發時的疑難雜症。&lt;/p&gt;

&lt;p&gt;因我主要還是一個 iOS 工程師，能和你分享的是以我個人經驗、知識與用它們歸納出你提出問題的可能解法和建議。當然一般性的軟體開發討論（比如不考慮框架，語言特性下的架構）還是能聊聊，但若是非 iOS 領域的問題應該大部分是無法回答（比如 Java 的 bytecode 細節）。&lt;/p&gt;

&lt;p&gt;基本上大家若願聽小弟之言，我必當全力以赴，但也沒能保證 100% 在當下解決你的問題，畢竟有的事需要天時、地利與人合 &lt;del&gt;或讓我跟你說說怎麼從入門到放棄&lt;/del&gt;。&lt;/p&gt;

&lt;p&gt;其實主要還是認為這些年來的經驗也許有機會幫助其它個人、團隊，這只是我所跨出嘗試的第一步，更多內容可以在最新一期的 &lt;a href=&quot;https://weakself.dev/episodes/55&quot;&gt;weak self 第 55 集&lt;/a&gt;裡聽到（預計 2020/10/4 21:00 上線）。&lt;/p&gt;

&lt;h1 id=&quot;如何預約&quot;&gt;如何預約&lt;/h1&gt;

&lt;p&gt;只要你是參與 iPlayground 2020 的會眾即可預約，即日起開放&lt;a href=&quot;https://forms.gle/84QSXnmqZm8GipB46&quot;&gt;填表預約&lt;/a&gt;，預約單位可以是個人，數個人一起或者一個團隊。&lt;/p&gt;

&lt;p&gt;請注意不是先搶先贏，我會挑選我認為適合當天活動的來進行，如果沒被選上的也請別介意，我會留存待日後有適合的機會再進行。&lt;/p&gt;

&lt;h1 id=&quot;進行方式&quot;&gt;進行方式&lt;/h1&gt;

&lt;p&gt;分兩個時段進行，在 iPlayground 2020 (11/8) 當天 10:30 - 12:00 和 15:30 - 17:00，每個時段各一個半小時、一組半小時，所以將會有六組在當天進行。過程中大家可以在表單註明接不接受旁聽，若接受的話會開放當天會眾自由參與旁聽，否則就只有我和預約的單位單獨在一個獨立的空間進行。&lt;/p&gt;</content><author><name>Pofat</name></author><category term="Swift" /><category term="iOS" /><category term="Software" /><summary type="html">Pofat 的軟工相談室</summary></entry><entry><title type="html">如何更精準地測試 Reactive Programming（一）</title><link href="https://pofat.github.io/2020/09/17/Precise-Tests-In-Reactive-Programming-I.html" rel="alternate" type="text/html" title="如何更精準地測試 Reactive Programming（一）" /><published>2020-09-17T00:00:00+00:00</published><updated>2020-09-17T00:00:00+00:00</updated><id>https://pofat.github.io/2020/09/17/Precise-Tests-In-Reactive-Programming-I</id><content type="html" xml:base="https://pofat.github.io/2020/09/17/Precise-Tests-In-Reactive-Programming-I.html">&lt;p&gt;Functional Reactive Programming 顯然是這些年的開發模式顯學之一，尤其是 Apple 也推出了自己的方案 - &lt;a href=&quot;https://developer.apple.com/documentation/combine&quot;&gt;Combine&lt;/a&gt;，由此可見其重要性。基本上 Reactive Programming 是將非同步的事件，加上時間這個維度，以變成一個面向資料串流的開發典範，而這個發射事件的主體，在不同的 FRP 框架各由不同角色擔當， ReactiveCocoa 是 Signal，RxSwift 是 Observable，Combine 就是 Publisher，今天的主角會是 RxSwift，不過 Combine 與它的概念幾乎都是相通的，物件間的對應關係也幾乎是 1:1， 所以同樣的概念也完全適用。&lt;/p&gt;

&lt;p&gt;Observable 的觀念一看 Rx 家族用來說明 Observable 的示意圖就能更容易地明白（&lt;a href=&quot;http://reactivex.io/documentation/observable.html&quot;&gt;圖片來源&lt;/a&gt;）
&lt;img src=&quot;https://i.imgur.com/LJDykO8.png&quot; alt=&quot;observable&quot; /&gt;&lt;/p&gt;

&lt;p&gt;由此可知 Observable 可視為用時間為 index 的 event collection ，若是 collection 那麼我們所熟的 high-order function ，這些從 FP 來的概念也可以套用在 Observbale 上，因此 map、flatMap等這些熟悉的伙伴們都出現了，這即是 FRP 裡 F 的部分。&lt;/p&gt;

&lt;h2 id=&quot;測試時間有點難&quot;&gt;測試時間有點難&lt;/h2&gt;

&lt;p&gt;那今天的主題是什麼呢？我們知道 Observable 發出來的都是非同步事件，所以在測試用了 Observbale 的程式碼時，如果牽扯到 thread 的切換，那你就得使用非同步的測試方式 （比如 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;XCTWaiter&lt;/code&gt;），一個適當的測試應該 Observable 裡的事件來源其實都是用 mock 來取代真正的非同步工作，所以一定很快，可惜你仍然需要為這種測試方式設一個等待的時間；更困難的問題是時間是複雜的，如果我們想要更精準地驗確一個工作前開始、中間以及結束時的狀態就會把測試變得很複雜。但別忘了利用 time 當 index 的 event collection 這個概念，若我們能實做的話是不是就能控制時間，測試也能做得更明確嚴謹！而 RxSwift 就提供了 RxTest 這個附加的框架來幫忙這件事，就一起理解一下這件事是怎麼辦到的。&lt;/p&gt;

&lt;p&gt;先看個例子，一個可以打 request 與有個 String 結果 Observable 的 ViewModel&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Dependency&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Observable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;scheduler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;SchedulerType&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;



&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ViewModel&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;dependency&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Dependency&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Observable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;resultRelay&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;PublishRelay&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;bag&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;DisposeBag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

   

  &lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;dependency&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Dependency&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

&lt;span class=&quot;err&quot;&gt;​&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dependency&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dependency&lt;/span&gt;

&lt;span class=&quot;err&quot;&gt;​&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resultRelay&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;asObservable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;



  &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;doRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

&lt;span class=&quot;err&quot;&gt;​&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;dependency&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;err&quot;&gt;​&lt;/span&gt;      &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;observeOn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dependency&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;scheduler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;err&quot;&gt;​&lt;/span&gt;      &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;subscribe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;onNext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;weak&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resultRelay&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;accept&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;

&lt;span class=&quot;err&quot;&gt;​&lt;/span&gt;      &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;disposed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;by&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;然後一般利用 waiter 的非同步測試會像：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;kd&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Dependency&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;mock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Dependency&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

&lt;span class=&quot;err&quot;&gt;​&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Observable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;just&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;mock&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;scheduler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ConcurrentDispatchQueueScheduler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;qos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;



&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;testMockedRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;results&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]()&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;vm&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ViewModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;vm&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;

&lt;span class=&quot;err&quot;&gt;​&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;observeOn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;ConcurrentDispatchQueueScheduler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;qos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;err&quot;&gt;​&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;subscribe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;onNext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;results&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;

&lt;span class=&quot;err&quot;&gt;​&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;disposed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;by&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

   

  &lt;span class=&quot;n&quot;&gt;vm&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;doRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;XCTWaiter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;wait&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;XCTestExpectation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()],&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;kt&quot;&gt;XCTAssertEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;results&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;mock&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;把非同步變成同步&quot;&gt;把非同步變成同步&lt;/h2&gt;

&lt;p&gt;那接下來要怎麼控制時間呢？第一件事要把這些非同步的動作都變成同步的，我們才有可能控制它，變成一個以 time 為 index 的 event collection。要做到這件事需要先了解 RxSwift 裡的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SchedulerType&lt;/code&gt; 幹啥用的 （在 Combine 就是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Scheduler&lt;/code&gt;）？ &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SchedulerType&lt;/code&gt; 繼承自 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ImmediateSchedulerType&lt;/code&gt;，共有一個 property &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;var now : RxTime { get }&lt;/code&gt; 和三個 method ，分別是三種 schedule：立刻馬上做，延後一段時間再做與週期性地做，其實 scheduler 的功能就是要有個 now 做為時間軸起點的參考點，然後不同時間點對不同的事情做排程在不同的 thread 上。具體地來說 RxSwift 有  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MainScheduler&lt;/code&gt; (main thread)，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DispatchQueueScheduler&lt;/code&gt; （包裝 GCD）以及&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt; OperationQueueScheduler&lt;/code&gt; （包裝 OperationQueue）； 在 Combine 中，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DispatchQueue&lt;/code&gt;，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OperationQueue&lt;/code&gt;與 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RunLoop&lt;/code&gt; 本身都遵循 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Scheduler&lt;/code&gt; 了，更為靠近原本的用法習慣。&lt;/p&gt;

&lt;p&gt;本文先只看第一種立刻馬上要做的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;func schedule&amp;lt;StateType&amp;gt;(_ state: StateType, action: @escaping (StateType) -&amp;gt; Disposable) -&amp;gt; Disposable&lt;/code&gt;，這件事在以往的 callback 做法就會是&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;n&quot;&gt;myQueue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

&lt;span class=&quot;err&quot;&gt;​&lt;/span&gt;    &lt;span class=&quot;nf&quot;&gt;doSomething&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;而現在我們的目標是要能操控事件的發生時間，對於馬上要做的事件時間狀態只有做與不做兩種，所以解法就是放個 array 儲存要排程的工作，然後有個 method 可以觸放所有儲存起來，準備要發生的工作（注意以下先略過了其它兩種 schedule）&lt;/p&gt;

&lt;p&gt;首先我們需要一個「包裝工作的容器」，而且它必須是個 Disposable ，因為你可以發現 schedule method 是回一個 Disposable&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VirtualSchedulerItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Disposable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// 真正的非同步工作在此&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;typealias&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Disposable&lt;/span&gt;



  &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Action&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;disposable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;SingleAssignmentDisposable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;isDisposed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Bool&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

&lt;span class=&quot;err&quot;&gt;​&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;disposable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isDisposed&lt;/span&gt;

  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;



  &lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;@escaping&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

&lt;span class=&quot;err&quot;&gt;​&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;

  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// 真正執行工作的觸發點&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

&lt;span class=&quot;err&quot;&gt;​&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;disposable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;setDisposable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;

  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;



  &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;dispose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

&lt;span class=&quot;err&quot;&gt;​&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;disposable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dispose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;接下來要來做一個自己的 Scheduler：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;MyTestScheudler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;SchedulerType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;scheduleds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;VirtualSchedulerItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// RxTime 其實是 Date 的 typealias&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;RxTime&lt;/span&gt;



  &lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;RxTime&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;RxTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

&lt;span class=&quot;err&quot;&gt;​&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;now&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;now&lt;/span&gt;

  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;



  &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;schedule&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;StateType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;StateType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;@escaping&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;StateType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Disposable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Disposable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

&lt;span class=&quot;err&quot;&gt;​&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;compositeDisposable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CompositeDisposable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;err&quot;&gt;​&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;// 把工作包在 scheduler item 裡&lt;/span&gt;

&lt;span class=&quot;err&quot;&gt;​&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VirtualSchedulerItem&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

&lt;span class=&quot;err&quot;&gt;​&lt;/span&gt;      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;err&quot;&gt;​&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;err&quot;&gt;​&lt;/span&gt;    

&lt;span class=&quot;err&quot;&gt;​&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;scheduleds&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;err&quot;&gt;​&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;compositeDisposable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;insert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;err&quot;&gt;​&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;compositeDisposable&lt;/span&gt;

  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// 真正執行所有 scheduled task &lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

&lt;span class=&quot;err&quot;&gt;​&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;// 這裡就把所有工作都變成同步了&lt;/span&gt;

&lt;span class=&quot;err&quot;&gt;​&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scheduleds&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

&lt;span class=&quot;err&quot;&gt;​&lt;/span&gt;      &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;err&quot;&gt;​&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;err&quot;&gt;​&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;scheduleds&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;removeAll&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 做完就可以移除了&lt;/span&gt;

  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;修改我們的 test：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;testScheduler&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;MyTestScheudler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Dependency&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Dependency&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

&lt;span class=&quot;err&quot;&gt;​&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Observable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;just&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;mock&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;scheduler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;testScheduler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;



&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;testMockedRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;results&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]()&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;vm&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ViewModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;vm&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;

&lt;span class=&quot;err&quot;&gt;​&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;observeOn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;testScheduler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;err&quot;&gt;​&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;subscribe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;onNext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

&lt;span class=&quot;err&quot;&gt;​&lt;/span&gt;          &lt;span class=&quot;n&quot;&gt;results&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;err&quot;&gt;​&lt;/span&gt;       

&lt;span class=&quot;err&quot;&gt;​&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;

&lt;span class=&quot;err&quot;&gt;​&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;disposed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;by&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// 以下才有變化&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;vm&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;doRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

  &lt;span class=&quot;kt&quot;&gt;XCTAssertEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;results&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[])&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// we can test the beginning&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;testScheduler&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

  &lt;span class=&quot;kt&quot;&gt;XCTAssertEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;results&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;mock&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;跑一下測試秒過，雖然是個簡單的例子，但不用等 expectation 就是爽啦！下集將介紹如果事情沒有要馬上發生的情況，以及若希望事件觸發也能指定發生在時間軸上的某一個點該如何進行。&lt;/p&gt;</content><author><name>Pofat</name></author><category term="RxSwift" /><category term="Swift" /><summary type="html">Functional Reactive Programming 顯然是這些年的開發模式顯學之一，尤其是 Apple 也推出了自己的方案 - Combine，由此可見其重要性。基本上 Reactive Programming 是將非同步的事件，加上時間這個維度，以變成一個面向資料串流的開發典範，而這個發射事件的主體，在不同的 FRP 框架各由不同角色擔當， ReactiveCocoa 是 Signal，RxSwift 是 Observable，Combine 就是 Publisher，今天的主角會是 RxSwift，不過 Combine 與它的概念幾乎都是相通的，物件間的對應關係也幾乎是 1:1， 所以同樣的概念也完全適用。</summary></entry><entry><title type="html">前所未有的優秀遊戲作品 - 最後生還者</title><link href="https://pofat.github.io/2020/06/19/The-Best-Product-As-A-Game.html" rel="alternate" type="text/html" title="前所未有的優秀遊戲作品 - 最後生還者" /><published>2020-06-19T00:00:00+00:00</published><updated>2020-06-19T00:00:00+00:00</updated><id>https://pofat.github.io/2020/06/19/The-Best-Product-As-A-Game</id><content type="html" xml:base="https://pofat.github.io/2020/06/19/The-Best-Product-As-A-Game.html">&lt;p&gt;如果要問我認為最棒最優秀的單機遊戲，我一定毫不猶豫地說出「最後生還者」，沒有之一。&lt;/p&gt;

&lt;p&gt;       因為我從來沒有玩過這樣的遊戲，單從遊戲的遊玩性上來看，它稱得上非常優秀但還不到神，只是它遊玩方式、細節、互動的設計，無一不是在為最後結局一層一層地鋪陳張力，透過親身遊玩體驗整段遊戲，讓結尾那一幕對話到來時，高潮和積累的張力一口氣全部釋放，然後又瞬間戛然而止，由奧斯卡獎得主的配樂家 Guatavo Santollala 撰寫的 &lt;a href=&quot;https://www.youtube.com/watch?v=3o_RwQysgA8&quot;&gt;The Path (A New Begining)&lt;/a&gt; 悠然響起，留給玩家無比的懸念和回味。&lt;/p&gt;

&lt;p&gt;       當時一破關我就決定一要為它寫一篇我的心得，最後生還者真的是太棒太棒的遊戲作品，因為遊玩時間比觀影更長，又有可以自己操控的互動，情感沉浸和入戲的過程可以比電影更細膩，比文字小說更為立體，我從沒有想過，原來說故事可以說到這種境界。&lt;/p&gt;

&lt;p&gt;       只是當初想寫心得時二代還沒消沒息，後來二代經歷過各種風雨都要發售了，就改成想說二代發售前一定要寫出來，結果還是遲了（寫到這段時正好是台灣時間 TLOU Part II 可以開始遊玩的時間點）。另外，既然是我個人的心得，以下內容會完完全全地大暴雷遊戲劇情，如果沒玩過想親自體驗的朋友就到此而止，玩完後再回來看吧！&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/UfnyYey.jpg&quot; alt=&quot;the last of us&quot; /&gt;&lt;/p&gt;

&lt;p&gt;       先提一些缺點，再來一一說明為何此遊戲我視為神作，首先這個遊戲背景設定並不新穎，末日僵屍啟示錄，女主角 Ellie 是唯一免疫，可能成為全人類救星的人。僵屍的來源倒有點有趣，靈感來自於一種會寄生蝸牛或昆蟲的真菌，因此有了讓它會感染（寄生）人類的想法，即為遊戲中所稱的「蟲草菌」，然後遊戲為單結局也非開放世界，有些人可能會因此減少興致不過這些都是我的愛。&lt;/p&gt;

&lt;p&gt;至於這遊戲我一直說是最優秀的遊戲作品，主要還是我認為他最高的成就是在「以遊戲的形式，說了一個精彩絕倫的故事」。&lt;/p&gt;

&lt;p&gt;第一整個遊戲橫跨四季，以冬季為中心點，冬季前和冬季後擁有高度對稱的劇情發展：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;遊戲以 Joel 的女兒 Sarah 的臉部特寫和以她的視角做為開始，遊戲最終段是操作 Ellie 並以她的臉部特寫做為結束。&lt;/li&gt;
  &lt;li&gt;轉換視角後 Joel 抱著 Sarah 逃難，遇到了持槍的士兵，然後失去了一個女兒，而末段前的醫院戰，Joel 抱著 Ellie 逃難，遇到了持槍的 Marlene，最終保護了 Ellie，得到了一個女兒。&lt;/li&gt;
  &lt;li&gt;而 Joel 會遇到 Ellie 則是一開始遇到 Marlene 後將 Ellie 帶離 FireFly 基地，最終又將 Ellie 帶回 FireFly 總部，Marlene 的手中。&lt;/li&gt;
  &lt;li&gt;一開始 Joel 嫌 Ellie 很吵總是不願多話只做必要的事，Ellie 卻極度地想要表現自我和尋求 Joel 的注意，然而後來 Ellie 在經歷許多後反思這一切而陷入沉默，那時的 Joel 已經是無法忽視 Ellie 的異樣與感受而主動地關心她了。&lt;/li&gt;
  &lt;li&gt;Joel 一度想離開 Ellie，最後是 Ellie 和她互相坦白把 Joel 找了回來繼續旅行，在 Joel 跌傷後 Ellie 離開了 Joel 去找藥和食物，被捉走後是 Joel 把她找了回來了。&lt;a href=&quot;https://www.youtube.com/watch?v=51Xs6sZGq3o&quot;&gt;兩人坦白的影片片段&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;       因高度對稱的關係，越到後期你會覺得這些場景都有 deja vu 的感覺，但心境已大不同，越來越心繫 Ellie ，最後抱著 Ellie 逃跑的時候，我內心完全自動把最初逃跑的那一幕重疊，內心也無比焦慮，禁不住地跟著 Joel 大喊：「這一次、這一次我絕對會把妳保護好！」&lt;/p&gt;

&lt;p&gt;       此外，遊戲中各個出現的 NPC 絕非打醬油，而是一段又一段地的情感催化劑，讓 Joel 和 Ellie 兩人的關係在旅途中不停地變化，比如 Tess 的自我犧牲（加深 Ellie 厭惡自我的罪惡感）， Bill 一個神經兮兮的火爆大叔，在幫忙 Joel 這任務的終點，是目睹自己的同性愛人上吊自殺的屍體。&lt;/p&gt;

&lt;p&gt;       而一開始關係緊張貌似敵人的黑人兄弟 Henry 和 Sam，初見面時劍拔弩張，最後在一同迎向一次又一次的絕望後，互相認可並成為好友，在 Joel 與 Ellie 的心中已佔據了一席之地，原來在殘忍醜陋的末世，還可以遇到能交心之人，而這樣美好的愉快夜晚卻被殘忍暴戾的早晨再次將他們推向另一個絕望的深淵。&lt;a href=&quot;https://www.youtube.com/watch?v=2GjhDMwudKY&quot;&gt;影片片段&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;       經歷過無數的絕望，看到一次比一次醜陋的人性，Ellie 除了Joel 以外早已不相信他人，這也是為何在 Joel 重傷後 Ellie 首次遇到食人魔 David 時，縱使 David 百般友善地釋出善意，Ellie 無論如何都不願相信他，最終制裁 David 時 Ellie 害怕驚慌，已然完全失控而 Joel 衝過來一把抱住她，不捨地抱緊她並脫口而出：「 oh, baby girl」，對比一開始嫌麻煩不想接下 Ellie 這「任務」的 Joel，Ellie 在她心中已經是無可取代的第二個女兒。&lt;a href=&quot;https://www.youtube.com/watch?v=NWfoMG3JHf4&quot;&gt;影片片段&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;       遊戲中還有各種遊玩時的細節，不停地在為轉折點發生時的情感加溫，比如一開始找到可收集元素的漫畫本時， Ellie 跟 Joel 解釋他還一臉「喔，是喔」，到後期探索時在一些角落找到了漫畫本，Joel 逐漸變成「Ellie 看到了一定會很開心」，當下臉上也會不自覺地掛著和 Joel 一樣的微笑。和黑人弟弟 Sam 逃難經過幼兒園時，Sam 看到一個機器人玩具非常想要撿走，其兄 Henry 用「只能帶一定要用到的東西」為由阻止了他，目擊這幕的 Ellie 會在幼兒園東張西望不走，但一旦你視線轉到 Ellie 不在的地方，她就會偷偷地把玩具撿起來，而且你可以用遊戲裡聽聲辨位的能力清楚地「聽」到 Ellie 彎腰把玩具撿起來塞進背包的動作！如果停在路上不動時 Ellie 也會發出各種牢騷，一起行動時也會不停地聊天，正是這些毫不馬虎的小細節，一直讓你能夠很細緻地去感受這些人物之間的情感與情緒。&lt;/p&gt;

&lt;p&gt;       除了高度對稱外，最後生還者的遊戲內容中也充滿了「對比」，比如 Henry 至始至終因擔心弟弟 Sam 會出事在逃難中完全不願放手讓他去面對危險或幫忙，最終兩人無法存活；Joel 一開始也是對 Ellie 抱持著相似的態度，但後來隨著關係進展，他也體認到如果不適度地放手讓她一起幫忙，兩個人都會無法存活下來，因此可以看到最後兩個人在戰鬥中合作無間，度過了一關又一關的難關。&lt;/p&gt;

&lt;p&gt;       雖然經歷過這麼多事，但 Ellie 仍然受到這些失去生命的親近之人所折磨著：為什麼是我？為什麼不是我？這樣的揪結在遇見長頸鹿後被她轉化為決心，決定完成自己的使命 - 成為全人類的疫苗，但是 Joel 卻退却了：「你知道的吧？我們也可以一起回到 Jackson City，和 Tommy 一起過生活，管他什麼疫苗？」但是若就這樣歸去，Ellie 認為無法給那些在她身邊失去生命的人一個交代，因此便決定一定要走到盡頭。&lt;a href=&quot;https://www.youtube.com/watch?v=znRz8sp61gU&quot;&gt;影片片段&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;       然而玩了這麼多小時到達最終的 FireFly 總部，在 Joel 得知疫苗的代價是 Ellie 的命時大開殺戒，神擋殺神，佛擋殺佛，只為了救出 Ellie，又或者說，救出當年她沒救到的 Sarah，最後快逃出時和 Marlene 有一段很有意思的對話，Marlene 說道：「你救不了她的，就算逃出這裡又怎樣，不是被 clickers 撕碎就是被人類姦殺！」Joel 回道：「That ain’t for you to decide.」Marlene 說：「這是她想要做的事，你知道的！」而 Joel 也的確知道，但最後在全人類存亡和女兒之間，Joel 選擇了女兒，這選擇不完美、自私，但卻充滿人味，這就是最後生還者的魅力所在。&lt;/p&gt;

&lt;p&gt;       最後那最最最最最棒又最難忘的，以謊言做為結尾的結局，我實在無法用言語形容它有多棒，當 Ellie 即將和 Joel 回到 Jackson City 時，她向 Joel 說出自己為何執意要走到旅程目的地的想法，她需要證明她的生存價值，而 Joel 以一個生存者過來人的身分告訴她，總有一天她會再找到她的生存目標（同時手摸了一下當年 Sarah 送給他做為生日禮物，早已不再走動的手表），而 Ellie 自我爭扎到達頂點時說出的話我永遠都能背得出來：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Swear to me, swear to me that everything you said about the FireFly is true.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;而 Joel 說了謊。&lt;/p&gt;

&lt;p&gt;       最後 Ellie 那雙咕嚕轉動的雙眼下的千絲萬緒，只有完整經歷過遊戲的人才有可能了解她在說出「Okay」時內心的真正想法，第一次在遊戲中，我了解到何謂表演和科技結合的藝術，這裡的表情捕捉非常真實細膩，這也是為何我如此推崇最後生還者的原因。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=RklOBIA6K5E&quot;&gt;最終結局影片片段&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;       最後我要提一下飾演 Ellie 的演員，Ashley Johnson 是我很喜歡的演員之一，雖然她不是好萊塢一線影星，但我就是很喜歡她的氣質和演戲的樣子，最後生還者遊戲總監 Neil Druckmann 有在推特發了一篇當時他們拍完最有野心的一幕的&lt;a href=&quot;https://twitter.com/neil_druckmann/status/1119026754952151040&quot;&gt;照片&lt;/a&gt;，Ashley 和 Troy (Joel 的演員) 之間也太有愛了吧，我久久無法忘懷。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/Gs47boh.jpg&quot; alt=&quot;Ashely And Tryo&quot; /&gt;&lt;/p&gt;

&lt;p&gt;       怎麼會有這麼棒的遊戲啦！我終於把內心的想法都一吐為快了，可以安心地準備來打二代了吧！！！最後生還者是個關於愛的故事，而二代是個關於仇恨的故事，無論如何，我都相信 Naughty Dog 會再次地說出一個超棒的故事。&lt;/p&gt;

&lt;p&gt;p.s. 本文引用之圖片皆為 Naughty Dog 所擁有。&lt;/p&gt;</content><author><name>Pofat</name></author><category term="Game" /><category term="TLOU" /><summary type="html">如果要問我認為最棒最優秀的單機遊戲，我一定毫不猶豫地說出「最後生還者」，沒有之一。</summary></entry><entry><title type="html">Opaque Result Type</title><link href="https://pofat.github.io/2019/08/05/Opaque-Result-Type.html" rel="alternate" type="text/html" title="Opaque Result Type" /><published>2019-08-05T00:00:00+00:00</published><updated>2019-08-05T00:00:00+00:00</updated><id>https://pofat.github.io/2019/08/05/Opaque-Result-Type</id><content type="html" xml:base="https://pofat.github.io/2019/08/05/Opaque-Result-Type.html">&lt;p&gt;上次寫完 PAT (Protcol with Assocaited Type) 不能做為型別的另一種解法沒多久（請見&lt;a href=&quot;https://pofat.dev/2019/05/21/%E9%87%8D%E6%96%B0%E6%AA%A2%E8%A6%96-swift-%E7%9A%84-protocol-%E4%BA%8C.html&quot;&gt;重新檢視 Swift 的 Protocl（二）&lt;/a&gt;），就迎來 WWDC 2019，在萬眾矚目的 SwiftUI 裡立刻展現了官方解決這個問題的帥氣手法，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;some View&lt;/code&gt;，也適逢 Podcast 提問箱有人提問什麼是 &lt;a href=&quot;https://github.com/apple/swift-evolution/blob/master/proposals/0244-opaque-result-types.md&quot;&gt;Opaque Result Type&lt;/a&gt; ，本期 Podcast 只有點到沒有深聊，所以在這討論一下。&lt;/p&gt;

&lt;h2 id=&quot;為何要有-opaque-result-type&quot;&gt;為何要有 Opaque Result Type&lt;/h2&gt;

&lt;p&gt;首先了解一下這個特性要解決什麼 Swift 5.1 前無法解決的問題：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;型別上對返回類型的抽象化無法被實現 (Type-level abastraction of returns)&lt;/li&gt;
  &lt;li&gt;無法建立帶有泛型類型的 existentail container （也就是無法用 PAT 做為一種類型）&lt;/li&gt;
  &lt;li&gt;如果有巢狀結構或是那種多層的泛型使用時常會需要在宣告時指明多層又臭又長的各層類型，像是：&lt;/li&gt;
&lt;/ol&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;kd&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Shape&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Rectangle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Shape&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Union&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Shape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Shape&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Shape&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Shape&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Shape&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Transformed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Shape&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Shape&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;shape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;S&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;GameObject&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;associatedtype&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ShapeType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Shape&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;shape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ShapeType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;EightPointedStar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;GameObject&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;shape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Union&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Rectangle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Transformed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Rectangle&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Union&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Rectangle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Transformed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;shape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Rectangle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;可見最後 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EightPointedStar.shape&lt;/code&gt; 的定義是多麼地可怕，以上幾個問題其實都來自於一個原先 Swift 泛型系綂設計的不足，那就是:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;只能由呼叫方(caller)來決定被呼叫方(callee)泛型參數最終要填入的真正型別&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;我們回顧一下實例：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;kd&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Shape&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Triangle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Shape&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 會印出：&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// *&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// **&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ***&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]()&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;repeating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;joined&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;separator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;FlippedShape&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Shape&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Shape&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;shape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;T&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;lines&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shape&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;separator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lines&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;reversed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;joined&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;separator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;smallTriangle&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Triangle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;flippedTriangle&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;FlippedShape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;shape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;smallTriangle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 這裡由 FlippedShape 決定 T == Triangle&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;flippedTriangle&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// ***&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// **&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// *&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;所以 Opaque Result Type 最重要的精神就是讓被呼叫方（callee），也就是實作方決定要回什麼真實的型別；因此外部呼叫後得到的就會是一個抽象的型別，這點與先前在宣告時給參數，外部需要明確地給予一個決定性的真實型別相反，可以視作是反向的泛型（reverse-generic）。&lt;/p&gt;

&lt;p&gt;有了這個特性，我們前面的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EightPointedStar&lt;/code&gt; 就可以簡化成&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;EightPointedStar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;GameObject&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;shape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Shape&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Union&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Rectangle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Transformed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;shape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Rectangle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;至於 PAT，我們就拿 SwiftUI 裡的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;some View&lt;/code&gt; 來說明，基本上 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;View&lt;/code&gt; 是一個帶有 associated type 的 Protocol&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;associatedtype&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Body&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;View&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Body&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;使用上是這樣的&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ContentView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello World&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;如此一來實作 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;View&lt;/code&gt; Protocol 的 ContentView 可以自由地決定要生成什麼 view，再也不是靠外部決定了，也因此 SwiftUI 在 runtime 呼叫 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;body&lt;/code&gt; 時無法跟我們以前的使用習慣一樣，它看到的是一個抽象的型別，要用遞迴的方式逐一檢查所有的 view 到底有哪些具體型別，這其中也有用 &lt;a href=&quot;https://forums.swift.org/t/dynamicviewproperty/25627/4&quot;&gt;metadata&lt;/a&gt; 來幫忙解析這些資訊。&lt;/p&gt;

&lt;h2 id=&quot;限制與注意事項&quot;&gt;限制與注意事項&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;最終的具體型別要在 compile time 能確定，比如你不能寫出&lt;/li&gt;
&lt;/ul&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;kd&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;P&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;P&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;P&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;f1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;condition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;P&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;condition&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;opaque&quot;&lt;/span&gt;   &lt;span class=&quot;c1&quot;&gt;// compile error&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;ul&gt;
  &lt;li&gt;最終還是會對應到一個具體的型別的，且具有單一性&lt;/li&gt;
&lt;/ul&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Equatable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Equatable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;condition&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 合法, 因為 x、y 是同一個泛型參數&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;condition&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1738&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;679&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;apples&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;bananas&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;apples&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;some fruit nobody's ever heard of&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 也合法，因為同個泛型參數&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;以下做法是不合法的&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;makeOpaque&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Any&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/* ... */&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;makeOpaque&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;makeOpaque&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Double&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// compile error，因為 x 的型別應該是 Any&amp;lt;Int&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;ul&gt;
  &lt;li&gt;不可包在 Optional 中，不可宣告在 protocol 中&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;以下的寫法也都不行，compiler 會抱怨的！&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;flip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)?&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Q&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;P&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;參考資料&quot;&gt;參考資料&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;weak self 第二集：&lt;a href=&quot;https://twitter.com/weak_self/status/1158049319653560321&quot;&gt;原來我不會用 protocol&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><author><name>Pofat</name></author><category term="Swift" /><category term="protocol" /><category term="SwiftUI" /><summary type="html">上次寫完 PAT (Protcol with Assocaited Type) 不能做為型別的另一種解法沒多久（請見重新檢視 Swift 的 Protocl（二）），就迎來 WWDC 2019，在萬眾矚目的 SwiftUI 裡立刻展現了官方解決這個問題的帥氣手法，some View，也適逢 Podcast 提問箱有人提問什麼是 Opaque Result Type ，本期 Podcast 只有點到沒有深聊，所以在這討論一下。</summary></entry><entry><title type="html">Value Type 也會在 Heap 裡嗎？</title><link href="https://pofat.github.io/2019/07/30/Value-Type-%E6%9C%83%E5%9C%A8-Heap-%E8%A3%A1%E5%97%8E.html" rel="alternate" type="text/html" title="Value Type 也會在 Heap 裡嗎？" /><published>2019-07-30T00:00:00+00:00</published><updated>2019-07-30T00:00:00+00:00</updated><id>https://pofat.github.io/2019/07/30/Value-Type-%E6%9C%83%E5%9C%A8-Heap-%E8%A3%A1%E5%97%8E</id><content type="html" xml:base="https://pofat.github.io/2019/07/30/Value-Type-%E6%9C%83%E5%9C%A8-Heap-%E8%A3%A1%E5%97%8E.html">&lt;p&gt;最近和兩位志同道合的 iOS 發燒友（現在還有人用這詞嗎？）一起開辦了一個針對 iOS 開發者的中文 Podcast，第一集就榮幸地有大師來踢館，因為我們在節目裡提到 Swift 的 value types 都會在 stack 裡，大師 CJ 表示不對喔，若是 reference type 的 value property 或者被 closure 捕獲的 value type 都會在 heap 裡！&lt;/p&gt;

&lt;iframe src=&quot;https://giphy.com/embed/fdRLQ2ApeAYkaXqXML&quot; width=&quot;480&quot; height=&quot;480&quot; style=&quot;display:block&quot; frameborder=&quot;0&quot; class=&quot;giphy-embed&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;這就有趣了不追究不行，但現在還沒有能力看完 Swift run time 的 source code，只好硬著頭皮想了個湊合的辦法來實驗一下。&lt;/p&gt;

&lt;h2 id=&quot;指向-class-instance-的-header-pointer&quot;&gt;指向 Class Instance 的 Header Pointer&lt;/h2&gt;

&lt;p&gt;以前看指標文章時有玩過直接取得位址後，可以對位址內容硬改值以改變本該無法被 mutate 的東西（比如 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;let&lt;/code&gt; ）
所以我們先宣告一個 class ，建立一個 instance，我們知道這個 instance 實際上的內容會存在 heap 裡的一塊連續空間裡，然後我們搞一個指向它 instance 開頭的指標：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Pofat&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;age&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;18&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;married&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;pofat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;headerPointer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Unmanaged&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;passUnretained&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pofat&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;AnyObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;toOpaque&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;照理來說這個 instance 裡所有的 property 應該會依著宣告的順序依序擺在這個 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;headerPointer&lt;/code&gt; 指向的位址之後，不過 class 前面有兩個 byte 是用來放 descriptor 以及 retain count，所以 property 會從第三個 byte 開始，我們用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MemoryLayout&lt;/code&gt; 來取得各個 property 所佔的空間大小，依序找出 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;name&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;age&lt;/code&gt; 以及 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;married&lt;/code&gt; 的位址：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;nameOffset&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;16&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ageOffset&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nameOffset&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;MemoryLayout&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stride&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;marriedOffset&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ageOffset&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;MemoryLayout&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stride&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Bound to the type of each property&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;namePointer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;haderPointer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;advanced&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;by&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nameOffset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;assumingMemoryBound&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;agePointer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;headerPointer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;advanced&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;by&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ageOffset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;assumingMemoryBound&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;marriedPointer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;headerPointer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;advanced&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;by&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;marriedOffset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;assumingMemoryBound&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Bool&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;這些位址當然還是在 heap 裡，而且應該指到就是這些 value type 的 property 所在地（吧），我們直接強行改變這些位置上的值實驗看看:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;n&quot;&gt;namePointer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pointee&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Joe&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;agePointer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pointee&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;30&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;marriedPointer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pointee&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;

&lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pofat&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;       &lt;span class=&quot;c1&quot;&gt;// get &quot;Joe&quot;&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pofat&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;        &lt;span class=&quot;c1&quot;&gt;// get 30&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pofat&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;married&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;// get false&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;OK，我們可以看到值如預料地變化了，這些 value type 真的是在 heap 裡啊！
也用了 Colab 做了一個可以直接在線上 run code 的&lt;a href=&quot;https://t.co/oiTZvLTKDB?amp=1&quot;&gt;版本&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;參考資料&quot;&gt;參考資料&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;weak self 第一集：&lt;a href=&quot;https://twitter.com/weak_self/status/1155511615661285376?s=21&quot;&gt;Swift API 設計之 Value Type 與 Reference Type&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;酄迎訂閱13、喬喬和我共同製作的 Podcast ，這裡有&lt;a href=&quot;http://bit.ly/weak_self_podcast&quot;&gt;訂閱指南&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;CJ Lin 大大不吝&lt;a href=&quot;https://twitter.com/eJamesLin/status/1155857719863042053&quot;&gt;指教&lt;/a&gt; 與他推薦的&lt;a href=&quot;https://link.medium.com/v96sFdHDIY&quot;&gt;文章&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</content><author><name>Pofat</name></author><category term="Swift" /><category term="struct" /><summary type="html">最近和兩位志同道合的 iOS 發燒友（現在還有人用這詞嗎？）一起開辦了一個針對 iOS 開發者的中文 Podcast，第一集就榮幸地有大師來踢館，因為我們在節目裡提到 Swift 的 value types 都會在 stack 裡，大師 CJ 表示不對喔，若是 reference type 的 value property 或者被 closure 捕獲的 value type 都會在 heap 裡！</summary></entry></feed>