帐户创建时崩溃,包括错误和按钮代码

Crash on account creation, error and button code included

提问人:Mech 提问时间:5/21/2023 最后编辑:Mech 更新时间:5/22/2023 访问量:106

问:

我在尝试创建帐户时大约有 20% 的时间遇到以下错误: 有人可以帮我诊断问题吗

崩溃:com.apple.main-thread EXC_BAD_ACCESS KERN_INVALID_ADDRESS 0x0000000000000010

    Crashed: com.apple.main-thread
0  libobjc.A.dylib                0x2820 objc_msgSend + 32
1  UIKitCore                      0x2004a4 -[UIViewController dealloc] + 772
2  UIKitCore                      0x1fe0cc -[UINavigationController dealloc] + 308
3  UIKitCore                      0x486bbc -[_UISplitViewControllerColumnContents .cxx_destruct] + 44
4  libobjc.A.dylib                0x20a4 object_cxxDestructFromClass(objc_object*, objc_class*) + 116
5  libobjc.A.dylib                0x6e00 objc_destructInstance + 80
6  libobjc.A.dylib                0x104fc _objc_rootDealloc + 80
7  CoreFoundation                 0x2a26c cow_cleanup + 168
8  CoreFoundation                 0x62538 -[__NSDictionaryM dealloc] + 148
9  libobjc.A.dylib                0x20a4 object_cxxDestructFromClass(objc_object*, objc_class*) + 116
10 libobjc.A.dylib                0x6e00 objc_destructInstance + 80
11 libobjc.A.dylib                0x104fc _objc_rootDealloc + 80
12 libobjc.A.dylib                0x20a4 object_cxxDestructFromClass(objc_object*, objc_class*) + 116
13 libobjc.A.dylib                0x6e00 objc_destructInstance + 80
14 libobjc.A.dylib                0x104fc _objc_rootDealloc + 80
15 UIKitCore                      0x2008d8 -[UIResponder dealloc] + 124
16 UIKitCore                      0x2005dc -[UIViewController dealloc] + 1084
17 libobjc.A.dylib                0x21d4 AutoreleasePoolPage::releaseUntil(objc_object**) + 196
18 libobjc.A.dylib                0x5bdc objc_autoreleasePoolPop + 256
19 UIKitCore                      0x1a0444 -[_UIAfterCACommitBlock run] + 92
20 UIKitCore                      0x1a0364 -[_UIAfterCACommitQueue flush] + 168
21 UIKitCore                      0x1a0278 _runAfterCACommitDeferredBlocks + 496
22 UIKitCore                      0x3eee4 _cleanUpAfterCAFlushAndRunDeferredBlocks + 108
23 UIKitCore                      0x4fd608 _UIApplicationFlushCATransaction + 72
24 UIKitCore                      0x64de00 _UIUpdateSequenceRun + 84
25 UIKitCore                      0xcb1944 schedulerStepScheduledMainSection + 144
26 UIKitCore                      0xcb0ea0 runloopSourceCallback + 92
27 CoreFoundation                 0xd3208 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 28
28 CoreFoundation                 0xdf864 __CFRunLoopDoSource0 + 176
29 CoreFoundation                 0x646c8 __CFRunLoopDoSources0 + 244
30 CoreFoundation                 0x7a1c4 __CFRunLoopRun + 828
31 CoreFoundation                 0x7f4dc CFRunLoopRunSpecific + 612
32 GraphicsServices               0x135c GSEventRunModal + 164
33 UIKitCore                      0x39d37c -[UIApplication _run] + 888
34 UIKitCore                      0x39cfe0 UIApplicationMain + 340
35 SwiftUI                        0x1bc3d8 OUTLINED_FUNCTION_895 + 2472
36 SwiftUI                        0x1229b0 block_copy_helper.1 + 496
37 SwiftUI                        0x10ca54 OUTLINED_FUNCTION_901 + 2752
38 AppName                   0x7844 main + 23 (AppName.swift:23)
39 ???                            0x1ab1b0dec (Missing)

当我在模拟器中运行时,只有当我通过Firestore App分发运行时,我才会遇到这种崩溃。

导致崩溃的按钮和 viewModel 的代码:

@StateObject var viewModel: AuthViewModel.CreateAccountViewModel
...
Button("Create Account", action: viewModel.submit)

AuthViewModel:

func makeCreateAccountViewModel() -> CreateAccountViewModel {
        return CreateAccountViewModel(action: authService.createAccount(name:email:password:))
    }

class CreateAccountViewModel: FormViewModel<(name: String, email: String, password: String)> {
        convenience init(action: @escaping Action) {
            self.init(initialValue: (name: "", email: "", password: ""), action: action)
        }
    }

FormViewModel:

@MainActor
@dynamicMemberLookup
class FormViewModel<Value>: ObservableObject {
    typealias Action = (Value) async throws -> Void
    
    @Published var value: Value
    @Published var error: Error?
    @Published var isWorking = false
    
