Swift 週報 第二十五期

語言: CN / TW / HK

前言

本期是 Swift 編輯組自主整理週報的第十六期,每個模塊已初步成型。各位讀者如果有好的提議,歡迎在文末留言。

歡迎投稿或推薦內容。目前計劃每兩週週一發佈,歡迎志同道合的朋友一起加入週報整理。

選擇與放棄,是生活與人生處處需要面對的關口。選擇了Swift社區,就擁有了一道最靚麗的風景!

週報精選

新聞和社區:印度將首次成為蘋果公司自有銷售地區

提案:提案 SE-0382、SE-0388、SE-0389 通過審查

Swift 論壇:提議 Observation 修訂

推薦博文:兩個新的開源 Swift 庫:Swift CertificatesSwift ASN.1

話題討論:

文心一言挑戰 ChatGPT,誰更勝一籌?

新聞和社區

印度將首次成為蘋果公司自有銷售地區

【環球網科技綜合報道】3 月 9 日消息,據外媒報道,蘋果公司正在改變國際業務的管理方式,推動印度首次成為其自有銷售區。

據悉,負責蘋果印度、中東、地中海、東歐和非洲地區業務的副總裁休斯·阿斯曼(HuguesAsseman)退休後,蘋果計劃將印度調整成自有銷售區域。而阿希什·喬杜裏(AshishChowdhary)將升職,成為印度地區的負責人,直接向蘋果的產品銷售負責人彙報。

外媒分析稱,作為世界第二大智能手機市場,印度市場對於蘋果而言越來越重要。2022 年第 四 季度,蘋果在印度市場的銷售額創新高。

目前,蘋果已開始在印度生產一些 iPhone 型號,包括 iPhone14 。此外,蘋果計劃於今年晚些時候在該國開設第一家零售店。

Meta第二輪裁員波及萬人,蘋果推遲發放獎金

硅谷的裁員、降薪潮遠未結束。

3 月 14 日週二,Meta 和蘋果紛紛宣佈最新的人事政策。

週二,Meta 的第二輪裁員終於確定,將裁員大約一萬人,同時還將關閉 5000 個額外空缺職位,以求節省開支、提高效率。這是 Meta 在六個月時間裏的第二輪重大裁員行動。

Meta 的首席執行官馬克·扎克伯格週二在聲明中表示,該公司的目標是通過取消管理層的多層級結構來讓公司變得更加扁平化。

週二,Meta 高開高走,當前股價上漲 5.93% ,報 191.62 美元。

上個月,有媒體報道稱,Meta 還一直致力於扁平化其組織,向管理人員提供買斷方案,並裁減其認為不必要的整個團隊,此舉仍在最後敲定中,可能會影響數千名員工。

知情人士稱,Meta 即將到來的新一輪裁員是由財務目標推動的,與公司“扁平化”沒有直接關係。知情人士説,Meta 已經看到廣告收入放緩,並將重點轉移到元宇宙平台,公司高層一直在要求董事和副總裁列出可以解僱的員工名單。

據知情人士透露,這一階段的裁員最快可能會在本週完成。一位知情人士説,那些正在制定裁員計劃的人希望在首席執行官扎克伯格為他的第三個孩子休育兒假之前準備好,因此這次裁員的速度可能會非常快。

App Store 的定價機制升級現已擴展至所有購買類型

在 12 月,我們宣佈對 App Store 進行問世至今最全面的定價機制升級,其中包括新增價格點和按店面管理定價的全新工具。即日起,這些升級和新價格適用於所有類型的 App 和 App 內購買項目,包括付費 App 和一次性 App 內購買項目。

更為靈活的價格點。可在 900 個價格點中選擇定價 — 比此前付費 App 和一次性 App 內購買項目的可選價格點數量增加了近 9 倍。這些選項也提供了更高的定價靈活度,價格點按價格區間逐漸遞增 (如在 RMB 10 以下每檔相差 RMB 0.5;RMB 10 到 RMB 200 之間每檔相差 RMB 1 等)。

