簡體   English   中英

使用OCMock意外崩潰,在NSString上模擬'mutableCopy'

[英]Unexpected crash using OCMock, mocking 'mutableCopy' on an NSString

我正在嘗試在iOS上使用OCMock和GHUnit模擬對mutableCopy的調用。

盡管通過了測試,但在清理過程中仍收到EXC_BAD_ACCESS異常,因此我試圖找出原因。

看看這個。 該測試表明可以在模擬NSString上模擬mutableCopy 在此測試中,我返回另一個NSString ,而不是NSMutableString 這只是為了證明mutableCopy期望被觸發,並且測試通過了。

#import <GHUnitIOS/GHUnit.h>
#import "OCMock.h"

@interface TestItClass : GHTestCase @end
@implementation TestItClass

// Test that mutableCopy on an NSString is mockable.
- (void)test_1_mutableCopyOfString_shouldBeMockable_givenAStringIsReturned {
    NSString *string = [OCMockObject mockForClass:NSString.class];
    NSString *copy = @"foo";
    [(NSString *) [[(id) string expect] andReturn:copy] mutableCopy];

    // MutableCopy is mocked to return a string, not a mutable string!
    // This is clearly wrong from a static typing point of view, but
    // the test passes anyway, which is ok.
    NSMutableString *result = [string mutableCopy];
    GHAssertEquals(result, copy, nil);
    [(id)string verify];
}

現在,我更改了模擬期望,以便mutableCopy現在返回NSMutableString 測試仍然通過,但是在拆卸測試中,我得到了EXC_BAD_ACCESS異常。

- (void)test_2_mutableCopyOfString_shouldBeMockable_givenAMutableStringIsReturned {
    NSString *string = [OCMockObject mockForClass:NSString.class];
    NSMutableString *copy = [@"foo" mutableCopy];
    [(NSString *) [[(id) string expect] andReturn:copy] mutableCopy];

    // Now mutableCopy is mocked to return a mutable string!
    // The test now blows up during the test teardown! Why?
    NSMutableString *foo = [string mutableCopy];
    GHAssertEquals(foo, copy, nil);
    [(id)string verify];
}

@end

在這兩個測試中,對於斷言,驗證工作正常。 這表明這兩個測試的構造都很好,並且模擬預期已按預期進行。 但是,第二項測試由於內存訪問錯誤而失敗:

Simulator session started with process 7496
Debugger attached to process 7496
2013-03-11 18:23:05.519 UnitTests[7496:c07] TestItClass/test_2_mutableCopyOfString_shouldBeMockable_givenAMutableStringIsReturned ✘ 0.00s
2013-03-11 18:23:06.466 UnitTests[7496:c07] Re-running: TestItClass/test_2_mutableCopyOfString_shouldBeMockable_givenAMutableStringIsReturned <GHTest: 0x7793340>
Exception: EXC_BAD_ACCESS (code=1, address=0x11dfe3ea))

您能告訴我為什么會發生這種情況嗎?

謝謝喬

您面臨的問題是由ARC遵循基本內存管理規則引起的 具體來說:

  • 您擁有自己創建的任何對象

    使用名稱以“ alloc”,“ new”,“ copy”或“ mutableCopy”開頭的方法(例如alloc,newObject或mutableCopy)創建對象。

因此解決方案將是查看調用選擇器以確定是否retain returnValue

希望對您有所幫助。

我是了解正在發生的事情的一部分。 我已經編譯了自己的OCMock調試庫,以便可以了解崩潰發生的位置。

這就是我所發現的。

在我的原始測試中,我呼叫andReturn:來設定回報期望值:

NSMutableString *copy = [@"foo" mutableCopy];
[(NSString *) [[(id) string expect] andReturn:copy] mutableCopy];

依次調用OCMReturnValueProvider來存儲copy以便可以在適當的時間返回它:

@implementation OCMReturnValueProvider

- (id)initWithValue:(id)aValue
{
    self = [super init];
    returnValue = [aValue retain];
    return self;
}

此時,調試器說aValue的類型為__NSCFString (敲響的鍾聲在我的腦海中響起;這不是通往底層字符串的免費電話嗎?不是對NSMutableString的引用)

接下來,測試完成並通過。

但是,如果現在出現問題OCMReturnValueProviderdealloc “d。

@implementation OCMReturnValueProvider
- (void)dealloc
{
    [returnValue release];
    [super dealloc];
}

[returnValue release]時發生崩潰; OCMReturnValueProvider嘗試釋放它先前retain__NSCFString

接下來,我打開NSZombie調試,這表明了:

2013-03-12 20:58:19.654 UnitTests[16667:c07] TestItClass/test_2_mutableCopyOfString_shouldBeMockable_givenAMutableStringIsReturned  
2013-03-12 20:58:21.778 UnitTests[16667:c07] Re-running: TestItClass/test_2_mutableCopyOfString_shouldBeMockable_givenAMutableStringIsReturned <GHTest: 0x4afc5fd0>
2013-03-12 20:58:21.780 UnitTests[16667:c07] *** -[CFString release]: message sent to deallocated instance 0x4b0b1fe0

malloc-history(Find Zombie工具)正在幫助闡明它:

Category            Event Type  Ref Ct  Responsible Caller
CFString (mutable)  Malloc        1     -[TestItClass test_2_mutable...]
CFString (mutable)  Retain        2     -[OCMReturnValueProvider initWithValue:]
CFString (mutable)  Retain        3     -[TestItClass test_2_mutable...]
CFString (mutable)  Retain        4     -[TestItClass test_2_mutable...]
CFString (mutable)  Release       3     -[TestItClass test_2_mutable...]
CFString (mutable)  Release       2     -[TestItClass test_2_mutable...]
CFString (mutable)  Release       1     -[TestItClass test_2_mutable...]
CFString (mutable)  Release       0     -[TestItClass test_2_mutable...]
CFString (mutable)  Zombie       -1     -[OCMReturnValueProvider dealloc]

因此,測試類中的某些內容導致釋放的次數超過保留的次數。 為什么會這樣呢? 奇怪!

經過更多調查后,我發現了發生崩潰的原因。

讓我們再次看一下測試:

- (void)test_2_mutableCopyOfString_shouldBeMockable_givenAMutableStringIsReturned {
    NSString *string = [OCMockObject mockForClass:NSString.class];
    NSMutableString *copy = [@"foo" mutableCopy];
    [(NSString *) [[(id) string expect] andReturn:copy] mutableCopy];

    NSMutableString *foo = [string mutableCopy];
}

發生的事情是,編譯器假定[string mutableCopy]返回的對象已被mutableCopy retained ,因此,在dealloced foo時,ARC等效於[foo release] 這是一個問題,因為我們沒有在andReturn:增加此對象的引用計數。

對於為什么我們在配置為由andReturn:返回的其他對象上看不到此行為,我感到困惑。 處理OCMReturnValueProvider響應的OCMReturnValueProvider不是ARC管理的,並且不保留返回值:

- (void)handleInvocation:(NSInvocation *)anInvocation
{
    [anInvocation setReturnValue:&returnValue];
}

因此,可以通過在NSInvocation設置返回值之前搶先retain返回值來解決此問題:

- (void)handleInvocation:(NSInvocation *)anInvocation
{
    [returnValue retain];
    [anInvocation setReturnValue:&returnValue];
}

這看起來像是OCMock的錯誤。 但是鑒於並非在所有情況下都會出現此問題,因此我不確定。 我的修復工作,但現在可以運行在這可能不是需要這個額外的對象內存泄露的風險retain 但是,對於測試而言,與沒有運行的測試相比,內存泄漏對於我而言是可以接受的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM