前言
本期是 Swift 編輯組整理週報的第三十八期,每個模組已初步成型。各位讀者如果有好的提議,歡迎在文末留言。
Swift 週報在 GitHub 開源,歡迎提交 issue,投稿或推薦內容。目前計劃每兩週週一釋出,歡迎志同道合的朋友一起加入週報整理。
三餐四季,春夏秋冬,平凡如爾,與眾不同。Swift社群陪你苦盡甘來,笑看山河星月!👊👊👊
週報精選
新聞和社群:蘋果自研調變解調器晶片受挫:速度太慢容易過熱,落後高通3年
提案:在匯入宣告上使用訪問級別修飾符
Swift 論壇:討論 Swift 測試的新方法
推薦博文:Swift 中使用 actors 實現執行緒安全
話題討論:
中秋遇上了國慶,詩和遠方開始了躁動,假期將至,你準備怎麼過?
上期話題結果
從投票結果可以看出,蘋果的品牌忠誠度和聲譽在一些使用者中仍然很高,而華為和小米等品牌則透過不同的價效比策略吸引了其他一些使用者。手機市場競爭激烈,消費者有幸擁有多種選擇,以滿足他們的不同需求。
新聞和社群
蘋果自研調變解調器晶片受挫:速度太慢容易過熱,落後高通 3 年
去年底的測試發現,蘋果自研的調變解調器晶片速度太慢且容易過熱,電路板尺寸太大,佔據半個 iPhone 的面積,無法使用。這些晶片基本上比高通最好的調變解調器晶片落後 3 年。
熟悉該專案的蘋果前工程師和高管透露,由於技術挑戰、溝通不暢,以及高層對嘗試設計晶片而不是購買晶片是否明智的問題存在分歧,蘋果調變解調器晶片的工程團隊工作進展緩慢,且設定了不切實際的目標。
蘋果硬體技術高階副總裁約翰尼·斯魯吉(Johny Srouji)領導了晶片研發。
據蘋果公司前工程師和高管透露,該公司原計劃將其自研調變解調器晶片用在最新的 iPhone 機型中,但去年年底的測試發現,該晶片速度太慢且容易過熱,電路板尺寸太大,佔據半個 iPhone 的面積,無法使用。
調變解調器晶片的作用是連線手機與無線運營商,iPhone 目前依賴高通公司生產的調變解調器晶片。本月上旬,高通宣佈將蘋果的採購合同延長 3 年,業界推測蘋果自主研發調變解調器晶片的計劃遇到挫折。
2018 年,蘋果執行長蒂姆·庫克(Tim Cook)下達設計和製造調變解調器晶片的命令,並招聘數千名工程師。目標是切斷蘋果對高通的依賴。據估計,去年蘋果已向高通支付了超過 72 億美元晶片採購費用。在 2017 年的訴訟中,蘋果指控高通對其專利使用費收取過高費用。
熟悉該專案的蘋果前工程師和高管告訴《華爾街日報》,由於技術挑戰、溝通不暢,以及高層對嘗試設計晶片而不是購買晶片是否明智的問題存在分歧,蘋果調變解調器晶片的工程團隊工作進展緩慢。團隊被孤立在美國和國外的不同小組中,沒有全球領導者。一些高管不鼓勵工程師傳播有關延誤或挫折的壞訊息,從而導致設定不切實際的目標和最後期限。
蘋果在十多年前就致力於生產用於其產品的各種晶片。2010 年 1 月,蘋果創始人史蒂夫·賈伯斯在第一代 iPad 釋出會上低調展示了自研的 A4 晶片,這枚 45nm 製程的晶片由三星代工,一開始並不被業界看好。一年之後,蘋果在 iPhone 4S 釋出會上展示了第二代晶片 A5,效能提升巨大。此後,蘋果構建了由 A 系列(手機和平板)、M 系列(桌面電腦)、H 系列(耳機)、S 系列(手錶)等多個產品線的晶片家族。特別是在 2020 年,蘋果用 M1 晶片替代 Mac 電腦中使用多年的英特爾處理器晶片,震動了市場。今年 9 月釋出的 iPhone 15 Pro 系列更是搭載了全球首款 3nm 工藝製程晶片—— A17 Pro。
App Store 現已接受適用於最新版作業系統的 App 和遊戲提交
iOS 17、iPadOS 17、macOS Sonoma、Apple tvOS 17 和 watchOS 10 即將面向全球使用者推出。使用釋出候選版 Xcode 15 和最新 SDK 構建你的 App 和遊戲,透過 TestFlight 進行測試,然後提交到 App Store 以供稽核。現在,你可以著手從 Xcode Cloud 將你的 App 和遊戲無縫部署到 TestFlight 和 App Store。藉助激動人心的新功能,以及針對各種語言、框架、工具和服務的重大改進,你可以在 Apple 平臺上提供更加獨特的體驗。
Xcode 和 Swift:Xcode 15 提供增強的程式碼補齊功能、互動式預覽和實時動畫,可讓你更快地推進 App 的編碼和設計。Swift 透過引入宏解鎖了多個新的 API 型別,不但表現力強,且直觀易用。全新的 SwiftData 框架使用宣告式程式碼,可輕鬆保留資料。SwiftUI 還支援使用相位和關鍵幀建立更復雜的動畫,並透過新的 Observation 框架簡化資料流。
小元件和實時活動:小元件現在支援互動操作,並且可以在新的位置執行,例如 iPhone 上的待機介面、iPad 上的鎖定螢幕、Mac 上的桌面以及 Apple Watch 上的智慧疊放。藉助 SwiftUI,系統會根據情境調整小元件的顏色和間距,從而提高它在各個平臺中的實用性。透過 WidgetKit 和 ActivityKit 構建的實時活動現已在 iPad 上推出,以幫助使用者實時瞭解 App 中正在發生的事情。
Metal:藉助新的遊戲移植工具包,可以比以往更輕鬆地將遊戲移植到 Mac,Metal 著色器轉換器大大簡化了遊戲著色器和圖形程式碼的轉換過程。藉助最新的光線追蹤更新,可將你的遊戲和產品渲染器擴充套件到更逼真、更細膩的場景。此外還能利用許多其他增強功能,在 Apple 晶片上更輕鬆地提供精彩的遊戲和專業 App。
App 快捷指令:如果你適配了 App 快捷指令,App 的主要功能會自動出現在聚焦中,方便使用者快速訪問 App 中最重要的檢視和操作。新的設計讓 App 快捷指令的執行變得更加容易,新的自然語言功能讓使用者能夠更加靈活地用自己的聲音來執行你的快捷指令。
App Store:藉助 StoreKit 中的全新 SwiftUI 檢視,你現在可以更輕鬆地在所有平臺上推銷 App 內購買專案和訂閱。利用 Xcode 中的 StoreKit 測試、Apple 沙盒環境以及 TestFlight 的最新增強功能,你還可以對更多產品內容進行測試。透過按地區預購功能,你可以在新地區提供 App 並設定不同的釋出日期,讓使用者更期待你的 App 釋出。App Store 提供極為靈活且個性化的 App 發現體驗,根據使用者的興趣和偏好提供量身定製的推薦內容,幫助他們找到更多出色的 App。
提案
透過的提案
SE-0407 成員 Macro 一致性 提案透過審查。該提案已在 三十六期週報 正在審查的提案模組做了詳細介紹。
正在審查的提案
SE-0409 在匯入宣告上使用訪問級別修飾符 提案正在審查。
透過在匯入宣告上使用訪問級別修飾符來宣告依賴項的可見性,可以強制規定哪些宣告可以引用匯入的模組。可以將依賴項標記為僅對原始檔、模組、包或所有客戶端可見。這將讓宣告的訪問級別行為對依賴項和匯入的宣告也適用。此功能可以隱藏實現細節,有助於管理依賴項的擴散。
駁回的提案
SE-0406 對 AsyncStream 的 Backpressure 支援 提案被駁回。該提案已在 三十六期週報 正在審查的提案模組做了詳細介紹。
Swift論壇
1) 討論結構和型別(以前是匿名聯合型別)
從狀態檢查中衍生出一個關於匿名聯合型別主題的新討論執行緒:型別丟擲。
關於這個主題的衍生討論是圍繞這個評論開始的。
型別化丟擲就像類一樣,是靜態型別資訊的重要載體。 你所說的相當於說“不應允許類例項在彈性庫中具有特定的類型別,而應始終為 AnyObject”。 這顯然是非常錯誤的。 不小心將自己鎖定在特定錯誤型別中,然後在主要版本釋出後後悔的可能性不是語言問題,而是工程無能問題。 作者應該採取預防措施,在設計錯誤型別時考慮到未來的擴充套件(例如,具有可選後設資料的結構而不是裸列舉)。
當我們談論這個話題時:
匿名聯合型別 (A | B) 也是如此,它們只不過是某些通用列舉周圍的語法糖(例如 Either<A, B>)。 這不是什麼新鮮事,Swift 已經完全能夠表達這種型別,因此我不斷聽到的“由於編譯器複雜性而經常被拒絕的提案”顯然也是非常錯誤的。
結論:
型別系統必須具有工程師認為合適的表達能力,以使他們的程式碼具有表達能力。 僅僅因為有人想不出保留靜態型別資訊的理由(透過使用特定的錯誤型別或使用匿名聯合型別),並不意味著沒有理由。
2) Swift使用推出 Swift SDK 生成器
我們很高興地宣佈推出新的開源實用程式,它可以簡化 Swift 包的交叉編譯!
使用 Xcode 時,許多 Swift 開發人員每天都會使用從 macOS 到其他 Darwin 平臺的交叉編譯。 與此同時,使用命令列開發工具對 Linux 和 Swift 支援的其他平臺進行交叉編譯並不那麼容易設定。 透過 SE-0387 35,我們希望縮小這一差距,並使交叉編譯成為 SwiftPM 命令列介面中的一流功能。
雖然 SE-0387 指定了 Swift SDK 捆綁包的格式和檔案系統佈局,但它沒有規定如何生成這些捆綁包。 我們提供了此類生成器的參考實現,它支援 macOS 作為主機平臺和一些主要的 Linux 發行版作為目標平臺。
區分 Swift SDK 作者和 Swift SDK 使用者非常重要。 新的 Swift SDK Generator 應主要由 Swift SDK 作者使用,他們可以根據自己的需求對其進行自定義併發布自己的 Swift SDK 捆綁包。 反過來,Swift SDK 使用者可以依賴 Swift 5.9 中引入的 swift Experimental-sdk 命令來安裝 Swift SDK 作者之前生成的捆綁包。
我們正在努力增加對 Swift 專案正式支援的所有 Linux 發行版的支援。
3) 討論Swift 測試的新方法
大家好,
我很高興地宣佈一個新的開源專案,旨在探索 Swift 測試體驗的改進。 我和我的同事最近幾個月一直在致力於此工作,並取得了一些早期進展,我們很高興與大家分享。
受到 Swift 宏的啟發,我們構建了一個測試庫 API,它可以:
使用名為 @Test 的附加宏提供有關各個測試的詳細資訊。 這使得許多新功能成為可能,例如表達需求、傳遞引數或新增自定義標籤,所有這些都直接在程式碼中而不是單獨的配置檔案中實現。
使用拼寫為 #expect(...) 的表示式宏,透過詳細且可操作的故障資訊驗證測試中的預期條件。 它透過自動捕獲傳入表示式的值及其原始碼來通知失敗訊息,並且比專門的斷言函式更容易學習,因為它接受內建運算子表示式,如 #expect(a == b)
。
透過向函式新增引數並在 @Test 屬性中指定其引數,可以使用不同的輸入輕鬆重複測試多次。
這是一個示例:它顯示了一個測試函式,使用 @Test 表示,其中包含兩個特徵:自定義顯示名稱和決定測試是否應執行的條件。 該測試建立一輛食品卡車,在其中存放食物,然後使用 #expect 檢查食物數量是否等於我們期望的值:
@Test("The Food Truck has enough burritos",
.enabled(if: FoodTruck.isAvailable))
func foodAvailable() async throws {
let foodTruck = FoodTruck()
try await foodTruck.stock(.burrito, quanity: 15)
#expect(foodTruck.quantity(of: .burrito) == 20)
}
如果上述測試失敗,#expect 將捕獲數量(of: .burrito) 等子表示式的值以及原始碼文字。 這允許在輸出中包含豐富的診斷資訊:
✘ Test "The Food Truck has enough burritos" recorded an issue at FoodTruckTests.swift:8:6:
Expectation failed: (foodTruck.quantity(of: .burrito) → 15) == 20
使用這種方法,使用不同的輸入多次重複測試(稱為引數化測試 15)也很簡單。 @Test 屬性可以包含引數,並且該函式將被重複呼叫並傳遞每個引數:
@Test(arguments: [Food.burrito, .taco, .iceCream])
func foodAvailable(food: Food) {
let foodTruck = FoodTruck()
#expect(foodTruck.quantity(of: food) == 0)
}
Swift 測試的新 API 方向深入探討了我們的願景,描述了專案的目標,並展示了我們提出的方法的更多示例。
這些想法已在名為 swift-testing 的新包中原型化,該包目前被認為是實驗性的,尚未推薦用於一般生產用途。 如果你感興趣,我們鼓勵你克隆它,探索它的實現,並嘗試使用它為你的專案編寫測試。
4) 討論VSCode 5.9:停止伺服器失敗
自從升級到 5.9 以來,VSCode 上的 sourcekit-lsp 變得更加不穩定,我不斷收到“客戶端 SourceKit 語言伺服器:與伺服器的連線出錯。 關閉伺服器。” 問題,它列印的唯一日誌輸出是:
[Error - 4:44:34 PM] Stopping server failed
Message: Cannot call write after a stream was destroyed
Code: -32099
我相信,這是應該解決該問題的 PR:Don’t crash when unregistering for change notifications of a file that isn’t watched by ahoppen · Pull Request #828 · apple/sourcekit-lsp · GitHub
5) 討論對於傳遞到非同步作用域函式的閉包來說,Sendable 是否是必需的?
我一直在思考以下函式程式碼。
public extension Database {
func transaction<T>(_ closure: @Sendable @escaping (Database) async throws -> T) async throws -> T {
try await self.transaction { db -> EventLoopFuture<T> in
let promise = self.eventLoop.makePromise(of: T.self)
promise.completeWithTask{ try await closure(db) }
return promise.futureResult
}.get()
}
}
這是來自 Vapor 框架的實際程式碼。
以下是供參考的網址:https://github.com/vapor/ Fluent-kit/blob/main/Sources/FluentKit/Concurrency/Database%2BConcurrency.swift 1
在這個事務函式中,引數閉包具有 @Sendable
和 @escaping
屬性。
我想知道是否可以將兩者刪除。
特別是,@Sendable
屬性意味著傳遞給閉包的型別必須是 Sendable,這施加了相當嚴格的限制。因此,如果我們可以省略它,那就方便多了。
我認為它可以被刪除的原因是,雖然這個閉包確實被傳遞到事件迴圈執行緒,當它離開交易功能時,它正在等待 EventLoopFuture.get()
,確保閉包的函式呼叫完成。
換句話說,兩個不同執行緒不可能同時呼叫閉包。
確實,理論上由於 eventLoop 型別被抽象為任何 EventLoop,實現一種將傳遞給 completeWithTask
的閉包儲存到全域性變數或類似的東西中的方法是可能的,但這對於 EventLoop 和 EventLoopFuture 來說顯然是不自然的行為,我認為沒有什麼可擔心的。
此外,我認為出於同樣的原因可以消除@escaping。 閉包實際上並沒有逃脫。
上面的想法可能是對的嗎?
我很想聽聽有更多見解的人的想法來權衡。
作為參考,具體實現如下:
public extension Database {
func transaction<T>(_ closure: (any Database) async throws -> T) async throws -> T {
try await withoutActuallyEscaping(closure) { (closure) in
let closureBox = UncheckedSendableBox(closure)
return try await self.transaction { db -> EventLoopFuture<T> in
let dbBox = UncheckedSendableBox(db)
let promise = self.eventLoop.makePromise(of: T.self)
promise.completeWithTask {
let db = dbBox.value
let closure = closureBox.value
return try await closure(db)
}
return promise.futureResult
}.get()
}
}
}
我相信這個想法可以推廣。
我將這些接受值並允許使用閉包進行靈活處理的函式稱為作用域函式。
這樣的作用域函式確實可以是非同步的,但是,即使它們是非同步的,在我看來,只要作用域函式中的閉包執行是序列完成的,它們不一定必須是 @Sendable
或 @escaping
。
你對此有何看法?
回答
這個問題很好理解,但解決方案不是放棄當前語言中的 Sendable 要求,而是讓編譯器可以推斷出根本不需要它。 請參閱 Pitch 跨隔離域安全傳送非“可傳送”值,瞭解編譯器如何增強此功能的示例。
我同意你的觀點,只要我們排除非同步程式碼中不安全的行為,這種使用模式可能是安全的,但此時我仍然不願意刪除註釋。
6) 討論編寫 TCP 客戶端應用程式的推薦方法是什麼?
我需要為 TCP/IP 上的自定義專有協議編寫一個客戶端。 我希望它能夠在 macOS、iOS 和 Linux 上使用。 推薦的方法是什麼?
我有一組現有的 Objective-C 程式碼來執行此操作,並且我只使用原始 BSD 套接字。 它們很簡單,並且由於不需要是高效能伺服器,所以我非常樂意阻塞:我只需將程式碼貼上在 NSOperation 中,在序列 NSOperationQueue 上執行它,並使用回撥來傳遞結果。 在 Swift 中使用 BSD 套接字感覺就像我在與該語言作鬥爭:很多都陷入了 UnsafePointer 領域。
我檢視了 Mojave 和 Swift-NIO 中引入的網路框架,但在這兩種情況下,我真的不確定如何構建客戶端。 我需要做很多來回操作:向事物傳送命令,讀迴響應,傳送下一個命令,讀取響應等。透過單個通道讀取處理程式(在 Swift-NIO 的情況下)感覺所有內容, 再次,就像我做錯事一樣。
有誰知道 Swift-NIO 類似的來回通訊示例嗎? 或者我看錯了方向?
回答
自從我上次檢視我的程式碼以來已經過去很長時間了,我確信自那時起 API 已經發生了很大的變化,但對我幫助最大的是檢視 Java 的 Netty 文件。 Swift-NIO 現在似乎有相當好的文件,所以我會先閱讀一下。
同樣,它已經很老了,而且事情可能已經發生了變化,但這裡有一個簡單的示例,說明 Swift-NIO 客戶端和處理程式類如何協同工作。 這個預設實現會讓你遇到你提到的確切問題,但是如果你在 TCP 客戶端類之外宣告通道、處理程式、事件迴圈等,你可以處理處理程式類中發生的更改,例如斷開連線或接收訊息, 在客戶端類的其他方法中。 我不確定這是否是“正確”的處理方式,但它足以讓它在我正在構建的應用程式中順利執行。
你可能會考慮由 IBM 開發並在 macOS、iOS 和 Linux 上執行的 BlueSocket。
我向這個庫新增了對 Windows 的支援,並以 GreenSocket 的名稱提供。
BlueSocket 此處(macOS、iOS、Linux):
https://github.com/Kitura/BlueSocket
https://github.com/litewrap/GreenSocket
7) 討論協議擴充套件可以定義類 API 覆蓋嗎?
我有幾個符合協議的 UIViewController 子類(它們不共享相同的父類)。 我想新增幾個 UIViewController API 重寫的預設實現,以避免在每個子類中重寫它們。 無論如何要讓這項工作成功嗎?
protocol StylingController {
}
extension StylingController where Self: UIViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
becomeFirstResponder()
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
resignFirstResponder()
}
}
回答
在這種情況下,只需建立兩個父類而不是一個:一個基於 UIViewController,另一個基於 UITableViewController。 如果有很多重疊的功能,並且希望它儘可能DRY,可以進一步將通用功能提取到協議擴充套件中:
class BaseViewController: UIViewController, CommonVCFunctionality {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
commonVCFunctionality()
}
}
class BaseTableViewController: UITableViewController, CommonVCFunctionality {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
commonVCFunctionality()
}
}
protocol CommonVCFunctionality: AnyObject {
func commonVCFunctionality()
}
extension CommonVCFunctionality {
func commonVCFunctionality() { ... }
}
推薦博文
Swift 中使用 actors 實現執行緒安全
摘要: 本文介紹了在 Swift 中使用 actors 實現執行緒安全的方法。首先,文章回顧了 Store 型別的定義,它允許我們可預測地實現狀態管理,但這個型別不是執行緒安全的。為了解決這個問題,文章使用了一個 NSRecursiveLock 型別的例項來確保執行緒安全。然而,作者指出使用鎖存在一些缺點,並引入了 actors 這個新的 Swift 語言特性。介紹瞭如何使用 actors 以及與使用鎖相比的優點,並討論了 actor 的重入問題。最後總結了actors在 Swift 中的重要性和優勢。
深入理解 Observation - 原理,back porting 和效能
摘要: 喵神這篇文章討論了 SwiftUI 中的狀態管理,特別是引用型別的狀態管理,以及 Apple 在 iOS 14 中推出的新 Observation 框架。Observation 框架可以在 View 中實現屬性粒度的訂閱,避免不必要的重新整理。它實質上透過新增 @ObservationTracked 宏將儲存屬性轉換為計算屬性,並新增與 ObservationRegistrar 相關的內容來實現。透過閱讀本文,你將更瞭解 SwiftUI 中的新 Observation 框架及其優勢。
貨拉拉 iOS 使用者端 10 萬分位 Crash 率攻堅之戰
摘要: 該文主要介紹了貨拉拉 iOS 使用者端在 Crash 治理方面的經驗和技術方案。文章探討了 iOS 平臺下 Crash 監控方案的優缺點,並分享了自建 Crash 監控平臺的思路和經驗。隨後,總結了 Crash 治理的思路和經驗,包括分級治理、版本追蹤、定期分析和團隊合作。最後,文章分享了常見的 Crash 型別及其解決方案,並總結了長期 Crash 治理的經驗和收益。
話題討論
中秋遇上了國慶,詩和遠方開始了躁動,假期將至,你準備怎麼過?
- 旅遊團已報名,攻略方案已熟記,靜待啟程,我的青春我做主。
- 老婆孩子,三餐四季,和生活對線,已傾盡了所有,錢包已然羞澀,歲月靜好足矣。
- 錢不錢的無所謂,主要是我爬山嫌累,涉水嗆口,夢想也是會變的嘛,西瓜啤酒,空調刷劇也是極好的。
歡迎在文末留言參與討論。
關於我們
Swift社群是由 Swift 愛好者共同維護的公益組織,我們在國內以微信公眾號的運營為主,我們會分享以 Swift實戰、SwiftUl、Swift基礎為核心的技術內容,也整理收集優秀的學習資料。
特別感謝 Swift社群 編輯部的每一位編輯,感謝大家的辛苦付出,為 Swift社群 提供優質內容,為 Swift 語言的發展貢獻自己的力量。