增強的全球定價機制。全球均衡價格遵循了各個國家或地區最常見的定價方式。採用全球均衡價格,你可以提供更適用於當地顧客的定價。

根據基準價格提供全球定價形式。針對付費 App 和一次性 App 內購買項目,指定你熟悉的國家或地區,以之為基礎為其他 174 個國家或地區的店面以及 43 種貨幣生成全球均衡價格。你為這個基準店面設定的價格,Apple 不會根據税款或外匯變化進行調整。此外,你也可以按個人喜好為每個店面自行設定價格。

為上架產品提供地區性定價方案。針對不同國家和地區的店面決定 App 內購買項目 (包括訂閲) 的銷售範圍,因此你可以為各個市場分發定製的內容和服務。

準備好迎接即將在 5 月推出的增強全球定價機制 App Store 的全球均衡價格工具為你提供了一種簡單便捷的方式來管理國際市場的定價。在 2023 年 5 月 9 日,在所有 175 個 App Store 店面中的現有 App 和一次性 App 內購買項目的價格都將更新,以充分利用此次全新的增強全球定價機制。更新後的價格將根據金融數據機構提供的公開匯率信息做調整,在全球範圍內與你為基準店面設定的價格保持平衡。這些價格點將跟隨各個國家或地區最常見的定價方式,讓價格更適用於當地顧客。

你即刻就能使用 App Store Connect 或 App Store Connect API 更新你的當前定價,以充分利用此次全新升級。在 5 月 9 日,如果你的現有 App 和一次性 App 內購買項目還沒有完成價格更新,Apple 將以產品當前在美國店面的價格為基礎,為它們生成相應的更新價格。如果你希望以其他價格作為基礎,現在可通過更新基準店面的國家或地區,為 App 或 App 內購買項目選擇新的基準店面。你還可以選擇手動管理所選店面中的價格,而不使用均衡的價格。

提案

通過審查的提案

SE-0382 Expression Macros 提案通過審查。該提案已在 二十期週報 正在審查的提案模塊做了詳細介紹。

SE-0388 增加 Async[Throwing]Stream.makeStream 方法 提案通過審查。該提案已在 二十四期週報 正在審查的提案模塊做了詳細介紹。

SE-0389 Attached Macros 提案通過審查。該提案已在 二十四期週報 正在審查的提案模塊做了詳細介紹。

正在審查的提案

SE-0392 自定義 Actor 執行器 提案正在審查。

該提案介紹了一種用於自定義 actor 執行程序的基本機制。通過提供執行者的實例,參與者可以影響他們將在什麼地方執行正在運行的一些任務。

注意: 該提案僅定義了一組 API 來自定義 actor 執行器,其他類型的執行器控制超出了該特定提案的範圍。

Swift論壇

1) 提議Observation(修訂)

介紹

製作響應式應用程序通常需要能夠在基礎數據發生變化時更新演示文稿。 觀察者模式允許一個主題維護一個觀察者列表,並通知他們特定的或一般的狀態變化。 這具有不直接將對象耦合在一起並允許在潛在的多個觀察者之間隱式分佈更新的優點。 可觀察對象不需要關於其觀察者的特定信息。

這種設計模式是許多語言都走過的一條很好的道路,Swift 有機會提供一個健壯的、類型安全的和高性能的實現。 該提議定義了什麼是可觀察引用、觀察者需要遵守什麼以及類型與其觀察者之間的聯繫。

動機

Swift 中已經有一些觀察機制。 其中包括鍵值觀察 (KVO) 和 ObservableObject,但它們中的每一個都有侷限性。 KVO 只能與 NSObject 後代一起使用,而 ObservableObject 需要使用 Combine,它僅限於 Darwin 平台並且不使用當前的 Swift 併發功能。 通過從這些現有系統中吸取經驗,我們可以構建一個更普遍有用的功能,適用於所有 Swift 引用類型,而不僅僅是那些繼承自 NSObject 的引用類型,並使其跨平台工作,並具有 async/await 等語言功能的優勢。

