![](/img/trans.png)
[英]Crash when using ignoringNonObjectArgs in OCMock in modern style
[英]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
的引用)
接下來,測試完成並通過。
但是,如果現在出現問題OCMReturnValueProvider
是dealloc
“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.