[英]Property wrappers and SwiftUI environment: how can a property wrapper access the environment of its enclosing object?
SwiftUI 附帶的@FetchRequest屬性包裝器有助於聲明在 Core Data 存儲更改時自動更新的屬性。 您只需提供一個獲取請求:
struct MyView: View {
@FetchRequest(fetchRequest: /* some fetch request */)
var myValues: FetchedResults<MyValue>
}
如果沒有托管 object 上下文,提取請求無法訪問存儲。 此上下文必須在視圖的環境中傳遞。
現在我很困惑。
是否有任何公共 API 允許屬性包裝器訪問其封閉的 object 的環境,或者讓 SwiftUI 將此環境提供給屬性包裝器?
我們不知道如何實現SwiftUI的確切內部原理,但是我們可以根據可用的信息做出一些有根據的猜測。
首先, @propertyWrapper
不會從其包含的struct / class中自動訪問任何種類的上下文。 您可以查看規范以獲取證明。 在演化過程中對此進行了幾次討論,但未被接受。
因此,我們知道框架在運行時必須發生一些事情,才能將@EnvironmentObject
(此處為NSManagedObjectContext
)注入@FetchRequest
。 有關如何通過Mirror
API執行類似操作的示例,您可以在此問題中看到我的答案 。 (順便說一句,它是在@Property
可用之前編寫的,因此特定示例不再有用)。
但是, 本文建議使用@State
的示例實現並推測(基於程序集轉儲),而不是使用Mirror API,SwiftUI使用TypeMetadata來提高速度:
無鏡反射
仍然有一種無需使用鏡像即可獲取字段的方法。 它使用元數據。
元數據具有字段描述符,其中包含該類型字段的訪問器。 可以通過使用它來獲取字段。
我的各種實驗結果AttributeGraph.framework在內部使用元數據。 AttributeGraph.framework是SwiftUI內部用於構造ViewGraph的私有框架。
您可以通過框架的符號來查看它。
$ nm /System/Library/PrivateFrameworks/AttributeGraph.framework/AttributeGraph符號列表中有AG :: swift :: metadata_visitor :: visit_field。 我沒有分析整個匯編代碼,但名稱暗示AttributeGraph使用訪問者模式來解析元數據。
DynamicProperty
結構可以簡單地聲明@Environment
並且它將在調用update
之前設置,例如
struct FetchRequest2: DynamicProperty {
@Environment(\.managedObjectContext) private var context
@StateObject private var controller = FetchController()
func update(){
// context will now be valid
// set the context on the controller and do some fetching.
}
和Xcode 13只要你的屬性包裝器械(尚未在早期版本的測試) DynamicProperty
可以使用@Environment
屬性包裝。
下面的示例創建一個屬性包裝器, lineSpacing
從當前環境中讀取lineSpacing
。
@propertyWrapper
struct LineSpacing: DynamicProperty {
@Environment(\.lineSpacing) var lineSpacing: CGFloat
var wrappedValue: CGFloat {
lineSpacing
}
}
然后您可以像使用任何其他屬性包裝器一樣使用它:
struct LineSpacingDisplayView: View {
@LineSpacing private var lineSpacing: CGFloat
var body: some View {
Text("Line spacing: \(lineSpacing)")
}
}
struct ContentView: View {
var body: some View {
VStack {
LineSpacingDisplayView()
LineSpacingDisplayView()
.environment(\.lineSpacing, 99)
}
}
}
這顯示:
行距:0.000000
行距:99.000000
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.