💬 Pofat 的話
這期份量特別飽滿,主要原因是接下來兩週我將前往一個網絡信號疲弱甚至可能完全無網絡的地方旅行,因此我決定休刊兩週。這週就先餵大家個粗飽,以解未來相思之苦(?)。
發表會近了,祝大家荷包平安。
💡 Hyrum’s Law
現在的工作中需要維護一個 libarary,最近在討論幾個回報問題時被同事介紹了Hyrum’s Law,這讓我意識到我狹隘的思維可能會對軟體維護造成風險。
當我感到他人誤會或錯用 API 時我總想著要改變呼叫端的行為再以文件輔佐約束,不過這並不一定有助於問題的解決,且可能會讓其更難用(造成更多火要救)。
當足夠多人使用你的 API,其原本的合約是什麼將不再重要,所有系統上可觀察的行為都會變成某些人的使用依據。
這個定律背後的意義是什麼呢? 如果想要延長系統的使用壽命和提昇使用量,開發者得判斷出何謂書面合同的邊界,以及系統有哪些可觀察的行為,並將兩者都納入考慮。如果持續忽略可觀察行為,可能會讓 API 變得很難用、甚至棄用。比如 API 的合同通常不保證效能,但如果效能太廢自然大家不會想用。
舉個近期實例, JSONEncoder 在 iOS 17 Encode 時,key 的排序會有偶發亂序(見 13 報 #199),此例中 key-value 的順序為可觀察行為,而書面合同(文件) 並無保證其為有序,但這已經成為部分開發者所仰賴的行為之一。
🌎 On Swift Community
SwiftUI View 生命中的一天
objc.io 作者之一 Chris Eidhof 在 Swift TO 解說 SwiftUI View 心智模型的演講,從程式碼如何轉變成 view tree 以及 render tree,整個 tree 如何進行 layout 的運算,不熟悉運作原理的值得一看。
SwiftUI 的高度抽象不代表不需理解其運作原理,正因為抽象隱藏了許多複雜,若使用上與其設計原則衝突,則會無比痛苦(每個版本都 breaking change 還不夠苦嗎?)
如果不喜歡看影片的話,Chris 也弄了一篇圖文並茂的逐字稿,文章比較容易前後尋找內容。
如何 Symbolic SwiftUI Crash Log
技術土炮爽文又一篇,如果使用非 Apple 官方的方收集 crash log (Crashlytics 等),你會對 SwiftUI 的 crash 感到頭痛,因為跟著 OS 釋出的 SwiftUI binary 沒有 symbol name。
Emerge Tools 做為我最愛的公司之一又沒令人失望,他們發表了一篇完整的教學,充滿著濃濃的 DIY 風,因為 symbol 是從官方管道下載的 crash logs 裡抽出來的,然後 symbol address 是手算出來的( ASLR,Apple 在 load app 到記憶體時會動態地載到不同位址,因為安全考量) ,這技巧可以應用到各種不同的 Apple framework,不限於 SwiftUI。此外,他們也預告已打造一個工具來輔助這工程,請期待下篇文。
全文見此連結。
SwiftUI + Core Data 內存优化之旅
肘子的文章讀來總是過癮,我在裡面最大的收獲是理解原來 @State 的釋放不夠積極,組合類型的 UI 裡面肥大的元件(應都 image 居多)放 @State
前要多考慮,以及如何在 onDisappear
後減少看不見 view 所佔用的記憶體量。
https://www.fatbobman.com/posts/memory-usage-optimization/
在 iOS 上裝 Swift Playgrounds
免越獄把 iPad 上的 Swift Playgrounds ,只要覆寫 Info.plist 的設定即可。當在思考 Swift 問題且手上只有 iPhone 時我常用 https://godbolt.org,但網頁畢竟受限於網路。Playgrounds 則自由輕便許多,畢竟我住的城市捷運裡幾乎沒網路。
詳情: https://mastodon.social/@khanhduy032@procursus.social/111000824062074366
🗣️ On Swift Forum
Fondation Workgroup
Swift 實做版本的新 Foundation 目前已經運用在今年的新版作業系統中 (寫到這突然想到上述的 iOS 17 JSONEncoder 亂序可能是因為換新實作),為了因應首要的效能與接下來更多的 porting 工作,新的 Foundation Workgroup 成立。
[SE-0408] Pack Iteration (Reviewing)
SE-0393 帶來強大的可變型別參數包(type and value parameter pack)後有個未解的問題,在泛型參數重覆展開成實際型態的過程中,無法早退。目前只能用 throw Error 的 workaround 達成:
struct NotEqual: Error {}
func == <each Element: Equatable>(lhs: (repeat each Element), rhs: (repeat each Element)) -> Bool {
// Local throwing function for operating over each element of a pack expansion.
func isEqual<T: Equatable>(_ left: T, _ right: T) throws {
if left == right {
return
}
throw NotEqual()
}
// Do-catch statement for short-circuiting as soon as two tuple elements are not equal.
do {
repeat isEqual(each lhs, each rhs)
} catch {
return false
}
return true
}
而這對熟悉 Sequence
的 Swift 開發者一點也不自然,所以此提案就是讓開發者可以 for-in 參數組,上述可改寫成:
func == <each Element: Equatable>(lhs: (repeat each Element), rhs: (repeat each Element)) -> Bool {
for (left, right) in repeat (each lhs, each rhs) {
guard left == right else { return false }
}
return true
}
比如呼叫端給的參數包是 (Int
, String
, Bool
),for-in 時就會依序拿到 Int
、String
,Bool
,而且 for-in 裡的 repeat 是 lazy 運算,如果提早 break 後面的表式示也不會繼續解析。
https://forums.swift.org/t/review-se-0408-pack-iteration/67152
Swift.org 增加 Packages 分類
Swift Package Index 的主要維護者 Dave 與 Sven 欲將這些 Swift package 資訊提昇為一等公民,也就是成為 Swift.org 首頁中分類之一。裡面的內容預期會有熱門選擇,Swift macros 的獨立子類,以及社群精選,精選清單會由 Swift Website Workgroup 來挑選維護,這對新加入 Swift 的開發圈的開發者無疑是一友善之舉,喜歡這想法的歡迎去論壇原文聲援。
預覽如下,也可按此進預覽網頁。
🤪 Pofat 選推
呼應本期特別內容 - Hyrum’s Law 的 xkcd
任何更新都會破壞某些人的工作流程
很漂亮的 widget
https://twitter.com/TideGuideApp/status/1700294042423022068?s=20
Apple Watch Ultra 上玩雷神之鎚