0

IOS 6.1

我们注意到,当我们为不存在的键值对获得 removeObserver 异常时,具有 KVP 获取的类和 removeObserver 调用中的额外保留计数。

以下是一些证明这一点的测试代码。还有一个桥接版本可以解决这个问题。

欢迎任何意见......

#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
4

2 回答 2

0

像这样的代码似乎工作......(没有额外的保留)

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

或者

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

或者

将 -fobjc-arc-exceptions 添加到有问题的 .m 文件中。

于 2013-07-03T19:22:08.050 回答
0

这看起来可能是从 Clang 3.4 文档中提取的 ARC 的“功能” :

例外

默认情况下,在 Objective C 中,ARC 对于正常版本不是异常安全的:

当 __strong 变量的作用域被异常异常终止时,它不会结束 __strong 变量的生命周期。如果完整表达式抛出异常,它不会执行将在完整表达式末尾发生的释放。可以使用选项 -fobjc-arc-exceptions 编译程序以启用它们,或使用选项 -fno-objc-arc-exceptions 显式禁用它们,最后一个这样的参数“获胜”。

基本原理 标准 Cocoa 约定是异常表示程序员错误并且不打算从中恢复。默认情况下使代码异常安全将对通常实际上不关心异常安全的代码施加严重的运行时和代码大小惩罚。因此,ARC 生成的代码默认会在异常情况下泄漏,如果进程无论如何都将立即终止,这很好。关心从异常中恢复的程序应该启用该选项。在 Objective-C++ 中,默认启用 -fobjc-arc-exceptions。

基本原理 C++ 已经引入了 ARC 引入的那种普遍的异常清理代码。尚未禁用异常的 C++ 程序员更有可能实际需要异常安全。当异常终止其范围时,ARC 确实会结束 __weak 对象的生命周期,除非在编译器中禁用了异常。

基本原理 本地 __weak 对象没有被销毁的后果很可能是破坏了 Objective-C 运行时,所以我们希望在这里更安全。当然,如果程序确实试图从异常中恢复,那么潜在的大量泄漏可能会导致进程停止运行,就像这种损坏一样。

于 2013-07-03T19:54:18.143 回答