2012-09-23 97 views
1

我在iOS和Mac OS X開發中使用Objective-C中的測試驅動開發,我希望能夠編寫能夠驗證我使用類工廠方法創建的對象返回autorelease對象的測試。檢測到自動釋放對象

某人如何編寫驗證提供的對象是autorelease的測試?

+2

您是否正在使用ARC?是的,我認爲沒有必要編寫這個測試,因爲它將測試框架,並且他們可能會寫出比我和我有時間更徹底的測試。如果不是,您可以嘗試創建一個'NSAutoreleasePool'並調用你的方法,並調用'[pool drain]'如果你有一種方法來觀察對'dealloc'的調用,那麼你可能會確定對象是否超出了所有明確的引用,然後被釋放調用'drain'只是一個想法,可能值得一玩 –

回答

2

總之,你不能。沒有辦法知道對象的自動釋放狀態。

0

在某些情況下,您可以推斷對象是否放置在自動釋放池中。這個想法是聲明一個指向對象的指針,在@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]; 
} 

現在,這裏的東西我跑一個片段。 SomeClass是不帶附加屬性或方法的NSObject子類。

self.deallocatedPointerValues = [NSMutableArray array];

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的其他消息可能被髮送到您的工廠方法返回的對象時,這不起作用。此外,這應該不言而喻,swarling dealloc應該只在實驗環境中完成,我認爲有些人會認爲它不應該在測試中出現混亂(顯然它不應該在生產過程中被混淆!)。最後,更重要的是,這不適用於諸如NSString之類的Foundation對象,這些對象已被優化,但並不總是清楚您是否創建新實例。所以,如果有的話,這對你自己的自定義對象來說是最合適的。

最後一句話,我不認爲這是真的。我覺得這是比它值得的工作更多的工作,並且如此狹隘地適用,因爲在內存管理方面,花費時間更好地學習工具是更好的投資。當然,對於ARC的優勢,這種方法從一開始就顯得過時了。無論如何,如果您確實需要編寫這樣的測試,並且可以在此處解決這些限制,請隨時修改此代碼。我很想知道它是如何在實際的測試環境中出現的。

+2

不需要調試;編寫一個類登錄dealloc,並使用objc_setAssociatedObject()從目標對象掛起一個實例.... – bbum

+0

@bbum哦完美,是的,這是一個偉大的想法! –

0

我讚揚你對TDD的奉獻精神。但是內存管理是一個你必須遵循既定約定的領域:「當返回一個對象時,它需要照顧自己的一生。「我的單元測試在我意外過度釋放某些東西的時候抓住了我,但他們不會發現泄漏。爲此,我首先依靠分析,然後運行泄漏測試儀