首先,我是一個Objective-C新手。所以我不熟悉OS X或iOS開發。我的經驗主要是Java。從線程異步更新NSView
我正在創建一個基於代理的建模框架。我想展示模擬,並做我正在寫一個小應用程序。首先,介紹一下框架。該框架有一個World
類,其中有一個start
方法,該方法遍歷所有代理並讓它們執行其任務。在世界的一個「步驟」結束時(即,在所有代理完成它們的事情之後),start
方法調用實現InterceptorProtocol
的對象的方法intercept
。這個對象以前通過構造函數傳入。使用攔截器,任何人都可以進入世界的狀態。這對日誌記錄很有用,或者在我試圖完成的場景中:以圖形方式顯示信息。撥打intercept
是同步的。
現在就GUI應用程序而言,它非常簡單。我有一個初始化自定義視圖的控制器。這個自定義視圖還實現InterceptorProtocol
,以便它可以監聽和查看世界上發生的事情。我創建了一個World
對象並作爲攔截器傳入視圖中。該視圖通過私有財產保持對世界的引用,因此,一旦我初始化了世界,我將視圖的世界屬性設置爲我剛剛創建的世界(我意識到這會創建一個循環,但我需要引用世界在視圖的drawRect
方法和唯一的方法,我可以擁有它是如果我保持從類的引用)。
由於世界上的start
方法是同步的,所以我不會馬上啓動世界。在drawRect
方法中,我檢查世界是否在運行。如果不是,我在後臺線程中啓動它。如果是這樣,我檢查世界並顯示我需要的所有圖形。
在intercept
方法(從start
被調用後臺線程運行),我設置setNeedsToDisplay
到YES
。由於世界的start
方法在單獨的線程中運行,因此我還有一個鎖定對象用於同步,這樣我就不會在World
對象正在進行變異的情況下工作(此部分有點笨拙,而且很可能不按我期望的方式工作 - 這裏有不止幾個粗糙的地方,我只是想努力工作;我打算晚些時候清理一下)。
我的問題是視圖呈現一些東西,然後它幾乎鎖定。我可以看到NSLog
語句正在被調用,所以代碼正在運行,但沒有任何更新的視圖。
下面是一些相關的代碼:
MasterViewController
#import "MasterViewController.h"
#import "World.h"
#import "InfectableBug.h"
@interface MasterViewController()
@end
@implementation MasterViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
_worldView = [[WorldView alloc] init];
World* world = [[World alloc] initWithName: @"Bhumi"
rows: 100
columns: 100
iterations: 2000
snapshotInterval: 1
interceptor: _worldView];
for(int i = 0; i < 999; i++) {
NSMutableString* name = [NSMutableString stringWithString: @"HealthyBug"];
[name appendString: [[NSNumber numberWithInt: i] stringValue]];
[world addBug: [[InfectableBug alloc] initWithWorld: world
name: name
layer: @"FirstLayer"
infected: NO
infectionRadius: 1
incubationPeriod: 10
infectionStartIteration: 0]];
}
NSLog(@"Added all bugs. Going to add infected");
[world addBug: [[InfectableBug alloc] initWithWorld: world
name: @"InfectedBug"
layer: @"FirstLayer"
infected: YES
infectionRadius: 1
incubationPeriod: 10
infectionStartIteration: 0]];
[_worldView setWorld: world];
//[world start];
}
return self;
}
- (NSView*) view {
return self.worldView;
}
@end
世界觀
#import "WorldView.h"
#import "World.h"
#import "InfectableBug.h"
@implementation WorldView
@synthesize world;
- (id) initWithFrame:(NSRect) frame {
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
}
return self;
}
- (void) drawRect:(NSRect) dirtyRect {
CGContextRef myContext = [[NSGraphicsContext currentContext] graphicsPort];
CGContextClearRect(myContext, CGRectMake(0, 0, 1024, 768));
NSUInteger rows = [world rows];
NSUInteger columns = [world columns];
NSUInteger cellWidth = 1024/columns;
NSUInteger cellHeight = 768/rows;
if([world running]) {
@synchronized (_lock) {
//Ideally we would need layers, but for now let's just get this to display
NSArray* bugs = [world bugs];
NSEnumerator* enumerator = [bugs objectEnumerator];
InfectableBug* bug;
while ((bug = [enumerator nextObject])) {
if([bug infected] == YES) {
CGContextSetRGBFillColor(myContext, 128, 0, 0, 1);
} else {
CGContextSetRGBFillColor(myContext, 0, 0, 128, 1);
}
NSLog(@"Drawing bug %@ at %lu, %lu with width %lu and height %lu", [bug name], [bug x] * cellWidth, [bug y] * cellHeight, cellWidth, cellHeight);
CGContextFillRect(myContext, CGRectMake([bug x] * cellWidth, [bug y] * cellHeight, cellWidth, cellHeight));
}
}
} else {
[world performSelectorInBackground: @selector(start) withObject: nil];
}
}
- (BOOL) isFlipped {
return YES;
}
- (void) intercept: (World *) aWorld {
struct timespec time;
time.tv_sec = 0;
time.tv_nsec = 500000000L;
//nanosleep(&time, NULL);
@synchronized (_lock) {
[self setNeedsDisplay: YES];
}
}
@end
開始方法World.m :
- (void) start {
running = YES;
while(currentIteration < iterations) {
@autoreleasepool {
[bugs shuffle];
NSEnumerator* bugEnumerator = [bugs objectEnumerator];
Bug* bug;
while((bug = [bugEnumerator nextObject])) {
NSString* originalLayer = [bug layer];
NSUInteger originalX = [bug x];
NSUInteger originalY = [bug y];
//NSLog(@"Bug %@ is going to act and location %i:%i is %@", [bug name], [bug x], [bug y], [self isOccupied: [bug layer] x: [bug x] y: [bug y]] ? @"occupied" : @"not occupied");
[bug act];
//NSLog(@"Bug has acted");
if(![originalLayer isEqualToString: [bug layer]] || originalX != [bug x] || originalY != [bug y]) {
//NSLog(@"Bug has moved");
[self moveBugFrom: originalLayer atX: originalX atY: originalY toLayer: [bug layer] atX: [bug x] atY: [bug y]];
//NSLog(@"Updated bug position");
}
}
if(currentIteration % snapshotInterval == 0) {
[interceptor intercept: self];
}
currentIteration++;
}
}
//NSLog(@"Done.");
}
請讓我知道如果你想看到任何其他的代碼。我意識到代碼並不漂亮;我只是想讓東西工作,我打算以後再清理它。另外,如果我違反了Objective-C最佳實踐,請告訴我!
走出去一點;對不起,如果我不立即迴應!
聽起來有希望!我會給這個:) –
另一個問題 - 將'peformOnMainThread'也工作? –
是的,'performOnMainThread'也可以。 – Pascal