Crashing in observeValueForKeyPath which attached to NSOperationQueue operationsCount

ios, key-value-observing, multithreading, nsoperationqueue, objective-c

Solution

When updating your UI, make sure that you're in the main thread. A possible solution follows:

dispatch_async(dispatch_get_main_queue(), ^{
    if (testnew > testold) {
        [[self logView] setText: [NSString stringWithFormat: @"Uploading %d files", testnew]];
        objc_setAssociatedObject([self logView], @"max_value_of_uploads", [change objectForKey: @"new"], OBJC_ASSOCIATION_COPY);
    } else {
        NSInteger value = [objc_getAssociatedObject([self logView], @"max_value_of_uploads") integerValue]; 
        [[self logView] setText: [NSString stringWithFormat: @"Uploaded %d of %d files", testnew, value]];
    }
}); 

Note that using `dispatch_async` or `dispatch_sync` depends on your desired implementation, but in the latter case, you risk deadlocking unless you check what thread you're in.

See also: NSOperation, observer and thread error where someone suggests moving your UI code to a separate method:

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
  [self performSelectorOnMainThread:@selector(dataLoadingFinished:) withObject:nil waitUntilDone:YES];
}

.. which is a fine alternate route.

Problem

I have `UITextView logView` (tried both `atomic` and `nonatomic`) and `NSOperationQueue uploadQueue` properties in my UIView subclass. I've added KVO on my `NSOperationQueue` property's `operationsCount` like this: ``` [[self uploadQueue] addObserver:self forKeyPath:@"operationCount" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context:NULL]; ``` Observer function looks like this: ``` - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if ([object isKindOfClass: [NSOperationQueue class]]) { NSLog(@"Keypath is: %@ change dictionary is: %@", keyPath, change); NSInteger testnew = [[change objectForKey: @"new"] integerValue]; NSInteger testold = [[change objectForKey: @"old"] integerValue]; if (testnew > testold) { [[self logView] setText: [NSString stringWithFormat: @"Uploading %d files", testnew]]; objc_setAssociatedObject([self logView], @"max_value_of_uploads", [change objectForKey: @"new"], OBJC_ASSOCIATION_COPY); } else { NSInteger value = [objc_getAssociatedObject([self logView], @"max_value_of_uploads") integerValue]; [[self logView] setText: [NSString stringWithFormat: @"Uploaded %d of %d files", testnew, value]]; } } } ``` `uploadQueue` filling works like that: ``` ... NSDictionary *iter; NSEnumerator *enumerator = [objects objectEnumerator]; while (iter = [enumerator nextObject]) { Uploader *op = [[Uploader alloc] initWithFileName: [iter objectForKey: @"fileName"] FileID: [[iter objectForKey: @"fileID"] integerValue] AndSessionID: [self sess]]; //Uploader *op = [[Uploader alloc] init]; [[self uploadQueue] addOperation: op]; ... ``` Without `if-else` block, everything works just fine: I've got NSLog messages about number of operations in queue, numbers are ok and so on. But with that `if-else` block, I get crash, which looks like this: ``` bool _WebTryThreadLock(bool), 0x10617fb0: Tried to obtain the web lock from a thread other than the main thread or the web thread. This may be a result of calling to UIKit from a secondary thread. Crashing now... 1 WebThreadLock 2 -[UITextView setText:] 3 -[SincViewController observeValueForKeyPath:ofObject:change:context:] 4 NSKeyValueNotifyObserver 5 NSKeyValueDidChange 6 -[NSObject(NSKeyValueObservingPrivate) _changeValueForKey:key:key:usingBlock:] 7 -[NSObject(NSKeyValueObservingPrivate) _changeValueForKey:usingBlock:] 8 ____NSOQDelayedFinishOperations_block_invoke_0 9 -[NSObject(NSKeyValueObservingPrivate) _changeValueForKey:key:key:usingBlock:] 10 -[NSObject(NSKeyValueObservingPrivate) _changeValueForKey:usingBlock:] 11 __NSOQDelayedFinishOperations 12 _dispatch_after_timer_callback 13 _dispatch_source_invoke 14 _dispatch_queue_invoke 15 _dispatch_worker_thread2 16 _pthread_wqthread 17 start_wqthread ``` It crashes in `else` block. Moreover, I don't see any changes to my logView. Thanks for future help and responses. UPDATE: `performSelectorOnMainThread` when setting text saved me.

Original source

Related problems