現有系統獲得了許多正確的行為和特徵。 但是,有許多領域可以在安全性、性能和表現力之間提供更好的平衡。 例如,將相關的更改分組到一個獨立的事務中是一項常見的任務,但是這在使用 Combine 時很複雜並且在使用 KVO 時不受支持。 在實踐中,觀察者想要訪問交易,並能夠指定如何解釋交易。

註釋闡明瞭可觀察的內容,但也可能很麻煩。 例如,Combine 不僅要求類型符合 ObservableObject,還要求被觀察的每個屬性都標記為 @Published。 此外,無法直接觀察計算出的屬性。 實際上,在可觀察的類型中具有非觀察字段並不常見。

在整個文檔中,對 KVO 和 Combine 的引用將説明哪些功能是有益的並且可以合併到新方法中,以及可以以更穩健的方式解決哪些缺點。

現有技術

KVO

Objective-C 中的鍵值觀察很好地服務於該模型,但僅限於從 NSObject 繼承的類層次結構。 API 僅提供事件攔截,這意味着更改通知位於 willSet 和 didSet 事件之間。 KVO 在事件粒度方面具有很大的靈活性,但缺乏可組合性。 KVO 觀察者還必須繼承自 NSObject,並依賴 Objective-C 運行時來跟蹤發生的變化。 儘管 KVO 的接口已經更新為使用更現代的 Swift 強類型鍵路徑,但在底層它的事件仍然是字符串類型的。

Combine

Combine 的 ObservableObjectwillSet/didSet 事件的前緣產生變化,所有的值都在值被設置之前傳遞。 雖然這很好地服務於 SwiftUI,但它對非 SwiftUI 的使用有限制,並且對於第一次遇到該限制的開發人員來説可能會感到驚訝。 ObservableObject 還要求將所有觀察到的屬性標記為 @Published 以與更改事件進行交互。 在大多數情況下,這一要求適用於每一個單獨的屬性,對開發者來説變得多餘; 編寫符合 ObservableObject 類型的人必須重複(幾乎沒有真正獲得清晰度)註釋每個屬性。 最後,這會導致對參與項目或不參與項目的意義疲勞。

我們提出了一個名為 Observation 的新標準庫模塊,其中包括實現這種模式的協議、類型和宏。 基本上,一個類型可以簡單地通過使用 @Observable 宏註解將自己聲明為可觀察的:

Swift @Observable public final class MyObject { public var someProperty: String = "" public var someOtherProperty = 0 fileprivate var somePrivateProperty = 1 }

@Observable 宏聲明並實現與 Observable 協議的一致性,該協議包括一組處理觀察的擴展方法。 在最簡單的情況下,客户端可以使用 changes(for:) 方法來觀察給定實例的特定屬性的變化。

Swift func processChanges(_ object: MyObject) async { for await value in object.values(for: \.someProperty) { print(value) } }

這允許 Observable 類型的用户將特定值的更改或整個實例作為更改事件的異步序列進行觀察。 changes(for:) 方法提供類型安全,因為它只提供對一個特定屬性的更改。

Swift object.someProperty = "hello" // prints "hello" in the awaiting loop object.someOtherProperty += 1 // nothing is printed

可觀察對象還可以提供分組到事務中的更改,這些事務合併了在暫停點之間進行的任何更改。 默認情況下,交易會單獨交付給您提供的參與者或主要參與者。

Swift func processTransactions(_ object: MyObject) async { for await change in objects.changes(for: [\.someProperty, \.someOtherProperty]) { print(myObject.someProperty, myObject.someOtherProperty) } }

ObservableObject@Published 不同,@Observable 類型的屬性不需要單獨標記為可觀察。 相反,所有存儲的屬性都是隱式可觀察的。 對於只讀計算屬性,作者可以添加 static dependencies(of:) 方法來聲明額外的關鍵路徑作為他們觀察的一部分。 這類似於 KVO 用來提供對鍵路徑有影響的附加鍵路徑的機制。

```Swift extension MyObject { var someComputedProperty: Int { somePrivateProperty + someOtherProperty }

nonisolated static func dependencies(
    of keyPath: PartialKeyPath<Self>
) -> TrackedProperties<Self> {
    switch keyPath {
    case \.someComputedProperty:
        return [\.somePrivateProperty, \.someOtherProperty]
    default:
        return [keyPath]
    }
}

} ```

