まず、私は Objective-C の初心者です。そのため、OS X や iOS の開発にはあまり詳しくありません。私の経験は主に Java です。
エージェントベースのモデリング フレームワークを作成しています。シミュレーションを表示したいのですが、そのために小さなアプリケーションを書いています。まず、フレームワークについて少し説明します。フレームワークには、すべてのエージェントを反復処理してタスクを実行させるメソッドWorld
があるクラスがあります。start
世界の 1 つの「ステップ」が終了すると (つまり、すべてのエージェントが処理を完了した後)、メソッドはを実装するオブジェクトstart
のメソッドを呼び出します。このオブジェクトは、コンストラクターを介して以前に渡されました。インターセプターを使用すると、誰でも世界の状態にフックできます。これは、ログを記録したり、達成しようとしているシナリオ (情報をグラフィカルに表示する) で役立ちます。への呼び出しは同期的です。intercept
InterceptorProtocol
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のstartメソッド:
- (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 のベスト プラクティスに違反している場合は、お知らせください。
少し外に出ます。すぐに返信しなかったらごめんなさい!