I have a publisher, pipelinePublisher, which runs a combine pipeline of various operations, some of which send a state update to a statePublisher passed in as an argument. pipelinePublisher gets removed on completion of its Combine pipeline:
func myFunction(_ request: MyRequest) -> PassthroughSubject<State, Never> {
let statePublisher = PassthroughSubject<State, Never>()
let presentationSubject = CurrentValueSubject<MyRequest, Error>(request)
var pipelinePublisher: AnyCancellable!
pipelinePublisher = presentationSubject
.eraseToAnyPublisher()
.checkSomething(returningStateTo: statePublisher)
// a few more operators here...
.sink(
receiveCompletion: { [weak self] _ in
self?.cancellables.remove(pipelinePublisher) // Crash happens here
},
receiveValue: { _ in }
)
pipelinePublisher.store(in: &cancellables)
return statePublisher
.receive(on: RunLoop.main)
.eraseToAnyPublisher()
}
However, very occasionally when I call the function multiple times in very quick succession, the app crashes on the line self?.cancellables.remove(pipelinePublisher).
This usually brings up one of two possible stack traces. This first is this:
2022-12-21 15:24:51.926131+0000 MyApp[23082:12933690] -[_NSCoreDataTaggedObjectID member:]: unrecognized selector sent to instance 0x8000000000000000
2022-12-21 15:24:51.931941+0000 MyApp[23082:12933690] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[_NSCoreDataTaggedObjectID member:]: unrecognized selector sent to instance 0x8000000000000000'
*** First throw call stack:
(
0 CoreFoundation 0x000000018040e7c8 __exceptionPreprocess + 172
1 libobjc.A.dylib 0x0000000180051144 objc_exception_throw + 56
2 CoreFoundation 0x000000018041d47c +[NSObject(NSObject) instanceMethodSignatureForSelector:] + 0
3 CoreFoundation 0x00000001804126c8 ___forwarding___ + 1308
4 CoreFoundation 0x0000000180414b4c _CF_forwarding_prep_0 + 92
5 libswiftCore.dylib 0x000000018be6ee68 $sSh8_VariantV6removeyxSgxF + 160
6 MyApp 0x00000001026c6080 $s12MyApp0A0C17myFunctiony7Combine12AnyPublisherVyAA12StateOs5NeverOGAA19MyRequestVFyAE11SubscribersO10CompletionOy_s5Error_pGcfU_ + 440
7 Combine 0x000000019baa2a70 $s7Combine11SubscribersO4SinkC7receive10completionyAC10CompletionOy_q_G_tF + 364
8 Combine 0x000000019baa2f28 $s7Combine11SubscribersO4SinkCy_xq_GAA10SubscriberA2aGP7receive10completionyAC10CompletionOy_7FailureQzG_tFTW + 20
9 Combine 0x000000019bb541cc $s7Combine10PublishersO7FlatMapV5Outer33_E91C3F00A6DFAAFEA2009FAF507AE039LLC7receive10completionyAA11SubscribersO10CompletionOy_7FailureQzG_tF + 1516
10 Combine 0x000000019bb55328 $s7Combine10PublishersO7FlatMapV5Outer33_E91C3F00A6DFAAFEA2009FAF507AE039LLCy_xq__qd__GAA10SubscriberA2aJP7receive10completionyAA11SubscribersO10CompletionOy_7FailureQzG_tFTW + 20
11 Combine 0x000000019bb53474 $s7Combine10PublishersO7FlatMapV5Outer33_E91C3F00A6DFAAFEA2009FAF507AE039LLC12receiveInner10completion_yAA11SubscribersO10CompletionOy_7FailureQzG_SitF + 1668
12 Combine 0x000000019bb52de4 $s7Combine10PublishersO7FlatMapV5Outer33_E91C3F00A6DFAAFEA2009FAF507AE039LLC4SideV7receive10completionyAA11SubscribersO10CompletionOy_7FailureQzG_tF + 20
13 Combine 0x000000019bac45ec $s7Combine6FutureC7Conduit33_3AE68DE9BADC00342FC052FEBC7D3BA6LLC7fulfillyys6ResultOyxq_GF + 1056
14 Combine 0x000000019bac4960 $s7Combine6FutureC7Conduit33_3AE68DE9BADC00342FC052FEBC7D3BA6LLC6finish10completionyAA11SubscribersO10CompletionOy_q_G_tF + 336
15 Combine 0x000000019bac2de4 $s7Combine6FutureC7promise33_3AE68DE9BADC00342FC052FEBC7D3BA6LLyys6ResultOyxq_GFyAA11ConduitBaseCyxq_GXEfU0_ + 156
16 Combine 0x000000019bac6b28 $s7Combine6FutureC7promise33_3AE68DE9BADC00342FC052FEBC7D3BA6LLyys6ResultOyxq_GFyAA11ConduitBaseCyxq_GXEfU0_TA + 16
17 Combine 0x000000019bae5140 $s7Combine11ConduitListO7forEachyyyAA0B4BaseCyxq_GKXEKF + 212
18 Combine 0x000000019bac2bfc $s7Combine6FutureC7promise33_3AE68DE9BADC00342FC052FEBC7D3BA6LLyys6ResultOyxq_GF + 716
19 Combine 0x000000019bac6b08 $s7Combine6FutureCyACyxq_Gyys6ResultOyxq_GcccfcyAGcfU_TA + 20
20 MyApp 0x0000000102541dd8 $s7Combine6FutureC12MyApps5Error_pRs_rlE9operationACyxsAE_pGxyYaKc_tcfcyys6ResultOyxsAE_pGccfU_yyYaYbcfU_TY2_ + 212
21 MyApp 0x0000000102542705 $s7Combine6FutureC12MyApps5Error_pRs_rlE9operationACyxsAE_pGxyYaKc_tcfcyys6ResultOyxsAE_pGccfU_yyYaYbcfU_TATQ0_ + 1
22 MyApp 0x000000010242f1a1 $sxIeghHr_xs5Error_pIegHrzo_s8SendableRzs5NeverORs_r0_lTRTQ0_ + 1
23 MyApp 0x000000010242f749 $sxIeghHr_xs5Error_pIegHrzo_s8SendableRzs5NeverORs_r0_lTRTA.24TQ0_ + 1
24 libswift_Concurrency.dylib 0x00000001b03bedcd _ZL23completeTaskWithClosurePN5swift12AsyncContextEPNS_10SwiftErrorE + 1
)
libc++abi: terminating with uncaught exception of type NSException
The second is in the same place but with the error: *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFNumber member:]: unrecognized selector sent to instance 0x8000000000000000'
What is causing this? I've tried making pipelinePublisher optional and having a check that it exists before it gets removed but it does actually exist and still crashes. I can't figure this out, please help!
EDIT
I am calling myFunction(_:) by using the method foo which gets a publisher.
static func publisher(forParam: String) -> AnyPublisher<State, Never> {
return Future {
// Do some stuff here
return objects
}
.flatMap { objects in
let request = MyRequest(objects)
return shared.myFunction(request)
}
.eraseToAnyPublisher()
}
static func foo(
param: String,
handler: ((State) -> Void)? = nil
) {
var cancellable: AnyCancellable!
cancellable = publisher(forParam: param)
.sink(
receiveCompletion: { _ in
self.shared.fooItems.cancellables.remove(cancellable) // sometimes crashes here too with the exact same crash!
}, receiveValue: { state in
handler?(state)
}
)
cancellable.store(in: &shared.fooItems.cancellables)
}
A few things to note:
Foo has to use completion block as part of an API.
publisher(forParam:) and therefore myFunction(_:) are only called from foo. However foo is called from many places.
myFunction’s pipelinePublisher cancels by an error that’s thrown at the same time as sending a state and a completion through to foo.
state can be sent to foo without a follow up completion too.
As you can see above, foo also sometimes gets the same error when removing the cancellable.
With help from people in the comments of my question, it looks like I had a race condition possibly caused by threads interacting with each other. I was removing from a Set, but Sets aren't thread safe. I solved this by changing the code to use Subscribers.Sink:
func myFunction(_ request: MyRequest) -> PassthroughSubject<State, Never> {
let statePublisher = PassthroughSubject<State, Never>()
let presentationSubject = CurrentValueSubject<MyRequest, Error>(request)
var pipelinePublisher: AnyCancellable!
pipelinePublisher = presentationSubject
.eraseToAnyPublisher()
.checkSomething(returningStateTo: statePublisher)
// a few more operators here...
.subscribe(Subscribers.Sink(
receiveCompletion: { _ in },
receiveValue: { _ in }
))
pipelinePublisher.store(in: &cancellables)
return statePublisher
.receive(on: RunLoop.main)
.eraseToAnyPublisher()
}
This fixed the problem. Combine handles the clean up of the subscription and the subscriber when the pipeline returns.
This was taken from the answer given here: With Combine, how to deallocate the Subscription after a network request
Related
According to Apple, receive(on:options:) runs callbacks on a given queue. We use a serial dispatch queue to prevent racing on localOptionalCancellable in the code below. But receiveCancel is not getting dispatched to that queue. Can someone tell me why?
From the documentation,
You use the receive(on:options:) operator to receive results and completion on a specific scheduler, such as performing UI work on the main run loop.
...
Prefer receive(on:options:) over explicit use of dispatch queues when performing work in subscribers. For example, instead of the following pattern:
Issue Reproduction:
import Foundation
import Combine
class Example {
private var localOptionalCancellable: AnyCancellable?
private let dispatchQueue = DispatchQueue(label: "LocalQueue-\(UUID())")
func misbehavingFunction() {
self.dispatchQueue.async {
self.localOptionalCancellable = Just(())
.setFailureType(to: Error.self)
.receive(on: self.dispatchQueue)
.handleEvents(
receiveCancel: {
// Simultaneous accesses to 0x600000364e10, but modification requires exclusive access.
// Can be fixed by wrapping in self.dispatchQueue.async {}
self.localOptionalCancellable = nil
}
)
.sink(
receiveCompletion: { _ in },
receiveValue: { _ in
self.localOptionalCancellable = nil
}
)
}
}
}
Example().misbehavingFunction()
Stack Trace:
Simultaneous accesses to 0x600000364e10, but modification requires exclusive access.
Previous access (a modification) started at (0x10eeaf12a).
Current access (a modification) started at:
0 libswiftCore.dylib 0x00007fff2ff7be50 swift_beginAccess + 568
3 Combine 0x00007fff4ba73a40 Publishers.HandleEvents.Inner.cancel() + 71
4 Combine 0x00007fff4ba74230 protocol witness for Cancellable.cancel() in conformance Publishers.HandleEvents<A>.Inner<A1> + 16
5 Combine 0x00007fff4b9f10c0 Subscribers.Sink.cancel() + 652
6 Combine 0x00007fff4b9f1500 protocol witness for Cancellable.cancel() in conformance Subscribers.Sink<A, B> + 16
7 Combine 0x00007fff4b9dd2d0 AnyCancellable.cancel() + 339
8 Combine 0x00007fff4b9dd5f0 AnyCancellable.__deallocating_deinit + 9
9 libswiftCore.dylib 0x00007fff2ff7da20 _swift_release_dealloc + 16
13 Combine 0x00007fff4b9f0da0 Subscribers.Sink.receive(_:) + 54
14 Combine 0x00007fff4b9f14c0 protocol witness for Subscriber.receive(_:) in conformance Subscribers.Sink<A, B> + 16
15 Combine 0x00007fff4ba73ed0 Publishers.HandleEvents.Inner.receive(_:) + 129
16 Combine 0x00007fff4ba74170 protocol witness for Subscriber.receive(_:) in conformance Publishers.HandleEvents<A>.Inner<A1> + 16
17 Combine 0x00007fff4ba26440 closure #1 in Publishers.ReceiveOn.Inner.receive(_:) + 167
18 libswiftDispatch.dylib 0x000000010e97cad0 thunk for #escaping #callee_guaranteed () -> () + 14
19 libdispatch.dylib 0x00007fff20105323 _dispatch_call_block_and_release + 12
20 libdispatch.dylib 0x00007fff20106500 _dispatch_client_callout + 8
21 libdispatch.dylib 0x00007fff2010c12e _dispatch_lane_serial_drain + 715
22 libdispatch.dylib 0x00007fff2010cde1 _dispatch_lane_invoke + 403
23 libdispatch.dylib 0x00007fff20117269 _dispatch_workloop_worker_thread + 782
24 libsystem_pthread.dylib 0x00007fff6116391b _pthread_wqthread + 290
25 libsystem_pthread.dylib 0x00007fff61162b68 start_wqthread + 15
Fatal access conflict detected.
According to Apple, receive(on:options:) runs callbacks on a given queue.
Not exactly. Here's what the documentation actually says:
You use the receive(on:options:) operator to receive results and completion on a specific scheduler, such as performing UI work on the main run loop. In contrast with subscribe(on:options:), which affects upstream messages, receive(on:options:) changes the execution context of downstream messages.
(Emphasis added.) So receive(on:) controls the Scheduler used to call a Subscriber's receive(_:) and receive(completion:) methods. It does not control the Scheduler used to call the Subscription's request(_:) or cancel() methods.
To control the Scheduler used to call the Subscription's cancel() method, you need to use the subscribe(on:options:) operator downstream of the handleEvents operator, like this:
self.localOptionalCancellable = Just(())
.setFailureType(to: Error.self)
.receive(on: self.dispatchQueue)
.handleEvents(
receiveCancel: {
// Simultaneous accesses to 0x600000364e10, but modification requires exclusive access.
// Can be fixed by wrapping in self.dispatchQueue.async {}
self.localOptionalCancellable = nil
}
)
.subscribe(on: self.dispatchQueue)
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.sink(
receiveCompletion: { _ in },
receiveValue: { _ in
self.localOptionalCancellable = nil
}
)
We are experiencing an exception in our project:
2019-08-08 10:18:28.703708-0600 AppName[99385:5069475] *** Terminating app due to uncaught exception 'com.apple.coreaudio.avfaudio', reason: 'required condition is false: !destNodeMixerConns.empty() && !isDestNodeConnectedToIONode'
*** First throw call stack:
(
0 CoreFoundation 0x000000010ee2e8db __exceptionPreprocess + 331
1 libobjc.A.dylib 0x000000010e21bac5 objc_exception_throw + 48
2 CoreFoundation 0x000000010ee2e662 +[NSException raise:format:arguments:] + 98
3 AVFAudio 0x0000000111b94dbc _Z19AVAE_RaiseExceptionP8NSStringz + 156
4 AVFAudio 0x0000000111bf3937 _Z11_AVAE_CheckPKciS0_S0_b + 295
5 AVFAudio 0x0000000111b8cb8f _ZN18AVAudioEngineGraph8_ConnectEP19AVAudioNodeImplBaseS1_jjP13AVAudioFormat + 1031
6 AVFAudio 0x0000000111bfb598 _ZN17AVAudioEngineImpl7ConnectEP11AVAudioNodeS1_mmP13AVAudioFormat + 194
7 AVFAudio 0x0000000111bfb5ff -[AVAudioEngine connect:to:format:] + 83
8 AppName 0x000000010a424c10 $s8AudioKitAAC6outputAA6AKNodeCSgvWZTf4dd_n + 2576
9 AppName 0x000000010a4230fd $s8AudioKitAAC6outputAA6AKNodeCSgvsZ + 93
10 AppName 0x000000010a2ba3a3 $s6AppName7MaestroC17setUpTrackPlayers7fileURLy10Foundation0H0V_tF + 1235
Examining the common gotchas video I see a similar exception being thrown, required condition is false: !nodeMixerConns.empty() && !hasDirectConnToIONode, which is caused by allowing the variables to go out of scope rather than be retained in the class.
So this occurs when we create an AKPlayer, which is retained in a class, then we create an AKTimePitch with this player which is also retained in that class, and finally assign that to AudioKit.output which triggers the exception. After that we were storing the class that holds onto the player and time pitch in an array, so I tried to move this up after it's created in hopes that was the issue, but I see the same exception.
Interestingly, this code works fine initially when we load up the first song but crashes when we hit the next button to load up the next song.
final class Maestro : NSObject {
static let shared = Maestro()
var audioPlayers = [TrackPlayer]()
func setUpTrackPlayers(fileURL: URL) {
let playerOne = TrackPlayer(url: fileURL)
audioPlayers.append(playerOne)
AudioKit.output = playerOne.handleMixerChain() //boom
do {
try AudioKit.start()
} catch {
print("Maestro AudioKit.start error: \(error)")
}
}
func next() {
for player in audioPlayers {
player.stop()
}
audioPlayers.removeAll()
setUpTrackPlayers(fileURL: newSong.getFileUrl())
}
}
final class TrackPlayer {
let player : AKPlayer
lazy var timePitch = AKTimePitch()
init(url: URL) {
player = AKPlayer(url: url)!
}
func handleMixerChain(pitch: Double = 0.0, tempo: Double = 1.0) -> AKTimePitch {
timePitch = AKTimePitch(player)
timePitch.pitch = pitch
timePitch.rate = tempo
return timePitch
}
}
Any ideas? If you need any more info let me know. May be good to note we are updating from AudioKit 4.5.5 where we didn't experience this crash.
I've opened the project and it seems like you're being a bit too relaxed about your set up / tear down of the signal chain. You're setting up AudioKit's output using local variables, never tearing down this signal chain, and then coming back resetting the AudioKit output and telling AudioKit to start without ever calling AudioKit.stop().
Here are three functions from my code:
func workOutResults() {
var fixtures = [Fixture]()
let fixtureFetch: NSFetchRequest<Fixture> = Fixture.fetchRequest()
do {
fixtures = try coreDataStack.managedContext.fetch(fixtureFetch)
} catch let error as NSError {
print("Could not fetch. \(error), \(error.userInfo)")
}
for fixture in fixtures {
// do stuff here
}
}
func processUpdates() {
// relevant code snippet below
let theTable = loadLeagueTableFor(leagueName: "Championship", cds: cds)
}
func loadLeagueTableFor(leagueName: String, cds: CoreDataStack) -> [Club] {
var leagueArray = [Club]()
// Set up the sort descriptors
let pointsSortDescriptor: NSSortDescriptor = {
let compareSelector = #selector(NSString.localizedStandardCompare(_:))
return NSSortDescriptor(key: #keyPath(Club.leaguePoints),
ascending: false,
selector: compareSelector)
}()
let goalDiffSortDescriptor: NSSortDescriptor = {
let compareSelector = #selector(NSString.localizedStandardCompare(_:))
return NSSortDescriptor(key: #keyPath(Club.leagueGoalDiff),
ascending: false,
selector: compareSelector)
}()
let goalsForSortDescriptor: NSSortDescriptor = {
let compareSelector = #selector(NSString.localizedStandardCompare(_:))
return NSSortDescriptor(key: #keyPath(Club.leagueGoalsFor),
ascending: false,
selector: compareSelector)
}()
let clubNameSortDescriptor: NSSortDescriptor = {
let compareSelector = #selector(NSString.localizedStandardCompare(_:))
return NSSortDescriptor(key: #keyPath(Club.name),
ascending: true,
selector: compareSelector)
}()
// Do the Fetch request of Clubs, placing them in order into leagueArray
let clubFetch: NSFetchRequest<Club> = Club.fetchRequest()
clubFetch.predicate = NSPredicate(format: "%K == %#", #keyPath(Club.league.nameID), leagueName)
clubFetch.sortDescriptors = [pointsSortDescriptor, goalDiffSortDescriptor, goalsForSortDescriptor, clubNameSortDescriptor]
do {
leagueArray = try cds.managedContext.fetch(clubFetch)
} catch let error as NSError {
print("Could not fetch. \(error), \(error.userInfo)")
}
cds.saveContext()
return leagueArray
}
If I call...
workOutResults()
coreDataStack.saveContext()
processUpdates()
... everything works fine. Yet if I call...
workOutResults()
processUpdates()
... I get the following error in loadLeagueTableFor():
2018-01-17 19:37:38.228954+0000 Tycoon[1331:32725] -[__NSCFNumber localizedStandardCompare:]: unrecognized selector sent to instance 0xb000000000000022
2018-01-17 19:37:38.278901+0000 Tycoon[1331:32725] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFNumber localizedStandardCompare:]: unrecognized selector sent to instance 0xb000000000000022'
*** First throw call stack:
(
0 CoreFoundation 0x0000000104da212b __exceptionPreprocess + 171
1 libobjc.A.dylib 0x0000000103d6ef41 objc_exception_throw + 48
2 CoreFoundation 0x0000000104e23024 -[NSObject(NSObject) doesNotRecognizeSelector:] + 132
3 CoreFoundation 0x0000000104d24f78 ___forwarding___ + 1432
4 CoreFoundation 0x0000000104d24958 _CF_forwarding_prep_0 + 120
5 Foundation 0x00000001037aaad2 _NSCompareObject + 46
6 Foundation 0x0000000103806097 _NSSortFunctionMany + 674
7 CoreFoundation 0x0000000104d1b3bc __CFSimpleMergeSort + 124
8 CoreFoundation 0x0000000104d1b41c __CFSimpleMergeSort + 220
9 CoreFoundation 0x0000000104d1b41c __CFSimpleMergeSort + 220
10 CoreFoundation 0x0000000104d1b41c __CFSimpleMergeSort + 220
11 CoreFoundation 0x0000000104d1b2fb CFSortIndexes + 827
12 CoreFoundation 0x0000000104d51726 CFMergeSortArray + 454
13 Foundation 0x00000001037aa81e _sortedObjectsUsingDescriptors + 596
14 Foundation 0x00000001037aa570 -[NSArray(NSKeyValueSorting) sortedArrayUsingDescriptors:] + 531
15 CoreData 0x000000010477909e -[NSManagedObjectContext executeFetchRequest:error:] + 4590
16 libswiftCoreData.dylib 0x00000001045e8f1a )
libc++abi.dylib: terminating with uncaught exception of type NSException
So saving the context in between the two fetches avoids the crash, but poses a problem as it means I have to save in a place I would rather not. Any idea why I am getting the error?
It's unclear why this would ever work, because you're comparing numeric values with compareSelector, which you define to be a method on NSString. The error message describes this exactly-- you're trying to compare two numbers using a method that doesn't exist for numbers.
When using sort descriptors, the selector and comparator versions of the constructors are only necessary if you don't want the common sorting implied by the value of ascending. In the case of numeric values, if you simply want them sorted by value, you don't need either. You can simply use something like
NSSortDescriptor(key: #keyPath(Club.leaguePoints),
ascending: false)
I am using Alamofire and after several hours of my app running on the simulator I got a crash with this error.
*** Terminating app due to uncaught exception 'NSGenericException', reason: 'Start date cannot be later in time than end date!'
I got this stack trace in console:
*** First throw call stack:
(
0 CoreFoundation 0x0000000111186d4b __exceptionPreprocess + 171
1 libobjc.A.dylib 0x0000000110be821e objc_exception_throw + 48
2 Foundation 0x00000001107f0e3c -[_NSConcreteDateInterval dealloc] + 0
3 CFNetwork 0x00000001131a18e8 -[__NSCFURLSessionTaskMetrics _initWithTask:] + 868
4 CFNetwork 0x00000001131a1497 -[NSURLSessionTaskMetrics _initWithTask:] + 100
5 CFNetwork 0x0000000112f77bc7 -[__NSCFURLLocalSessionConnection _tick_finishing] + 351
6 libdispatch.dylib 0x00000001128e3978 _dispatch_call_block_and_release + 12
7 libdispatch.dylib 0x000000011290d0cd _dispatch_client_callout + 8
8 libdispatch.dylib 0x00000001128eae17 _dispatch_queue_serial_drain + 236
9 libdispatch.dylib 0x00000001128ebb4b _dispatch_queue_invoke + 1073
10 libdispatch.dylib 0x00000001128ee385 _dispatch_root_queue_drain + 720
11 libdispatch.dylib 0x00000001128ee059 _dispatch_worker_thread3 + 123
12 libsystem_pthread.dylib 0x0000000112cbc736 _pthread_wqthread + 1299
13 libsystem_pthread.dylib 0x0000000112cbc211 start_wqthread + 13
)
libc++abi.dylib: terminating with uncaught exception of type NSException
Did someone get the similar crash?
Thanks
I had the same crash and did some research today and found this:
http://www.openradar.me/28301343
It looks Apple fixed the issue in iOS 10.2. just thought it may help you!
Yes I just got the same exact crash. It happened in a background thread and it seems to have to do with making a URL session network request. I wonder if it's some sort of multithreading bug having to do with the fact that I'm making two network requests at the same time. I'm using Alamofire as well but not sure if the bug lies in Alamofire or in Apple's code. I've been unable to reproduce it as of now. Maybe you can figure out how to reproduce it and then file an issue in Apple's bug radar or in the Alamofire GitHub repo.
This is a bug in Apple's NSURLSessionTaskMetrics code and happens during a network request when the user's clock gets moved far enough backwards that the request start timestamp is after the request end timestamp. This is reproducible using a network debugging proxy and manually adjusting the clock, and only occurs from iOS 10.0 up to but not including iOS 10.2
If you're using Alamofire, and you don't need NSURLSessionTaskMetrics, you can work around this by using a custom SessionDelegate for your SessionManager and overriding the responds(to aSelector..) function e.g:
class MySessionDelegate: Alamofire.SessionDelegate {
override public func responds(to aSelector: Selector) -> Bool {
let result: Bool = super.responds(to: aSelector)
if #available(iOS 10.2, *) {
// NSURLSessionTaskMetrics date crash is fixed
return result
} else if #available(iOS 10.0, *) {
// NSURLSessionTaskMetrics date crash is not fixed, turn off metric collection
if aSelector == #selector(self.urlSession(_:task:didFinishCollecting:)) {
return false
} else {
return result
}
} else {
// NSURLSessionTaskMetrics doesn't exist
return result
}
}
}
If you're using the default SessionManager (e.g. calling Alamofire.request(...)) you can create your own SessionManager instead in order to use your custom SessionDelegate:
let sessionManager: Alamofire.SessionManager = {
let configuration: URLSessionConfiguration = URLSessionConfiguration.default
configuration.httpAdditionalHeaders = SessionManager.defaultHTTPHeaders
return Alamofire.SessionManager(configuration: configuration, delegate: MySessionDelegate(), serverTrustPolicyManager: nil)
}()
And now instead of calling Alamofire.request(...) you'd call sessionManager.request(...)
I have been struggling with this problem in an os x application for the last few months and have found a workaround.
Background:
Like the OP am using Alamofire to request JSON data via a Timer to send requests several times per second. The data comes in as expected however I get random crashes at irregular intervals with the same message as the OP i.e. Start date cannot be later in time than end date! etc etc.
Solution:
Rather than send Alamofire requests at a regular interval I added some logic which checks for a return of the previous request before sending the next one. This completely eliminated the random crashes.
Hope it helps :)
#thierryb
When I start my app, I perform an API call to see whether there's new data available. The data is stored in my local Realm database, and some of it is displayed in the initial table view controller.
Once the API call is finished, I check if some conditions are met that require me to delete a bunch of the previous data from the database and then create new objects. However, when I delete the old data, my app crashes with the following exception:
2015-08-06 11:56:32.057 MSUapp[19754:172864] *** Terminating app due to uncaught exception 'RLMException', reason: 'Object has been deleted or invalidated.'
*** First throw call stack:
(
0 CoreFoundation 0x000000010660cc65 __exceptionPreprocess + 165
1 libobjc.A.dylib 0x00000001083bdbb7 objc_exception_throw + 45
2 Realm 0x0000000105b78e95 _ZL17RLMVerifyAttachedP13RLMObjectBase + 85
3 Realm 0x0000000105b7878d _ZL10RLMGetLinkP13RLMObjectBasemP8NSString + 29
4 Realm 0x0000000105b7c23e ___ZL17RLMAccessorGetterP11RLMProperty15RLMAccessorCodeP8NSString_block_invoke_12 + 46
5 MSUapp 0x0000000105764867 _TFFC6MSUapp29FavoriteLeaguesViewController18generateLeagueListFS0_FT_T_U_FTCS_6LeagueS1__Sb + 39
6 MSUapp 0x00000001057648eb _TTRXFo_oC6MSUapp6LeagueoS0__dSb_XFo_iS0_iS0__dSb_ + 27
7 libswiftCore.dylib 0x0000000108674ae2 _TFSs14_insertionSortUSs21MutableCollectionType_USs13GeneratorType__Ss22BidirectionalIndexType_Ss18_SignedIntegerType_Ss33_BuiltinIntegerLiteralConvertible____FTRQ_GVSs5RangeQQ_5Index_RFTQQQ_9Generator7ElementS7__Sb_T_ + 1570
8 libswiftCore.dylib 0x0000000108676682 _TFSs14_introSortImplUSs21MutableCollectionType_USs13GeneratorType__Ss21RandomAccessIndexType_Ss18_SignedIntegerType_Ss33_BuiltinIntegerLiteralConvertible_Ss16SignedNumberType_S3_____FTRQ_GVSs5RangeQQ_5Index_RFTQQQ_9Generator7ElementS8__SbSi_T_ + 1250
9 libswiftCore.dylib 0x0000000108676172 _TFSs10_introSortUSs21MutableCollectionType_USs13GeneratorType__Ss21RandomAccessIndexType_Ss18_SignedIntegerType_Ss33_BuiltinIntegerLiteralConvertible_Ss16SignedNumberType_S3_____FTRQ_GVSs5RangeQQ_5Index_FTQQQ_9Generator7ElementS8__Sb_T_ + 1058
10 libswiftCore.dylib 0x00000001085ec947 _TFSs4sortUSs21MutableCollectionType_USs13GeneratorType__Ss21RandomAccessIndexType_Ss18_SignedIntegerType_Ss33_BuiltinIntegerLiteralConvertible_Ss16SignedNumberType_S3_____FTRQ_FTQQQ_9Generator7ElementS6__Sb_T_ + 471
11 libswiftCore.dylib 0x00000001086a8d9e _TPA__TFFSa4sortU__FRGSaQ__FFTQ_Q__SbT_U_FRGVSs26UnsafeMutableBufferPointerQ__T_ + 222
12 libswiftCore.dylib 0x00000001086a8e18 _TPA__TTRG0_R_XFo_lGVSs26UnsafeMutableBufferPointerq___dT__XFo_lGS_q___iT__42 + 56
13 libswiftCore.dylib 0x00000001085f7fda _TFSa30withUnsafeMutableBufferPointerU__fRGSaQ__U__FFRGVSs26UnsafeMutableBufferPointerQd___Q_Q_ + 522
14 libswiftCore.dylib 0x00000001085f7db4 _TFSa4sortU__fRGSaQ__FFTQ_Q__SbT_ + 132
15 MSUapp 0x0000000105761709 _TFC6MSUapp29FavoriteLeaguesViewController18generateLeagueListfS0_FT_T_ + 1097
16 MSUapp 0x000000010576354b _TFC6MSUapp29FavoriteLeaguesViewController27numberOfSectionsInTableViewfS0_FCSo11UITableViewSi + 59
17 MSUapp 0x00000001057635fa _TToFC6MSUapp29FavoriteLeaguesViewController27numberOfSectionsInTableViewfS0_FCSo11UITableViewSi + 58
18 UIKit 0x000000010737cac3 -[UITableViewRowData _updateNumSections] + 84
19 UIKit 0x000000010737d4b4 -[UITableViewRowData invalidateAllSections] + 69
20 UIKit 0x00000001071c873b -[UITableView _updateRowData] + 217
21 UIKit 0x00000001071de2b7 -[UITableView noteNumberOfRowsChanged] + 112
22 UIKit 0x00000001071dd9f5 -[UITableView reloadData] + 1355
23 MSUapp 0x00000001057647c6 _TFFC6MSUapp29FavoriteLeaguesViewController11viewDidLoadFS0_FT_T_U_FTO10RealmSwift12NotificationCS1_5Realm_T_ + 166
24 RealmSwift 0x0000000105f37210 _TFF10RealmSwift41rlmNotificationBlockFromNotificationBlockFFT12notificationOS_12Notification5realmCS_5Realm_T_bTSSCSo8RLMRealm_T_U_FTSSS2__T_ + 224
25 RealmSwift 0x0000000105f372af _TTRXFo_oSSoCSo8RLMRealm_dT__XFdCb_dCSo8NSStringdS__dT__ + 111
26 Realm 0x0000000105c0645a -[RLMRealm sendNotifications:] + 986
27 Realm 0x0000000105c068e6 -[RLMRealm commitWriteTransaction] + 262
28 Realm 0x0000000105c06a48 -[RLMRealm transactionWithBlock:] + 120
29 RealmSwift 0x0000000105f34250 _TFC10RealmSwift5Realm5writefS0_FFT_T_T_ + 176
30 MSUapp 0x00000001056d46db _TZFC6MSUapp14DatabaseHelper23removeForSportAndSeasonfMS0_FTCS_5Sport6seasonSS_T_ + 603
31 MSUapp 0x0000000105710d22 _TFFFC6MSUapp11AppDelegate14loadRemoteDataFS0_FT_T_U_FGSaCS_5Sport_T_U_FGSaCS_6League_T_ + 866
32 MSUapp 0x0000000105710dc7 _TTRXFo_oGSaC6MSUapp6League__dT__XFo_iGSaS0___iT__ + 23
33 MSUapp 0x00000001057103d1 _TPA__TTRXFo_oGSaC6MSUapp6League__dT__XFo_iGSaS0___iT__ + 81
34 MSUapp 0x000000010575de90 _TTRXFo_iGSaC6MSUapp6League__iT__XFo_oGSaS0___dT__ + 32
35 MSUapp 0x000000010575ddeb _TFZFC6MSUapp9APIHelper11loadLeaguesFMS0_FTSi18shouldWriteToRealmSb10completionGSqFGSaCS_6League_T___T_U_FCSo6NSDataT_ + 2763
36 MSUapp 0x00000001056f4a0e _TTSf2n_n_n_n_n_d_i_n_n_n___TFFC6MSUapp14JSONDataSource18loadRemoteJsonDataFS0_FTSSCS_19GETParameterBuilderFCSo6NSDataT__T_U_FTCSo12NSURLRequestGSqCSo17NSHTTPURLResponse_GSqS2__GSqCSo7NSError__T_ + 2302
37 MSUapp 0x00000001056f2d59 _TPA__TTSf2n_n_n_n_n_d_i_n_n_n___TFFC6MSUapp14JSONDataSource18loadRemoteJsonDataFS0_FTSSCS_19GETParameterBuilderFCSo6NSDataT__T_U_FTCSo12NSURLRequestGSqCSo17NSHTTPURLResponse_GSqS2__GSqCSo7NSError__T_ + 249
38 Alamofire 0x00000001059e7599 _TTRXFo_oCSo12NSURLRequestoGSqCSo17NSHTTPURLResponse_oGSqCSo6NSData_oGSqCSo7NSError__dT__XFo_oS_oGSqS0__iGSqS1__oGSqS2___dT__ + 25
39 Alamofire 0x00000001059e7461 _TFFFC9Alamofire7Request8responseFDS0_US_18ResponseSerializer___FT5queueGSqCSo8NSObject_18responseSerializerQ_17completionHandlerFTCSo12NSURLRequestGSqCSo17NSHTTPURLResponse_GSqQ0__GSqCSo7NSError__T__DS0_U_FT_T_U_FT_T_ + 737
40 Alamofire 0x00000001059e690e _TPA__TFFFC9Alamofire7Request8responseFDS0_US_18ResponseSerializer___FT5queueGSqCSo8NSObject_18responseSerializerQ_17completionHandlerFTCSo12NSURLRequestGSqCSo17NSHTTPURLResponse_GSqQ0__GSqCSo7NSError__T__DS0_U_FT_T_U_FT_T_ + 206
41 Alamofire 0x00000001059a89d7 _TTRXFo__dT__XFdCb__dT__ + 39
42 libdispatch.dylib 0x000000010938b186 _dispatch_call_block_and_release + 12
43 libdispatch.dylib 0x00000001093aa614 _dispatch_client_callout + 8
44 libdispatch.dylib 0x0000000109392a1c _dispatch_main_queue_callback_4CF + 1664
45 CoreFoundation 0x00000001065741f9 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 9
46 CoreFoundation 0x0000000106535dcb __CFRunLoopRun + 2043
47 CoreFoundation 0x0000000106535366 CFRunLoopRunSpecific + 470
48 GraphicsServices 0x000000010cc17a3e GSEventRunModal + 161
49 UIKit 0x00000001070f08c0 UIApplicationMain + 1282
50 MSUapp 0x000000010570f857 main + 135
51 libdyld.dylib 0x00000001093df145 start + 1
52 ??? 0x0000000000000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
This call stack lets me assume, that it's because of my write access in FavoriteLeaguesViewController's generateLeagueList method. The following is its body:
var favorites = FavoritesHelper.sharedInstance.favoriteLeagues
favorites.sort { $0.sport < $1.sport }
for favorite in favorites {
// Add to array, which we can later use for cellForRowAtIndexPath
}
favorites is of the type [League], where League is a Realm Object. I'd assume the exception occurs because I'm accessing properties of the League objects, which have been deleted from the Realm database in the mean time (because the API call that has been started in the AppDelegate is now finished).
My question then is: How can I prevent this from happening? How can I make sure that there's no more writing/reading-access to any of the League objects prior to deleting them?
You can check if an object has been deleted from the Realm by calling object.invalidated -- if it returns true, then it has been deleted or the Realm has manually invalidated.
I got a really nice way to catch a RLMException within Swift.
Currently Swift doesn't show where a RLMException happened.
In Realm/RLMUtil.mm:266, there is the definition for RLMException.
If you change the code to generate a swift error,
Xcode now can show you where the exception occurred.
Now it is a part of Swift.
// Realm/RLMUtil.mm:266
static NSException *RLMException(NSString *reason, NSDictionary *additionalUserInfo) {
// add some code to generate a swift error. E.g. division-by-zero.
int a = 0;
if (reason == nil) {
a = 1;
}
NSLog(#"calculating 1 / %d = %f", a, 1 / a);
... remainder of the original code...
}
I've just place breakpoint inside method:
// Realm/RLMUtil.mm:193
static NSException *RLMException(NSString *reason, NSDictionary *additionalUserInfo) {
// ...
}
And on left panel you can check stack trace, here you can find where error throws.
The issue was in my FavoritesHelper class. It had both a favoriteLeagueIDs and favoriteLeagues property. I always set both of them and used the IDs for internal usage and the other property for whenever I want some data from these leagues.
This meant, that all favorite leagues were constantly referenced by the favoriteLeagues property (of the type [League]), thus crashing the app when I wanted to fetch them after they're invalidated.
What I've done to fix this, was to change the property favoriteLeagues to a computed property as follows:
var favoriteLeagues: [League] {
get {
var leagues = [League]()
for id in favoriteLeagueIDs {
if let league = realm.objectForPrimaryKey(League.self, key: id) {
leagues.append(league)
}
}
return leagues
}
}
Now the leagues are no longer referenced and just get loaded from the database when I need to read them. Invalidated or deleted objects don't get loaded because of the if let statement (the Realm.objectForPrimaryKey(:key:) method returns nil in such a case).
I was unable to place breakpoints within the Realm framework itself as others suggested, but instead placed an exception breakpoint on my entire project:
This allowed me to catch a proper stack trace when the exception was thrown, and find my bug.
you can calling isInvalidated or invalidated to judge the object is invalid (YES) or not (NO).
you can also write a a custom method to define what is real invalidate
look for the document ,we will see a property:
/**
Indicates if the object can no longer be accessed because it is now invalid.
An object can no longer be accessed if the object has been deleted from the Realm that manages it, or
if `invalidate` is called on that Realm.
*/
#property (nonatomic, readonly, getter = isInvalidated) BOOL invalidated;
the invalidated is readonly but what does isInvalidated mean?
it is equals to - (BOOL)invalidated { return invalidated; }
it means that you can write a a custom method to define what is real invalidate you want.
Try to create the element on realm instead of add
So:
try! realm.write {
realm.add(...)
}
Replace with:
try! realm.write {
realm.create(...)
}
And then after the delete operation realm should work as expected!
In my experience, if you trying to use target object (which you wanna delete) after delete, application will crash.
If you wanna trigger some code blocks after removing realm object, just trying to trigger that block right before the object removing in the memory. Trying to usage that object after removed from memory, will make some problem and will crash the app.
For example:
try! realm.write {
print("deleted word: \(targetObject.word)")
realm.delete(targetObject)
// targetObject was removed, so don't try to access it otherwise you gonna got the 'nil' value instead of object.
}
after spent a day, i figure out with the remove DispatchQueue.main.async in my realm.delete() function and finally it worked.
DispatchQueue.main.async {
realm.delete()
}
to
realm.delete()
In my case I was deleting data from 2 tables at once.. one with a foreign to the other.
let itemToDelete = counters[indexPath.row]
let realm = try! Realm()
try! realm.write {
realm.delete(itemToDelete)
let predicate = NSPredicate(format: "counterid = \(c.id)")
let children = realm.objects(Details.self).filter(predicate)
realm.delete(children)
}
But the problem was that I was trying to delete the children of the item that does not exist anymore. Switching the order, solved it!
let itemToDelete = counters[indexPath.row]
let realm = try! Realm()
try! realm.write {
let predicate = NSPredicate(format: "counterid = \(c.id)")
let children = realm.objects(Details.self).filter(predicate)
realm.delete(children)
realm.delete(itemToDelete) //this should be deleted after
}
Hope this helps someone else!
Here are some of the reasons based on my past experiences.
Overriding NSObject's isEqual(_ object: Any?), then returning identifier which was just deleted by Realm.
How did I fix it?
Check if object is invalidated if so return false otherwise proceed with using your identifier
Example:
extension YourModel {
override func isEqual(_ object: Any?) -> Bool {
guard let object = object as? YourModel,
!self.isInvalidated else {
return false
}
return object.id == self.id
}
}
Using Diffing tools
For those using diffing tools like RxDataSources, IGListKit etc.. you have to perform the same check as mentioned above before trying to access the indentifier.
Here is a work round if you are using RxDataSources that prevent the crash caused by adopting to IdentifiableType protocol.
Check if the Realm object was invalidated. If so, just return a random unique value, like so:
var identity: String {
return isInvalidated ? "deleted-object-\(UUID().uuidString)" : objectId
}
Source: - https://github.com/RxSwiftCommunity/RxDataSources/issues/172#issuecomment-427675816
What you can do is observe the Results<> object returned from your initial Realm query (the one used to populate your list/table view) and update that list in the observer callback method whenever there is a change in the database.
Just make sure that you use the same Realm instance to delete/modify the object as the one used for the initial query.
EDIT:
Some code examples:
let realm = Realm()
let results = realm.objects(Favourite.self)
let notificationToken = results.observe { [weak self] (changes) in
guard let tableView = self?.tableView else { return }
tableView.reloadData()
}
// Somewhere else
try? realm.write {
if let favourite = results.first {
realm.delete(favourite)
}
}
// OR
if let objectRealm = results.first?.realm {
try? objectRealm.write {
objectRealm.delete(results.first!)
}
}
// Don't forget to stop observing in deinit{}
// notificationToken.invalidate()
You can also use the #ObservedRealmObject or #ObservedResults property wrapper, you implicitly open a realm and retrieve the specified objects or results. You can then pass those objects to a view further down the hierarchy.
And use the .onDelete(perform: ) method. In example: .onDelete(perform: $dogs.remove) see below:
(SwiftUI)
struct DogsView: View {
#ObservedResults(Dog.self) var dogs
/// The button to be displayed on the top left.
var leadingBarButton: AnyView?
var body: some View {
NavigationView {
VStack {
// The list shows the dogs in the realm.
// The ``#ObservedResults`` above implicitly opens a realm and retrieves
// all the Dog objects. We can then pass those objects to views further down the
// hierarchy.
List {
ForEach(dogs) { dog in
DogRow(dog: dog)
}.onDelete(perform: $dogs.remove) **//<- Deletion of the Object**
}.listStyle(GroupedListStyle())
.navigationBarTitle("Dogs", displayMode: .large)
.navigationBarBackButtonHidden(true)
.navigationBarItems(
leading: self.leadingBarButton,
// Edit button on the right to enable rearranging items
trailing: EditButton())
}.padding()
}
}
}
Souce: MongoDB - CRUD Swift SDK