由於所有對觀察變化的訪問都是通過關鍵路徑進行的,因此 public 和 private 等可見性關鍵字決定了可以觀察到什麼,不能觀察到什麼。 與 KVO 不同,這意味着只能觀察到在特定範圍內可訪問的成員。 這一事實反映在設計中,其中事務表示為 TrackedProperties 實例,它允許查詢更改的鍵路徑,但不能查詢它們的迭代。

2) 提議在 Swift 6 中省略 some

介紹

目前,在類型位置中編寫普通協議名稱的默認值是 any,但其中許多隱含的 any 用法可以替換為 some,從而為它們提供更多類型信息,同時仍能正確運行。 該提議翻轉了默認值,以便在編寫普通協議時,類型將默認為 some 而不是 any。 使默認值 some 保證固定的底層類型,它保留與底層類型的靜態類型關係,使您可以完全訪問使用 Self 和關聯類型的協議要求和擴展方法。

動機

一段時間以來,Swift 一直致力於改進泛型的 UI,在 Swift 5.1 中引入了 some 關鍵字來表示不透明類型——特定具體類型的抽象類型佔位符——它在 Swift 5.7 類型中擴展到參數,這樣:

```Swift func foo(_ bar: T) where T: Equatable { }

// is equivalent to

func foo(_ bar: some T) { } ```

Swift 5.6 引入了 explicit any,這在 Swift 6 語言模式中是必需的,以確保選擇類型擦除而不是使用類型參數是明確和深思熟慮的。 引入使用顯式 any 的要求並鼓勵默認編寫一些,這為將一些作為普通協議名稱的默認值提供了機會。

通用代碼已經在比您想象的更多的地方得到了簡化。 以協議擴展為例。 要編寫適用於任何符合協議的具體類型的通用代碼,您只需編寫擴展關鍵字和普通協議名稱。 在此示例中,我們使用 Collection 協議:

Swift // What you write to extend all concrete types conforming to 'Collection': extension Collection { ... }

在通用代碼中,通用簽名表示通用參數和對這些參數的任何要求。 在協議擴展的情況下,有一個名為 Self 的隱式類型參數和單一一致性要求 Self: Collection 由編譯器添加,而不需要您編寫。 這允許您從符合要求的 Self 類型上的 Collection 協議訪問所有協議要求、關聯類型和其他擴展方法。

當存在不需要使用 where 子句內化泛型簽名的墊腳石時,程序員學習泛型編程會容易得多。 程序員可以使用更直接、更直觀的語法來表達同一事物。 Swift 6 可以將同樣的原則應用於其他上下文中的普通協議名稱。 這種做法對於學習 Swift 的初學者來説可能是無價的,它消除了在向代碼中添加協議時比較某些和任何之間的權衡的心理負擔。 初學者不需要在使用 some 還是 any 之間做出決定,推遲完全理解語義差異的需要,直到絕對有必要在兩者之間進行選擇。 即使您不是初學者,一些默認設置仍然可以通過使代碼更簡潔來提高代碼的可讀性。

3) 討論併發性能真的很差以及如何優化代碼

內容大概:

我們正在對各種編程語言的併發性進行比較,並使用 Swift 實現一維熱方程求解器。 與 Python、Rust 和 C++ 相比,swift 的性能看起來不是很好。 首先,代碼最多隻能擴展到三個內核,見下文

核心,總時間

10,2111.423936009407 8,2189.256893992424 5,1967.6182420253754 4,1929.6173659563065 2,3097.796007990837 1,4388.57520699501

然而,每秒的浮點運算相當低,例如在三個內核上我們每秒可以進行 500 次浮點運算。 與其他語言相比,這並不多。

所以,既然我們想發佈結果,我想尋求一些幫助,因為我認為我們在 Swift 中的實現並不好。

Vote最多的回答:

這種問題對於簡單的併發或多線程來説通常是一個非常糟糕的情況:

