You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

账号创建偶现EXC_BAD_ACCESS崩溃及Swift并发合规问题求助

创建账号时的偶发崩溃诊断与修复建议

崩溃现象

  • 创建账号时约20%概率触发崩溃,仅在Firebase App Distribution分发的应用中出现,模拟器运行无问题
  • 崩溃核心信息:
    Crashed: 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)

触发代码与相关逻辑

触发按钮与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
    }
}

AuthService代码

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()
    }
}

并发检查警告

开启严格/完整并发检查后,createAccount方法出现两个警告:

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)
    }

警告内容:

  1. let result行:Non-sendable type 'AuthDataResult' returned by call from main actor-isolated context to non-isolated instance method 'createUser(withEmail:password:)' cannot cross actor boundary
  2. try await行:Non-sendable type 'WritableKeyPath<UserProfileChangeRequest, String?>' exiting main actor-isolated context in call to non-isolated instance method 'updateProfile(_:to:)' cannot cross actor boundary

修复建议

1. 解决并发安全警告

处理AuthDataResult跨actor问题

Firebase的AuthDataResult未标记为Sendable,需将Firebase操作移至非主线程上下文:

  • 方案一:给AuthService添加自定义actor隔离
actor AuthService {
    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)
    }
}
  • 方案二:使用Task.detached在后台线程执行Firebase操作
func createAccount(email: String, password: String) async throws {
    try await Task.detached {
        let result = try await Auth.auth().createUser(withEmail: email, password: password)
        try await result.user.updateProfile(\.displayName, to: email)
    }.value
}

处理WritableKeyPath警告

给updateProfile方法添加@Sendable标记,确保参数符合并发安全要求:

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

2. 修复偶发崩溃问题

从调用栈看,崩溃发生在UIViewController释放阶段,大概率是并发导致的野指针或对象过度释放:

  • 确认ViewModel生命周期:@StateObject已保证ViewModel与视图同生命周期,需避免账号创建成功后导航时非法持有或提前释放ViewModel引用
  • 减轻主线程压力:通过上述actor或detached Task将Firebase操作移至后台,减少主线程在自动释放池弹出时的负载
  • 更新Firebase版本:升级Firebase Auth到最新版,官方可能已修复相关内存管理或并发兼容问题
  • 启用内存调试:在Release模式下开启Zombies工具(Xcode Scheme设置),捕获具体被过度释放的对象,定位根因

内容的提问来源于stack exchange,提问作者Mech

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 06:14:52