MAIN THREAD – CRASHED
closure #1 (Swift.Bool) -> () in YourProject.addPublisherHandler() -> () :0
Combine
0x1a551b000 + 44748
Combine
0x1a551b000 + 44640
Combine
0x1a551b000 + 79636
Combine
0x1a551b000 + 78876
Combine
0x1a551b000 + 77648
Combine
0x1a551b000 + 77260
Redflower
closure #3 () -> () in YourProject.fetchDetails() -> () ViewModel+Network.swift:47
Redflower
reabstraction thunk helper from @escaping @callee_guaranteed () -> () to @escaping @callee_unowned @convention(block) () -> () :0
libdispatch.dylib
_dispatch_call_block_and_release
libdispatch.dylib
_dispatch_client_callout
libdispatch.dylib
_dispatch_main_queue_drain
libdispatch.dylib
_dispatch_main_queue_callback_4CF
CoreFoundation
CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE
CoreFoundation
__CFRunLoopRun
CoreFoundation
CFRunLoopRunSpecific
GraphicsServices
GSEventRunModal
UIKitCore
-[UIApplication _run]
UIKitCore
UIApplicationMain
OKEx
main main.m:33
0x0 + 0
It looks like the crash is caused by accessing self
inside a DispatchGroup.notify
closure after self
has already been deallocated. This happens because, by default, closures capture strong references to self
, which can lead to retain cycles or crashes if the object is released before the closure is executed.
ViewModel+Network.swift:47
var fetchDataPublisher = PassthroughSubject<Bool, Never>()
func fetchDetails() {
let group = DispatchGroup()
group.enter()
self.fetchName() { [weak self] names in
guard let self = self else { return }
group.leave()
}
group.notify(queue: .main) {
fetchDataPublisher.send() // CRASHED HERE
}
}
The solution is to use [weak self]
in the closure, as you correctly noted, to avoid retaining self
and causing a crash if it is deallocated before the tasks in the DispatchGroup
complete.
Here’s a concise summary of the steps involved in fixing the issue:
- Problem:
self
is being captured in agroup.notify
closure, and ifself
is deallocated before the tasks in theDispatchGroup
complete, the app will crash. - Solution: Use
[weak self]
in thegroup.notify
closure to avoid retainingself
. This ensures that the closure won’t access a deallocated object, thus preventing a crash.
Corrected Code:
func fetchDetails() {
let group = DispatchGroup()
group.enter()
self.fetchName() { [weak self] names in
guard let self = self else { return }
group.leave()
}
group.notify(queue: .main) { [weak self] in
guard let self = self else {
print("Object was deallocated before tasks completed")
return
}
self.fetchDataPublisher.send() // Ensure self exists
}
}
Key Takeaways:
- Weak self in closures: Ensures that
self
is not retained unnecessarily, preventing retain cycles and crashes whenself
is deallocated before the closure is executed. - Tasks continue executing even after
self
is deallocated: The tasks within theDispatchGroup
will complete regardless of whether the object that initiated the group (like a view controller) has been deallocated. - Memory safety: By using
[weak self]
, we ensure that the closure will not attempt to accessself
after it has been deallocated, thus preventing crashes.
This pattern is crucial when dealing with asynchronous tasks and dispatch groups, especially in cases where object deallocation can happen while tasks are still in progress.
Leave a Reply