SwiftUI:在 iOS 14 上点击返回时导航链接崩溃,但在 iOS 13 上没有

Posted

技术标签:

【中文标题】SwiftUI:在 iOS 14 上点击返回时导航链接崩溃,但在 iOS 13 上没有【英文标题】:SwiftUI: Navigation Link crashes when tapping back on iOS 14 but not on iOS 13 【发布时间】:2021-02-27 15:13:56 【问题描述】:

我目前正在开发一个具有登录功能的应用程序来试用 SwiftUI。崩溃发生在模拟器和真实设备上。我可以成功登录并且能够进入下一个屏幕。但是,如果我按回并返回登录屏幕,则会发生崩溃。这是崩溃的唯一输出。

Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)

但在 ios 13 模拟器上运行时没有崩溃。我正在使用 XCode 12.2,并且崩溃也发生在 12.1 上。这是代码。

struct LoginView: View 

    @ObservedObject
    var loginViewModel: LoginViewModel
    
    @State var showSafari = false

    var body: some View 
        NavigationView 
            NavigationLink(destination: MainView(mainViewModel: MainViewModel(Fetcher: Fetcher())), isActive: $loginViewModel.successfulLogin) 
                Button(action: 
                    self.showSafari = true
                ) 
                    Text("Login")
                
                .sheet(isPresented: $showSafari) 
                    SafariView(handler: loginViewModel.handler)
                
            
        
        .navigationViewStyle(StackNavigationViewStyle())
    

class LoginViewModel: ObservableObject, Identifiable 
    
    private let fetcher: Fetchable
    private var disposables = Set<AnyCancellable>()
    
    @Published
    var successfulLogin: Bool = false
    
    lazy var handler: LoginVCHandler =  [weak self] user, error in
        if let self = self, let user = user, let Id = user.ID 
            self.getPlayerSummary(Id: Id)
         else 
            print("ERROR: \(error)")
        
    
    
    init(Fetcher: Fetchable) 
        self.Fetcher = Fetcher
    
    
    func getPlayerSummary(Id: String) 
        fetcher.getPlayerSummary(Id: Id)
            .map  response in
                response.response.players
            
            .receive(on: DispatchQueue.main)
            .sink(receiveCompletion:  value in
                switch value 
                case .failure:
                    print(value)
                    User.shared.cleareUserInfo()
                case .finished:
                  break
                
            , receiveValue:  [weak self] players in
                guard let self = self, let player = players.first else  return 
                User.shared.updateUserInfo(username: player.name, firstName: "", lastName: "", Id: Id)
                self.successfulLogin = true
            )
            .store(in: &disposables)
    

我看过其他问题,例如 Why does my SwiftUI app crash when navigating backwards after placing a `NavigationLink` inside of a `navigationBarItems` in a `NavigationView`? 和 NavigationLink Works Only for Once 表示问题已得到解决,但不是我所看到的。

这是崩溃发生时的堆栈跟踪。

* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
    frame #0: 0x0000000111c6d63c libsystem_platform.dylib`_os_unfair_lock_recursive_abort + 23
    frame #1: 0x0000000111c68a23 libsystem_platform.dylib`_os_unfair_lock_lock_slow + 256
    frame #2: 0x0000000108cc6f1b Combine`Combine.Publishers.ReceiveOn.(Inner in _1178A6B2012BC46DB46053E713A746B4).cancel() -> () + 27
    frame #3: 0x0000000108cc7c20 Combine`protocol witness for Combine.Cancellable.cancel() -> () in conformance Combine.Publishers.ReceiveOn<A, B>.(Inner in _1178A6B2012BC46DB46053E713A746B4)<A1> : Combine.Cancellable in Combine + 16
    frame #4: 0x0000000108c9234c Combine`Combine.Subscribers.Sink.cancel() -> () + 652
    frame #5: 0x0000000108c92510 Combine`protocol witness for Combine.Cancellable.cancel() -> () in conformance Combine.Subscribers.Sink<A, B> : Combine.Cancellable in Combine + 16
    frame #6: 0x0000000108c7e423 Combine`Combine.AnyCancellable.cancel() -> () + 339
    frame #7: 0x0000000108c7e5f9 Combine`Combine.AnyCancellable.__deallocating_deinit + 9
    frame #8: 0x000000010a676a70 libswiftCore.dylib`_swift_release_dealloc + 16
    frame #9: 0x000000010a66c74b libswiftCore.dylib`swift_arrayDestroy + 59
    frame #10: 0x000000010a4d20fc libswiftCore.dylib`Swift._SetStorage.deinit + 188
    frame #11: 0x000000010a4d2179 libswiftCore.dylib`Swift._SetStorage.__deallocating_deinit + 9
    frame #12: 0x000000010a676a70 libswiftCore.dylib`_swift_release_dealloc + 16
    frame #13: 0x000000010541eb87 GameTracker`outlined destroy of Set<AnyCancellable> at <compiler-generated>:0
    frame #14: 0x000000010542315b GameTracker`MainViewModel.deinit(self=0x0000600003c843f0) at MainViewModel.swift:0
    frame #15: 0x00000001054231b9 GameTracker`MainViewModel.__deallocating_deinit(self=0x0000600003c843f0) at MainViewModel.swift:0
    frame #16: 0x000000010a676a70 libswiftCore.dylib`_swift_release_dealloc + 16
    frame #17: 0x000000010a677519 libswiftCore.dylib`bool swift::RefCounts<swift::SideTableRefCountBits>::doDecrement<(swift::PerformDeinit)1>(unsigned int) + 217
    frame #18: 0x0000000105421545 GameTracker`___lldb_unnamed_symbol12$$GameTracker + 21
    frame #19: 0x000000010a676a70 libswiftCore.dylib`_swift_release_dealloc + 16
    frame #20: 0x0000000108d3c3f8 Combine`Combine.Publishers.FlatMap.(Outer in _E91C3F00A6DFAAFEA2009FAF507AE039).deinit + 120
    frame #21: 0x0000000108d3c459 Combine`Combine.Publishers.FlatMap.(Outer in _E91C3F00A6DFAAFEA2009FAF507AE039).__deallocating_deinit + 9
    frame #22: 0x000000010a676a70 libswiftCore.dylib`_swift_release_dealloc + 16
    frame #23: 0x0000000108c7f2c1 Combine`assignWithTake value witness for Combine.SubscriptionStatus + 49
    frame #24: 0x0000000108c7dc1d Combine`outlined assign with take of Combine.SubscriptionStatus + 29
    frame #25: 0x0000000108cc79cd Combine`closure #1 () -> () in Combine.Publishers.ReceiveOn.(Inner in _1178A6B2012BC46DB46053E713A746B4).receive(completion: Combine.Subscribers.Completion<A.Failure>) -> () + 141
    frame #26: 0x0000000109d4fade libswiftDispatch.dylib`reabstraction thunk helper from @escaping @callee_guaranteed () -> () to @escaping @callee_unowned @convention(block) () -> () + 14
    frame #27: 0x00000001117cc7ec libdispatch.dylib`_dispatch_call_block_and_release + 12
    frame #28: 0x00000001117cd9c8 libdispatch.dylib`_dispatch_client_callout + 8
    frame #29: 0x00000001117dbe75 libdispatch.dylib`_dispatch_main_queue_callback_4CF + 1152
    frame #30: 0x000000010b4bddab CoreFoundation`__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 9
    frame #31: 0x000000010b4b862e CoreFoundation`__CFRunLoopRun + 2685
    frame #32: 0x000000010b4b76c6 CoreFoundation`CFRunLoopRunSpecific + 567
    frame #33: 0x000000010a228db3 GraphicsServices`GSEventRunModal + 139
    frame #34: 0x000000010dee2187 UIKitCore`-[UIApplication _run] + 912
    frame #35: 0x000000010dee7038 UIKitCore`UIApplicationMain + 101
  * frame #36: 0x000000010543058b GameTracker`main at AppDelegate.swift:12:7
    frame #37: 0x000000011185c409 libdyld.dylib`start + 1

【问题讨论】:

崩溃需要调试,前提是代码不可测试。 How to create a Minimal, Reproducible Example 我现在添加了一个堆栈跟踪。希望这会有所帮助 【参考方案1】:

这实际上是一个非常简单的解决方案。只需将@ObservedObject 替换为@StateObject

【讨论】:

以上是关于SwiftUI:在 iOS 14 上点击返回时导航链接崩溃,但在 iOS 13 上没有的主要内容,如果未能解决你的问题,请参考以下文章

Swiftui:当用户点击推送通知时,如何导航到特定的 NavigationLink?

SwiftUI 2.0:如何使用没有标题的工具栏()自定义 iOS 14 导航栏?

SwiftUI - 未取消选择列表项

iOS SwiftUI 视频自动播放

iOS 16.2 在 SwiftUI 子视图中无法关闭弹出的(sheet)导航视图(NavigationView)之解决

导航链接在 SwiftUI 中只能使用一次