Question

IOS 6.1

We have noticed that when we get an exception for removeObserver for a Key Value Pair that is not there, the class that has the KVP get's and extra retain count from the removeObserver Call.

Following is some test code that proves this. Also in there is a bridging release that fixes this.

Any comments welcome....

#import "ViewController.h"
#import "ClassA.h"

@interface ViewController ()

@property (strong, nonatomic) ClassA* classA;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];    
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if ([keyPath isEqualToString:@"radarOn"])
    {
        NSLog(@"--- here in radaron");
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"" message:@"Here" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
        [alert show];
    }
}

- (IBAction)CreateClassAAction:(id)sender
{
    self.classA = [[ClassA alloc] init];
}

- (IBAction)SendNotificationAction:(id)sender
{
    self.classA.radarOn = ! self.classA.radarOn;
}

- (IBAction)ClearKVPAction:(id)sender
{
    @try
    {
        [self.classA removeObserver:self forKeyPath:@"radarOn"];
    }
    @catch (NSException *exception)
    {
        NSString *s = [NSString stringWithFormat:@"Exception ClassA Retain Count %ld %@", CFGetRetainCount((__bridge CFTypeRef)(self.classA)), exception.description];
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"" message:s delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
        [alert show];
        // this will let the class release
        // CFBridgingRelease((__bridge CFTypeRef)(self.classA));
    }
}

- (IBAction)AddKVPAction:(id)sender
{
  [self.classA addObserver:self forKeyPath:@"radarOn" options:NSKeyValueObservingOptionNew context:nil];
}

@end

#import <Foundation/Foundation.h>

@interface ClassA : NSObject

@property (nonatomic, assign) BOOL radarOn;

@end

#import "ClassA.h"

@implementation ClassA

- (void) dealloc
{
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"" message:@"ClassA Dealloc" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
    [alert show];
}

@end
Was it helpful?

Solution 2

code like this seems to work... (no extra retain)

- (IBAction)ClearKVPAction:(id)sender
{
    @try
    {
        [_classA removeObserver:self forKeyPath:@"radarOn"];
    }
    @catch (NSException *exception)
    {
    }  
}

Or

- (IBAction)ClearKVPAction:(id)sender
{
    ClassA* temp = self.classA;
    @try
    {
        [temp removeObserver:self forKeyPath:@"radarOn"];
    }
    @catch (NSException *exception)
    {
    }  
}

Or

Add -fobjc-arc-exceptions to the .m file(s) in question.

OTHER TIPS

This looks like it might be a "feature" of ARC lifted from the Clang 3.4 documentation:

Exceptions

By default in Objective C, ARC is not exception-safe for normal releases:

It does not end the lifetime of __strong variables when their scopes are abnormally terminated by an exception. It does not perform releases which would occur at the end of a full-expression if that full-expression throws an exception. A program may be compiled with the option -fobjc-arc-exceptions in order to enable these, or with the option -fno-objc-arc-exceptions to explicitly disable them, with the last such argument “winning”.

Rationale The standard Cocoa convention is that exceptions signal programmer error and are not intended to be recovered from. Making code exceptions-safe by default would impose severe runtime and code size penalties on code that typically does not actually care about exceptions safety. Therefore, ARC-generated code leaks by default on exceptions, which is just fine if the process is going to be immediately terminated anyway. Programs which do care about recovering from exceptions should enable the option. In Objective-C++, -fobjc-arc-exceptions is enabled by default.

Rationale C++ already introduces pervasive exceptions-cleanup code of the sort that ARC introduces. C++ programmers who have not already disabled exceptions are much more likely to actual require exception-safety. ARC does end the lifetimes of __weak objects when an exception terminates their scope unless exceptions are disabled in the compiler.

Rationale The consequence of a local __weak object not being destroyed is very likely to be corruption of the Objective-C runtime, so we want to be safer here. Of course, potentially massive leaks are about as likely to take down the process as this corruption is if the program does try to recover from exceptions.

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