Question

I need to detect the tableview contentOffset changes so I use the KVO, and everything works perfectly. In the same view controller I've an UIActivityViewController services too. If I use a service from the uiactivityviewcontroller and then, just after dismissed, i pop the view controller, I get the follow error...

* frame #0: 0x0000000183071280 CoreFoundation`___forwarding___ + 736
    frame #1: 0x0000000182f9108c CoreFoundation`__forwarding_prep_0___ + 92
    frame #2: 0x0000000183b2e7d0 Foundation`NSKeyValuePushPendingNotificationPerThread + 340
    frame #3: 0x0000000183b19068 Foundation`NSKeyValueWillChange + 532
    frame #4: 0x0000000183b023a8 Foundation`-[NSObject(NSKeyValueObserverNotification) willChangeValueForKey:] + 236
    frame #5: 0x0000000183bc9148 Foundation`_NSSetPointValueAndNotify + 116
    frame #6: 0x00000001863126c0 UIKit`-[UIScrollView(UIScrollViewInternal) _adjustContentOffsetIfNecessary] + 864
    frame #7: 0x0000000186116a0c UIKit`-[UIScrollView(UIScrollViewInternal) _stopScrollingNotify:pin:tramplingDragFlags:] + 352
    frame #8: 0x0000000186116864 UIKit`-[UIScrollView removeFromSuperview] + 44
    frame #9: 0x000000018603377c UIKit`-[UIView dealloc] + 424
    frame #10: 0x0000000182f69ad4 CoreFoundation`CFRelease + 468
    frame #11: 0x0000000182f75ec0 CoreFoundation`-[__NSArrayM dealloc] + 156
    frame #12: 0x000000018f58d474 libobjc.A.dylib`(anonymous namespace)::AutoreleasePoolPage::pop(void*) + 524
    frame #13: 0x0000000182f6d438 CoreFoundation`_CFAutoreleasePoolPop + 28
    frame #14: 0x000000018302c974 CoreFoundation`__CFRunLoopRun + 1460
    frame #15: 0x0000000182f6d6d0 CoreFoundation`CFRunLoopRunSpecific + 452
    frame #16: 0x0000000188c51c0c GraphicsServices`GSEventRunModal + 168
    frame #17: 0x000000018609efdc UIKit`UIApplicationMain + 1156

To add or remove the kvo I use the following lines

- (void)viewWillAppear:(BOOL)animated
{
    [self.tableView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:NULL];    
}

- (void)viewWillDisappear:(BOOL)animated
{
    [self.tableView removeObserver:self forKeyPath:@"contentOffset"];
}
Was it helpful?

Solution

You should probably have a state property to keep track of whether you added your observer or not. It's possible that you might not see a viewWillDisappear when your activity view indicator controller is presented, even though you might see viewWillAppear be called again when you return. Thus, when you dismiss your view controller, you'd added the observer twice, but are only removing it once.

Put in some NSLog statements and I bet you'll find that your calls to viewWillDisappear and viewWillAppear are not balanced (through no fault of your own). As a result, I bet you're adding the observer more times than you're removing it.

By adding a state property, observingContentOffset, you can remedy this:

@property (nonatomic) BOOL observingContentOffset;

You can then have your code that adds and removes the observer check this property:

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];   // don't forget to call super

    if (!self.observingContentOffset) {
        [self.tableView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:NULL];
        self.observingContentOffset = YES;
    }
}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];   // don't forget to call super

    if (self.observingContentOffset) {
        [self.tableView removeObserver:self forKeyPath:@"contentOffset"];
        self.observingContentOffset = NO;
    }
}

While the above should remedy your issue, I would take this a step further and suggest you consider retiring this KVO code entirely. Since the tableview is actually a subclass of a scroll view, when you set the table view's delegate, you're also specifying the delegate for the underlying scrollview. Thus, having specified your table view's delegate, it will also serve as the scroll view delegate and you can simply implement the relevant UIScrollViewDelegate method(s). If you need to identify every scroll event, that's probably a more robust technique than KVO (and it coincidentally eliminates your original problem, too). And if you only need to identify when cells are removed from the view, iOS 6 provides some relevant high-level UITableViewDelegate methods that make it even easier.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top