💬 Pofat 的話
其一:本週週報有史以來最長,都圍繞著危險:有 Apple 危險的 API,以及如何防止危險的措施,總共兩千多字,大家有興趣就慢慢看。
其二:以往總是說請用贊助支持我繼續寫作,但自己好像也不是真的這麼認為,這週洗澡時對於贊助寫作這件事突然有不同的想法 - 分享快樂才是最重要的。如果你在讀波報時感到快樂,或是像「原來如此啊」、「這真是個好故事呢」之類的正向情緒,然後也想回饋這樣的正向情緒(給我),就贊助我吧!因為我會把全部的贊助都拿去買遊戲,你的贊助都會成為我的快樂!
想回饋好心情給我的讀者,歡迎贊助我。
🌎 On Swift Community
Advancing iMessage security: iMessage Contact Key Verification
Apple 在 iOS 17.2、macOS 14.2 和 watchOS 10.2 的開發者預覽版中提供了 iMessage 聯絡人的密鑰驗證功能,確保他們是和想要的對方發送消息。Apple security research blog 也同步發佈了其保證安全性的方式與其面臨的挑戰。
https://security.apple.com/blog/imessage-contact-key-verification/
SwiftUI Sheets Leak in iOS 17
sheet
和 fullScreenCover
在 iOS 17 有 memory leak,在修復前的解方是: 先用 UIKit 吧。
https://developer.apple.com/forums/thread/737967?answerId=767599022#767599022
Tuist 的故事
老實說我現在才認識這個用來改善 Xcode 在大型專案開發體驗的工具,上週波報講到 Xcode 不良於應付大型專案,所以 Spotify 搬遷到 Buck 的故事(也有推友分享他們逆向搬回去純 Xcode 的經驗),能用 Xcode 當然最好,所以我樂見此類專案繼續成長,但開源工具怎麼持續性地獲利仍然是個巨大的挑戰。
同時本週也有另人開心的消息,被 Google 丟棄的 fastlane 終於順利轉移到 MNF 之下: https://github.com/MobileNativeFoundation/discussions/discussions/194
https://pepicrft.me/blog/2023/10/28/open-source-sustainability
搬遷至 swift-testing
如果你想嘗試用新的 swift-testing 來替代 XCTest ,官方搬遷文件有很完整的說明,還有表格列出 XCTest 下的方法如何對應到 swift-testing 下的 macro。
但我要再提醒一下,Swift macros 需要依賴 SwiftSyntax ,而 SwiftSyntax 的依賴處理不如一般框架,需小心處理,想知道如何安全處理的請見 pointfree 的好文。
https://x.com/polpielladev/status/1717859039512555986?s=20
🗣️ On Swift Forum
也許你不該使用 @backDeploy
@backDeploy
會 override 支援 OS 的原始實作(非 back 的部分),老實說這個看來幾乎是專為 Apple 第一方開發的功能,加上文件說明容易讓大家有不正確的認知,除非你是真正會需要這功能的人(如 SDK 開發者),不然請小心使用。當然最理想策略是為這個標記加上錯誤使用的警告,如不用於 library evoluation 時,或 Apple 至少給予更清楚的說明。
[Pitch] Region Based Isolation
Swift Concurrency 實現資料安全的關鍵概念就是「提供能夠隔離同步程式狀態的機制以消除資料競爭(data races)」,這隔離的單位即 actor, SE-0302規範了僅遵循 Sendable
的物件可以跨離隔離邊界傳遞(從外界傳入 actor ),從語言層面防止競爭。只是這個規範過度保守了些,很多用例就算不遵守也能安全使用。比如物件被傳入 actor 後就不再使用,自然不存在競爭的機會。
為了放寬這些規則,此提案提出了一套定義以編譯器能夠推斷是否存在資料競爭(以嚴謹的方式證明),編譯器無法推導時則會輸出診斷。這裡除了可能會有的新標記 @returnsIsolated
外不存在 Swift 使用者肉眼可見的功能,是個讓編譯器變聰明的提案,以後就放心傳非 Sendable
物件進 actor
或 Task
,或者用來 init actor (SE-0327 規定只有 designated initializer 可以接受非 Sendable
參數),剩下交給編譯器去檢查即可(🤞)。
如果只想知道這個提案對 Swift 使用者的影響讀到這裡就可以了,以下內容是我對於其推斷方法的理解,不知道也不影響使用。
設計說明
為了做出判斷,編譯器需要理解的核心概念是「傳入同步執行程式點內資料是否有其它存取路徑」,因此提出了「隔離集合」(isoliation region,因為還有另一個名詞 domain 也是區域的概念,這裡我用意譯)的概念,同一個隔離集合內的資料代表存在著相關聯的存取路徑,若被傳入同步執行就會有發生資料競爭的可能,只有同時傳輸完全不同隔離集合的非 Sendable
資料才是安全的,使用者不用特地標上 @Sendable
,比如:
// 把 a、b 傳入同 actor instance 是安全的,因為各在不同的隔離集合
let a = NonSendable()
let b = NonSendable()
await SomeActor.shared.doSomething(a)
await SomeActor.shared.doSomething(b)
隔離集合是非 Sendable
值的集合,可以受 actor instance,task,global actor 這些隔離領域(isolation domain)的保護,或是完全與這幾個領域無關,等待傳遞。隨著程式的執行,若不同集合內的值可彼此訪問(或被取別名),這些集合就會互相合併,變成更大的集合。
往下討論之前要先介紹表示這些概念的符號,因為它們在 Swift 並無對應實體:
[{(x, y), #SomeDomain}, (z), (w, t)]
這裡表示三個隔離集合,(x, y),(z) 與 (w, t),其中 (x, y) 被某個隔離領域(actora instance,global actor 或 task)保護,不能再被傳入其它隔離領域使用,其它兩個集合則沒和任何領域有關聯,再沿用上例說明:
await SomeActor.shared.doSomething(w)
await SomeActor.shared.doSomething(t) // 不安全,因為 w t 同集合,除非這裡改傳入 z
合併集合的規則
為值取別名或增加訪問路徑都會將隔離集合合併,這裡的操作可以由由賦值或調用函數實現,其實存取屬性也只是調用函數的語法糖,下面舉幾個例
func assignValueToField() {
let x = NonSendableStruct()
// Regions: [(x)]
let y = NonSendable()
// Regions: [(x), (y)]
x.field = y
// Regions: [(x, y)]
}
func transfer(x: NonSendable, y: NonSendable) {
// Regions: [(x, y)]
let z = NonSendable()
// Regions: [(x, y), z]
f(x, z)
// Regions: [(x, y, z)]
}
// 如果值被 closure capture 後再賦值,則新舊的集合合併,反之舊的集合會被遺忘
func mutableBindingAssignmentClosure() {
var x = NonSendable()
// Regions: [(x)]
let closure = { useInOut(&x) }
// Regions: [(x, closure)]
let y = NonSendable()
// Regions: [(x, closure), y]
x = y
// Regions: [(x, closure, y)]
}
最後合併也會受到 if-else 等控制流的影響,只要任一分支有合併集合的邏輯,這個控制流語句後就會合併集合,採取「只要有可能就當會發生」的保守策略,更多細節可以看附錄。
規則一個隔離集合一個時間內只能傳入一個隔離域使用(被其保護),差別是 task 執行完後連結會斷開,之後可以再傳給其它隔離域,而傳入 actor 的則會永遠綁定,無法再傳給其它人。
跨邊界傳遞不會傳遞所有權
對 ownership 熟悉的朋友看到這可能會有違和感,把值傳輸進隔離域後任何訪問都是非法的(除非斷開聯結),可是調用方仍擁有該值,即調用方能決定它無權訪問的的生命週期(這裡稱做弱傳輸),何不一併轉移所有權(強傳輸)?
若要預設強傳輸會有幾個問題:
ABI 不相容
這個操作永遠會有「第二個存取」因為第一個在傳遞數據,第二個是傳遞所有權,所以可能還會額外地調用 deinit,更可能引入預期外的副作用
轉換需要的 ARC 操作會降低效能,除非 inline,但這種解法需要很積極的 inline,又會造成 binary size 的增加。
因此強傳輸不會是預設行為,但提案考慮未來能透過 transferring
的標記來啟用這行為,
🤪 Pofat 選推
應泰勒絲新專輯發行,Apple 的 engineer manager 改編泰勒絲老歌來紀念 Swift 2 -> 3 重大遷移的時光,經歷過的應該心有戚戚焉
https://mas.to/@ayanonagon/111307920275309847
本週金句