GUIの更新が実行されるのを待つためのテストの必要性を、メインコードパスの実行方法から切り離す必要があります。あなたが投稿した最初のコードブロックでdispatch_sync
は、ほぼ間違いなく間違ったアプローチです(vs. dispatch_async
)。理由もなく(後にコードがないdispatch_sync
)メインスレッドで待機しているバックグラウンドスレッドをブロックするため、スレッドの枯渇につながる可能性があります(展開中)。dispatch_sync
キュー自体を使用して2つの並列タスクをインターロックしようとして作成したと思います。そのやや最適ではないアプローチを使用することに真剣に取り組んでいる場合は、次のようなことを行うことができます。
- (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コードはごくわずかです。(注: 2つの並列タスクがインターロックするためには、インターロックするものを共有する必要があるため、侵入がまったくないことは事実上不可能です。既存のコードでは、それがキューでした。ここでは、セマフォを使用しています。)この不自然な例を考えてみましょう。バックグラウンド操作が完了したときに通知するために使用できるセマフォをテストハーネスにプッシュするための一般的な手段を追加しました。テストされるコードへの「侵入」は、2つのマクロに制限されています。
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.m:
#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
お役に立てれば。