您应该解耦您的测试需求,以等待GUI更新从主代码路径的运行方式运行。在您发布的第一个代码块中,dispatch_sync
几乎肯定是错误的方法(与dispatch_async
),因为您将无缘无故地阻止在主线程中等待的后台线程(在dispatch_sync
之后没有代码),这可能会导致线程匮乏(在部署中)。我猜你已经做了dispatch_sync
试图使用队列本身来连接两个并行任务。如果你是真正致力于使用有所次优的方法,你可以做这样的事情:
- (void)testOne
{
SOAltUpdateView* view = [[SOAltUpdateView alloc] initWithFrame: NSMakeRect(0, 0, 100, 100)];
STAssertNotNil(view, @"View was nil");
STAssertEqualObjects(view.color, [NSColor redColor] , @"Initial color was wrong");
dispatch_queue_t q = dispatch_queue_create("test", 0);
dispatch_group_t group = dispatch_group_create();
view.queue = q;
// Run the operation
[view update];
// An operation we can wait on
dispatch_group_async(group, q, ^{ });
while (dispatch_group_wait(group, DISPATCH_TIME_NOW))
{
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, YES);
}
STAssertEqualObjects(view.color, [NSColor greenColor] , @"Updated color was wrong");
view.queue = nil;
[view release];
dispatch_release(group);
dispatch_release(q);
}
这是似乎最接近你已经有什么办法,但我想出了一些可能是更好/更清洁一点:信号量可以为你做到这一点,只需一点点努力,你就可以将侵入你的实际GUI代码变得非常小。 (注意:根本不可能有任何入侵,因为为了让两个并行任务互锁,他们必须共享一些东西来联系,在 - 共享 - 在你现有的代码中,它是队列,这里我使用了一个信号量。)考虑一下这个人为的例子:我为测试工具添加了一个通用的方法来推入一个信号量,当后台操作完成时它可以用来通知它。被测代码的“入侵”限于两个宏。
NSObject的+ AsyncGUITestSupport.h:
@interface NSObject (AsyncGUITestSupport)
@property (nonatomic, readwrite, assign) dispatch_semaphore_t testCompletionSemaphore;
@end
#define OPERATION_BEGIN(...) do { dispatch_semaphore_t s = self.testCompletionSemaphore; if (s) dispatch_semaphore_wait(s, DISPATCH_TIME_NOW); } while(0)
#define OPERATION_END(...) do { dispatch_semaphore_t s = self.testCompletionSemaphore; if (s) dispatch_semaphore_signal(s); } while(0)
NSObject的+ AsyncGUITestSupport。L:
#import "NSObject+AsyncGUITestSupport.h"
#import <objc/runtime.h>
@implementation NSObject (AsyncGUITestSupport)
static void * const kTestingSemaphoreAssociatedStorageKey = (void*)&kTestingSemaphoreAssociatedStorageKey;
- (void)setTestCompletionSemaphore:(dispatch_semaphore_t)myProperty
{
objc_setAssociatedObject(self, kTestingSemaphoreAssociatedStorageKey, myProperty, OBJC_ASSOCIATION_ASSIGN);
}
- (dispatch_semaphore_t)testCompletionSemaphore
{
return objc_getAssociatedObject(self, kTestingSemaphoreAssociatedStorageKey);
}
@end
SOUpdateView.h
@interface SOUpdateView : NSView
@property (nonatomic, readonly, retain) NSColor* color;
- (void)update;
@end
SOUpdateView.m
#import "SOUpdateView.h"
#import "NSObject+AsyncGUITestSupport.h"
@implementation SOUpdateView
{
NSUInteger _count;
}
- (NSColor *)color
{
NSArray* colors = @[ [NSColor redColor], [NSColor greenColor], [NSColor blueColor] ];
@synchronized(self)
{
return colors[_count % colors.count];
}
}
- (void)drawRect:(NSRect)dirtyRect
{
[self.color set];
NSRectFill(dirtyRect);
}
- (void)update
{
OPERATION_BEGIN();
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);
@synchronized(self)
{
_count++;
}
dispatch_async(dispatch_get_main_queue(), ^{
[self setNeedsDisplay: YES];
OPERATION_END();
});
});
}
@end
然后测试工具:
#import "TestSOTestGUI.h"
#import "SOUpdateView.h"
#import "NSObject+AsyncGUITestSupport.h"
@implementation TestSOTestGUI
- (void)testOne
{
SOUpdateView* view = [[SOUpdateView alloc] initWithFrame: NSMakeRect(0, 0, 100, 100)];
STAssertNotNil(view, @"View was nil");
STAssertEqualObjects(view.color, [NSColor redColor] , @"Initial color was wrong");
// Push in a semaphore...
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
view.testCompletionSemaphore = sem;
// Run the operation
[view update];
// Wait for the operation to finish.
while (dispatch_semaphore_wait(sem, DISPATCH_TIME_NOW))
{
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, YES);
}
// Clear out the semaphore
view.testCompletionSemaphore = nil;
STAssertEqualObjects(view.color, [NSColor greenColor] , @"Updated color was wrong");
}
@end
希望这有助于。
你不能在另一个线程或队列上运行单元测试吗? – 2013-02-20 23:47:09