您的工作集太大。 在 2 * 10000000 * MemoryLayout(Double).size,你有一個 160MB 的工作集,它不適合緩存,所以你實際上受到內存速度的限制,而不是計算速度,這 與額外資源的擴展幾乎不一樣。

如果你用較小的工作集解決了這個問題,你就會被數據局部性所困擾。 您確實希望將每個 worker 固定到數據的固定部分,因此它會在下一個時間步位於與 worker 關聯的緩存中。

4) 討論@State 沒有正確初始化

5) 討論Swift 中的輕量級 MVVMLight 架構模式

6) 討論協議類型裏的 Generic“where” 失效

7) 討論如何使用 defer 模擬 RAII

內容大概

Swift 的 defer 語句具有很好的模擬 C++ 的資源獲取即初始化行為的能力,由於 ARC,我們無法做到這一點。 如果您正在使用 UnsafeMutablePointer 通過確保在退出範圍時正確清理資源來執行某些操作,這可能會很方便:

```Swift import Foundation

class FileHandler { private var file: UnsafeMutablePointer?

init?(filePath: String) {
    file = fopen(filePath, "r")
    guard file != nil else {
        print("Error: Unable to open the file.")
        return nil
    }
}

deinit {
    if file != nil {
        fclose(file)
    }
}

func readAndProcessFile() {
    defer {
        if file != nil {
            fclose(file)
            file = nil
        }
    }

    // Read and process the file
}

}

if let fileHandler = FileHandler(filePath: "path/to/your/file.txt") { fileHandler.readAndProcessFile() } ```

在這個例子中,我們有一個管理文件的 FileHandler 類。 當調用 readAndProcessFile 方法時,我們使用 defer 塊來確保在方法退出時關閉文件,無論它是正常退出還是由於錯誤退出。 這類似於 C++ 中的 RAII 概念,其中在對象超出範圍時執行資源清理。

當然,這不是 RAII 的 1:1,但它顯示了一種可以使用 defer 來實現類似效果的方法。

推薦博文

Swift 中如何使用 XCTest 框架進行性能測試

摘要: 本文介紹瞭如何在 Swift 中使用 XCTest 框架進行性能測試,並通過 measure 函數來測量應用程序中特定代碼路徑的性能。

兩個新的開源 Swift 庫:Swift Certificates 和 Swift ASN.1

摘要: 這篇文章介紹了兩個新的開源 Swift 庫:Swift Certificates 和 Swift ASN.1。這兩個庫共同提供了更快、更安全的 X.509 證書實現,X.509 證書是支持 TLS 安全的關鍵技術之一。文章解釋了X.509 證書和 ASN.1 格式的概念,和為什麼需要構建一個 ASN.1 庫以支持完整的 X.509 庫,並介紹了 Swift ASN.1 和 Swift Certificates 的功能和目標。 Swift Certificates 目前可以解析大多數符合 RFC 5280 標準和 Web PKI 中使用的 X.509 證書,支持插件式 X.509 驗證策略和 OCSP 分辨率。短期目標是使用 Swift Certificates 替換 swift-nio-ssl 中的 BoringSSL 實現,以提供更高性能和更好的內存安全性。

雲音樂 Swift 混編 Module 化實踐

摘要: 文章介紹了網易雲音樂 iOS App 在支持 Swift 混編過程中,Module 化階段的分析與實踐以及在實踐過程中可能會遇到各種未知問題。

話題討論

文心一言挑戰ChatGPT,誰更勝一籌?

  1. 文心一言
  2. ChatGPT
  3. 不分伯仲

歡迎在文末留言參與討論。

關於我們

Swift社區是由 Swift 愛好者共同維護的公益組織,我們在國內以微信公眾號的運營為主,我們會分享以 Swift實戰SwiftUlSwift基礎為核心的技術內容,也整理收集優秀的學習資料。

特別感謝 Swift社區 編輯部的每一位編輯,感謝大家的辛苦付出,為 Swift社區 提供優質內容,為 Swift 語言的發展貢獻自己的力量。

本文正在參加「金石計劃」