由于 dispatch_once 导致 XCTest 失败的解决方法?

Posted

技术标签:

【中文标题】由于 dispatch_once 导致 XCTest 失败的解决方法?【英文标题】:Workaround for XCTest failures due to dispatch_once? 【发布时间】:2014-04-26 05:40:09 【问题描述】:

如果多个测试需要运行 dispatch_once(),那么只有一个测试通过。

这是一个问题示例(非 ARC 代码):

#import <XCTest/XCTest.h>

@interface TestBrokenDispatchOnce : XCTestCase

@end

@implementation TestBrokenDispatchOnce

+(NSArray*) get 
    static NSArray* _get;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^
        _get=[NSArray arrayWithObjects:@(1),@(2),nil];
    );

    return _get;


- (void)testOne 
    for(NSNumber* n in [[self class] get]) 
        NSLog(@"%@",n);
    

    XCTAssertTrue(YES, @"pass");    


- (void)testTwo 
    // will die here with access exception
    for(NSNumber* n in [[self class] get]) 
        NSLog(@"%@",n);
    

    XCTAssertTrue(YES, @"pass");


@end

每个单独运行的测试用例都会通过,但如果两个都运行,第二个就会失败。

我看到的实际问题是针对使用 dispatch_once 对数组进行延迟初始化的(非 ARC 库)进行测试,但失败与这个最小示例相同。我猜测 XCTest 框架正在释放一些内存池,因此 NSArray 被释放,但静态保持初始化。

除了在测试中重置 onceToken 之外,还有其他解决方案吗?

【问题讨论】:

【参考方案1】:

测试暴露了get 方法中的内存管理问题。由于arrayWithObjects: 返回一个自动释放对象,因此在MRC 下,_get 指向的数组在下次弹出自动释放池时被释放。要解决此问题,需要保留数组,方法是使用initWithObjects: 分配它或向arrayWithObjects: 返回的对象发送保留消息。

【讨论】:

谢谢。将保留附加到 arrayWithObjects 固定的东西。

以上是关于由于 dispatch_once 导致 XCTest 失败的解决方法?的主要内容,如果未能解决你的问题,请参考以下文章

模拟dispatch_once

目标c单例dispatch_once实现更好?

iOS 的单例模式 dispatch_once

dispatch_once 的秘密

ios34---GDC,dispatch_once

为啥苹果推荐使用 dispatch_once 来实现 ARC 下的单例模式?