0

まず、私は Objective-C の初心者です。そのため、OS X や iOS の開発にはあまり詳しくありません。私の経験は主に Java です。

エージェントベースのモデリング フレームワークを作成しています。シミュレーションを表示したいのですが、そのために小さなアプリケーションを書いています。まず、フレームワークについて少し説明します。フレームワークには、すべてのエージェントを反復処理してタスクを実行させるメソッドWorldがあるクラスがあります。start世界の 1 つの「ステップ」が終了すると (つまり、すべてのエージェントが処理を完了した後)、メソッドはを実装するオブジェクトstartのメソッドを呼び出します。このオブジェクトは、コンストラクターを介して以前に渡されました。インターセプターを使用すると、誰でも世界の状態にフックできます。これは、ログを記録したり、達成しようとしているシナリオ (情報をグラフィカルに表示する) で役立ちます。への呼び出しは同期的です。interceptInterceptorProtocolintercept

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 のベスト プラクティスに違反している場合は、お知らせください。

少し外に出ます。すぐに返信しなかったらごめんなさい!

4

1 に答える 1

1

おそらく簡単な答えのために質問を静かにしてください: ;)

UI の更新はメイン スレッドで実行する必要があります。

あなたのコードを正しく読めばstart、バックグラウンド スレッドでメソッドを呼び出すことができます。start メソッドにはmoveBugFrom:...intercept:メソッドのようなものが含まれています。したがって、intercept メソッドsetNeedsDisplay:はバックグラウンド スレッドで呼び出します。

すべての UI 関連のものをメイン スレッドで実行します。次のように、iOS < 4 または OS X < 10.6 (または 10.7 でしたか?) をサポートする必要がない限り、Grand Central Dispatch を使用することをお勧めします。

dispatch_async(dispatch_get_main_queue(), ^{
    // perform UI updates
});
于 2012-09-25T17:09:43.780 に答える