所以我有以下方法(它是一个UIView类别方法来补充nib加载,但是,它已被清理为更相关):
+ (id) loadFromNib {
NSString* nibName = NSStringFromClass([self class]);
NSArray* elements = [[NSBundle mainBundle] loadNibNamed:nibName owner:nil options:nil];
NSMutableArray* foundCustomObjects = [NSMutableArray array];
NSObject* foundViewObject = nil;
for (NSObject* anObject in elements) {
if ([anObject isKindOfClass:[self class]] && foundViewObject == nil) {
foundViewObject = anObject;
// Keep strong references to non-UIView custom objects (to prevent them from being released due to having weak-only references):
} else if (![anObject isKindOfClass:[UIView class]]) {
[foundCustomObjects addObject:anObject];
}
}
// Generate strong references to all found custom objects:
if (foundViewObject != nil) {
[foundCustomObjects enumerateObjectsUsingBlock:
^(id obj, NSUInteger idx, BOOL *stop) {
[customObjects setObject:foundViewObject forKey:obj];
// (Yes, I will skip objects that are strongly referenced by their view later on)
}];
}
return foundViewObject;
}
customObjects是一个静态变量,定义为:
+ (void) initialize {
if (customObjects == nil) {
// For each view that holds a custom object, store a strong reference to that object here, that way preventing the object from being deallocated due to weak referencing (in UICollectionView.delegate, for example):
customObjects = [NSMapTable mapTableWithKeyOptions:NSMapTableStrongMemory valueOptions:NSMapTableWeakMemory];
}
}
我的问题是我想要对已解除分配的视图真正导致释放引用的“自定义对象”的事实进行单元测试。我该怎么做?
这是我到目前为止(使用OCMock):
- (void) test {
/*
* SETUP */
NSObject* __weak weakRefToSomeObject;
UIView* someView;
NSObject* someObject;
@autoreleasepool {
someView = [[UIView alloc] init];
someObject = [[NSObject alloc] init];
NSArray* nibElements = @[someView, someObject];
id mainBundleMock = [OCMockObject niceMockForClass:[NSBundle class]];
[[[mainBundleMock stub] andReturn:nibElements] loadNibNamed:[OCMArg any] owner:[OCMArg any] options:[OCMArg any]];
id NSBundleMock = [OCMockObject niceMockForClass:[NSBundle class]];
[[[NSBundleMock stub] andReturn:mainBundleMock] mainBundle];
/*
* RUN */
weakRefToSomeObject = someObject;
[UIView loadFromNib];
someObject = nil;
nibElements = nil;
[mainBundleMock stopMocking];
[NSBundleMock stopMocking];
mainBundleMock = nil;
NSBundleMock = nil;
}
/*
* VERIFY */
XCTAssertNotNil(weakRefToSomeObject); // This passes!
@autoreleasepool {
someView = nil;
}
XCTAssertNil(weakRefToSomeObject); // This does not pass - why?
}
在最后一行,我希望删除键值对(视图被弱引用),这样就可以删除对someObject的最后一个强引用,从而使weakRefToSomeObject为零。
我还尝试将someView = nil添加到第一个autoreleasepool(就在NSBundleMock = nil之下),但这没有帮助。
有什么想法吗?
答案 0 :(得分:0)
我通过添加访问功能修复了这个问题:
// Allow tests to access the customObjects map:
NSMapTable* getCustomObjectsMap() {
return customObjects;
}
然后在我的单元测试文档中声明:
// Declare method that gives us access to the static customObjects variable:
NSMapTable* getCustomObjectsMap();
因此,测试代码最终成为:
- (void) testCustomObjectLifecycleFromStartToFinish {
/*
* ASSERT REQUIRED INITIAL STATE */
XCTAssertNil([[getCustomObjectsMap() objectEnumerator] nextObject]);
/*
* SETUP */
UIView* someView = [[UIView alloc] init];
NSObject* someObject = [[NSObject alloc] init];
@autoreleasepool {
@autoreleasepool {
NSArray* nibElements = @[someView, someObject];
id mainBundleMock = [OCMockObject niceMockForClass:[NSBundle class]];
[[[mainBundleMock stub] andReturn:nibElements] loadNibNamed:[OCMArg any] owner:[OCMArg any] options:[OCMArg any]];
id NSBundleMock = [OCMockObject niceMockForClass:[NSBundle class]];
[[[NSBundleMock stub] andReturn:mainBundleMock] mainBundle];
/*
* RUN */
[UIView loadFromNib];
someObject = nil;
nibElements = nil;
}
/*
* VERIFY */
XCTAssertNotNil([[getCustomObjectsMap() objectEnumerator] nextObject]);
// Dropping last strong reference to view:
someView = nil;
}
// Without strong references to someView, the objects map should have been emptied:
XCTAssertNil([[getCustomObjectsMap() objectEnumerator] nextObject]);
}