检测到自动释放对象

Posted

技术标签:

【中文标题】检测到自动释放对象【英文标题】:Detected autorelease objects 【发布时间】:2012-09-23 01:12:51 【问题描述】:

我在 ios 和 Mac OS X 开发中使用 Objective-C 中的测试驱动开发,我希望能够编写测试来验证我使用类工厂方法创建的对象是否返回自动释放对象。

如何编写测试来验证提供的对象是自动释放的?

【问题讨论】:

您是否使用ARC?如果是,我认为没有必要编写此测试,因为它将测试框架,而且他们可能会编写比您和我有时间的更彻底的测试。如果你不是,你可以尝试创建一个NSAutoreleasePool 并在它和[pool drain] 之间调用你的方法。如果您有办法观察对dealloc 的调用,那么您可能能够查明该对象是否已超出所有显式引用,然后通过对drain 的调用被释放。只是一个想法,可能值得一玩。 【参考方案1】:

简而言之,你不能。无法知道对象的自动释放状态。

【讨论】:

【参考方案2】:

在某些情况下,您可以推断对象是否被放置在自动释放池中。这个想法是声明一个指向对象的指针,在 @autoreleasepool 块中实例化它,然后验证它是否在块结束后调用了 dealloc

通过您选择的混合或覆盖dealloc 的任何组合,您必须首先提供一种方法来验证dealloc 是否已被调用。我编写了一个NSObject 类别,它具有以下接口和实现,它提供了一个deallocationDelegate 属性,当对象被释放时,该属性将收到handleDeallocation: 的消息。

@interface NSObject (FunTimes)

@property (nonatomic, assign) id deallocationDelegate;

@end

@implementation NSObject (FunTimes)

+ (void)load

    Class klass = [NSObject class];
    SEL originalSelector = @selector(dealloc);
    Method originalMethod = class_getInstanceMethod(klass, originalSelector);
    SEL replacementSelector = @selector(funDealloc);
    Method replacementMethod = class_getInstanceMethod(klass, replacementSelector);
    if(class_addMethod(klass, originalSelector, method_getImplementation(replacementMethod), method_getTypeEncoding(replacementMethod)))
    
        class_replaceMethod(klass, replacementSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
    
    else
    
        method_exchangeImplementations(originalMethod, replacementMethod);
    


- (void)funDealloc

    if (self.deallocationDelegate)
        [self.deallocationDelegate performSelector:@selector(handleDeallocation:) withObject:self];

    [self funDealloc];


static char myKey;

- (void)setDeallocationDelegate:(id)deallocationDelegate

    objc_setAssociatedObject(self, &myKey, deallocationDelegate, OBJC_ASSOCIATION_ASSIGN);


- (id)deallocationDelegate

    return objc_getAssociatedObject(self, &myKey);


@end

我在我的应用程序委托中运行了一些测试代码,只是为了看看它是否有效。我声明了一个NSMutableArray 实例,该实例旨在保存从调用-handleDeallocation 的对象的指针派生的NSValue 实例,我实现如下所示:

- (void)handleDeallocation:(id)toDie

    NSValue *pointerValue = [NSValue valueWithPointer:toDie];
    [self.deallocatedPointerValues addObject:pointerValue];

现在,这是我运行的一个 sn-p。 SomeClass 是一个 NSObject 子类,没有其他属性或方法。

self.deallocatedPointerValues = [NSMutableArray 数组];

SomeClass *arsc = nil;
@autoreleasepool 
    arsc = [[[SomeClass alloc] init] autorelease];
    arsc.deallocationDelegate = self;
    NSValue *prePointerValue = [NSValue valueWithPointer:arsc];
    BOOL preDeallocated = [self.deallocatedPointerValues containsObject:prePointerValue];
    NSLog(@"PreDeallocated should be no is %d",preDeallocated);


NSValue *postPointerValue = [NSValue valueWithPointer:arsc];
BOOL postDeallocated = [self.deallocatedPointerValues containsObject:postPointerValue];
NSLog(@"Post deallocated should be yes is %d",postDeallocated);

在这种情况下,可以验证arsc(代表自动释放的SomeClass)指向的对象由于结束了@autoreleasepool块而被释放。

这种方法有几个重大限制。一,当retain 的其他消息可能被发送到从您的工厂方法返回的对象时,这不起作用。此外,这应该不言而喻,swizzling dealloc 应该只在实验环境中进行,我认为有些人会争辩说它不应该在测试中进行 swizzled(显然它不应该在生产中进行 swizzled!)。最后,更重要的是,这不适用于诸如 NSString 之类的 Foundation 对象,这些对象的优化方式并不总是清楚您是否正在创建新实例。因此,这对于您自己的自定义对象来说是最合适的(如果有的话)。

最后,我认为这样做并不实际。我觉得这比它值得做的工作更多,而且适用范围如此之小,因为在内存管理方面,花时间学习工具更好,成为更好的投资。而且,当然,由于 ARC 的优势,这种方法从一开始就是过时的。无论如何,如果您确实需要编写此类测试,并且可以解决此处的限制,请随时调整此代码。我很想知道它在实际测试环境中的表现如何。

【讨论】:

无需调酒;编写一个登录 dealloc 的类,并使用 objc_setAssociatedObject() 将一个实例挂在目标对象上...... @bbum 哦,完美,是的,这是个好主意!【参考方案3】:

我赞扬您对 TDD 的奉献。但是内存管理是一个你只需要遵循公认的约定的领域:“当返回一个对象时,它需要照顾它自己的生命周期。”当我不小心过度释放某些东西时,我的单元测试会抓住我,但它们不会发现泄漏。为此,我首先依赖分析,然后运行泄漏工具。

【讨论】:

以上是关于检测到自动释放对象的主要内容,如果未能解决你的问题,请参考以下文章

自动释放池

双重释放自动释放的对象不会崩溃

释放自动释放字符串对象不会崩溃

为啥我的自动释放对象没有被释放?

在创建对象后自动释放对象时出错

将自动释放的对象添加到 NSMutableArray