    subscript<T>(dynamicMember keyPath: WritableKeyPath<Value, T>) -> T {
        get { value[keyPath: keyPath] }
        set { value[keyPath: keyPath] = newValue }
    }
    
    private let action: Action
    
    init(initialValue: Value, action: @escaping Action) {
        self.value = initialValue
        self.action = action
    }
    
    nonisolated func submit() {
        Task {
            await handleSubmit()
        }
    }
    
    private func handleSubmit() async {
        isWorking = true
        do {
            try await action(value)
        } catch {
            print("[FormViewModel] Cannot submit: \(error)")
            self.error = error
        }
        isWorking = false
    }
}

身份验证服务:

private let auth = Auth.auth()

func createAccount(email: String, password: String) async throws {
        let result = try await auth.createUser(withEmail: email, password: password)
        try await result.user.updateProfile(\.displayName, to: email)
    }

private extension FirebaseAuth.User {
    func updateProfile<T>(_ keyPath: WritableKeyPath<UserProfileChangeRequest, T>, to newValue: T) async throws {
        var profileChangeRequest = createProfileChangeRequest()
        profileChangeRequest[keyPath: keyPath] = newValue
        try await profileChangeRequest.commitChanges()
    }
}

请提出任何问题,如果有什么需要添加以使其更清楚。

谢谢

进一步编辑: 完成 lorem ipsum 的建议后,我打开了“严格/完全并发” 这给了我以下关于此代码的“错误”:

func createAccount(email: String, password: String) async throws {
        let result = try await auth.createUser(withEmail: email, password: password)
        try await result.user.updateProfile(\.displayName, to: email)
    }

让结果部分: 通过调用从主参与者隔离上下文到非隔离实例方法“createUser(withEmail:password:)”返回的不可发送类型“AuthDataResult”不能跨越参与者边界 尝试等待部分: 在调用非独立实例方法“updateProfile(_:to:)”时退出主参与者隔离上下文的不可发送类型“WritableKeyPath<UserProfileChangeRequest, String?>”无法跨越参与者边界

有人对解决这个问题有建议吗?

谢谢

iOS Swift 错误处理

评论

0赞 lorem ipsum 5/22/2023
我的猜测是存在线程/actor 问题,您基本上正在通过您在那里进行的非隔离浮动任务来取消异步 await 的安全性。该任务可能在完成任何事情之前就已经死亡,然后您正在尝试更改 UI。但是有很多东西没有包括在内,SwiftUI 的实现非常严格,因为我们处理的是结构而不是类。由于您的规格,您可能无法在模拟器上重现此内容。您需要一个较旧、速度较慢、连接较差/速度较慢的设备。
0赞 Mech 5/22/2023
感谢您的回复,您对我今后的步骤有什么建议吗?
0赞 lorem ipsum 5/22/2023
很难说非隔离任务是一个问题,但其他东西(例如公共 StateObject)也可以,它们应该始终是私有的。

答:

0赞 lorem ipsum 5/22/2023 #1

MainActor 包装器的目的是在主 actor 上保持 UI 更新。您可以通过非隔离和“泄漏”来消除安全性。Task

尝试类似的东西

@State private var isProcessing: Bool = false

            Button {
                //This can start/stop a task
                isProcessing.toggle()
            } label: {
                Text("Create Account")
                    .task(id: isProcessing) {
                        guard isProcessing else {
                            return
                        }
                        await viewModel.handleSubmit()
                        isProcessing = false
                    }

            }

这将使您能够本着并发的精神“坚持”任务。

我还建议打开“严格/完全并发”或至少是“有针对性的”。

为什么“Task<C, Error>”即使“C”是一个不“Sendable”的类,或者是吗?

并添加

-Xfrontend -warn-concurrency 
-Xfrontend -enable-actor-data-race-checks

Build Settings > Swift Compiler > Other Swift Flags

评论

0赞 Mech 5/22/2023
谢谢你的建议!我已经实现了这段代码。我使handleSubmit不再是私有的,无法访问它。它对崩溃的频率产生了影响,但我仍然在~10%的时间内遇到相同的崩溃。我还没有实现第二部分,等等
0赞 lorem ipsum 5/22/2023
@Mech第二部分可能会打开一大罐蠕虫。但是没有办法绕过它,Swift 6最终会降临到我们身上。
0赞 Mech 5/22/2023
你没有错!我将尝试找出哪些蠕虫对解决问题很重要
0赞 Mech 5/22/2023
我收到 Auth Service createAccount 的以下问题:“从主参与者隔离上下文到非独立实例方法”createUser(withEmail:password:)“的调用返回的不可发送类型'AuthDataResult'无法跨越参与者边界' 这可能是问题所在
0赞 Mech 5/22/2023
在调用非独立实例方法“updateProfile(_:to:)”时,“不可发送类型'WritableKeyPath<UserProfileChangeRequest, String?>'退出主参与者隔离上下文”无法跨越参与者边界“也被调用为createAccount上的问题