1

短いバージョン:

NSNotificationCenter が通知をプッシュしていたのとは異なるオブジェクトのインスタンスでメソッドが呼び出されているという問題に遭遇しました (作成されたインスタンスは 1 つだけでしたが)。

ロングバージョン:

OpenGL 更新/描画ループによって操作されている「パズル」オブジェクト インスタンスがあります。

次のように、さまざまなタッチ イベントを管理するための Control シングルトンを作成しました。

@implementation Controls

static Controls *SharedGameControls = nil;

-(id)init {
    self = [super init];
    return self;
}

+ (Controls*)SharedGameControls{
    if (SharedGameControls == nil){
        SharedGameControls = [[super allocWithZone:NULL] init];
    }
    return SharedGameControls;
}

+ (id)allocWithZone:(NSZone *)zone
{
    return self.SharedGameControls;
}

- (id)copyWithZone:(NSZone *)zone
{
    return self;
}


-(void)oneFingerSwipeDelegator:(UISwipeGestureRecognizer *)swipe{
    switch (GState.currentMode)
    {
        case PuzzleLayer: {
            CGPoint Origin = [swipe locationInView: _view];
            //I believe this line should call oneFingerSwipe on the object instance 
            //provided to the singleton
            [_oneFingerSwipeDelegate oneFingerSwipe:Origin Direction:swipe.direction];
            break;
        }
        default:{
            break;
        }
    }
}

-(void)setDefaultState{
    GState = [GameState SharedGameState];

    UISwipeGestureRecognizer *oneFingerSwipeUp = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(oneFingerSwipeDelegator:)];
    [oneFingerSwipeUp setDirection:UISwipeGestureRecognizerDirectionUp];
    [_view addGestureRecognizer:oneFingerSwipeUp];

    UISwipeGestureRecognizer *oneFingerSwipeDown = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(oneFingerSwipeDelegator:)];
    [oneFingerSwipeDown setDirection:UISwipeGestureRecognizerDirectionDown];
    [_view addGestureRecognizer:oneFingerSwipeDown];

    UISwipeGestureRecognizer *oneFingerSwipeLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(oneFingerSwipeDelegator:)];
    [oneFingerSwipeLeft setDirection:UISwipeGestureRecognizerDirectionLeft];
    [_view addGestureRecognizer:oneFingerSwipeLeft];

    UISwipeGestureRecognizer *oneFingerSwipeRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(oneFingerSwipeDelegator:)];
    [oneFingerSwipeRight setDirection:UISwipeGestureRecognizerDirectionRight];
    [_view addGestureRecognizer:oneFingerSwipeRight];

    UITapGestureRecognizer * single = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(singleTapDelegator:)];
    single.numberOfTapsRequired = 1;
    [_view addGestureRecognizer:single];
}

-(void)singleTapDelegator:(UITapGestureRecognizer *)tap{
    CGPoint origin = [tap locationInView: _view];
    NSValue *Origin = [NSValue valueWithCGPoint:origin];
    switch (GState.currentMode)
    {
        case PuzzleLayer: {
            [[NSNotificationCenter defaultCenter] postNotificationName:@"puzzleTap" object:nil userInfo:[NSDictionary dictionaryWithObject:Origin forKey:@"Origin"]];
            break;
        }
        default:{
            break;
        }
    }
}

@end

「_oneFingerSwipeDelegate」は、.h ファイルで次のように定義されています。

@property (nonatomic, assign) id oneFingerSwipeDelegate;

その後、 Puzzle クラスでは、イベントは次のように処理されました。

@implementation Puzzle

-(id)init{
    self =[super init];
    if (self){
        GControls = [Controls SharedGameControls];
        //I believe, possibly wrongly, that the line below will set this object instance
        //to be the object oneFingerSwipe is called on
        GControls.oneFingerSwipeDelegate = self;
        GControls.twoFingerSwipeDelegate = self;
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(singleTap:) name:@"puzzleTap" object:nil];
        _Started = false;
       //other code omitted
    }
    return self;
}

//implementation of the delegate in Controls.h
-(void)oneFingerSwipe:(CGPoint)touchPoint Direction:(UISwipeGestureRecognizerDirection)direction{
    //do some stuff with objects inside puzzle
}

//observer for 'puzzleTap'
-(void)singleTap:(NSNotification*)note{
    GLKVector3 Near,Far;
    NSDictionary *dict = [note userInfo];
    CGPoint origin = [[dict objectForKey:@"Origin"] CGPointValue ];
    //Do stuff with objects inside puzzle
}

//other code omitted
@end

そこで、ジェスチャ認識をテストしてすべてが機能していることを確認したところ、スワイプが処理されていないことに気付きました。

タップすると、Puzzle オブジェクトの singleTap メソッドに通知が正しく送信されます (Puzzle インスタンスの子オブジェクトに「選択済み」フラグが設定されます)。

スワイプにより、パズル オブジェクトの oneFingerSwipe メソッドが正しく呼び出されましたが、何らかの理由で、どのオブジェクトがタップによって「選択」されたかを検出できませんでした。

よく見てみると、singleTap にステップインしたときに、Control シングルトンによって呼び出された oneFingerSwipe メソッドにステップインしたときに表示されるアドレスとは異なるパズル オブジェクトのアドレスが表示されていることに気付きました。

つまり、なんらかの理由で、コントロール シングルトンは、通知の送信先 (またはその逆) のオブジェクトのツイン インスタンスで動作しています。各インスタンス内のすべてのオブジェクトは、それぞれのツインとは異なるメモリ アドレスを持っているようです。

その結果、oneFingerSwipe が呼び出されたときに、子オブジェクトで「選択済み」フラグが更新されていないため、スワイプのロジックが呼び出されません。

スワイプの通知を使用するように切り替えたとき、問題はなくなりました。

ここで何が起こっているのかよくわかりません。パズル インスタンスを oneFingerSwipe プロパティに割り当てると、Control シングルトンは Puzzle インスタンスのコピーを作成しますか?

私は Objective C を使って約 6 か月しか経っていませんが、理解できないことがたくさんあります。

テキストの壁を押し進めていただきありがとうございます。時間を割いてご覧いただきありがとうございます。

4

1 に答える 1

0

ダイレクト メソッド呼び出しが、NSNotificationCenter が通知をプッシュしていたオブジェクトとは別のオブジェクトで発生しているように見えた理由は、実際には別のオブジェクトだったからです。ループ内で多数の異なるパズル オブジェクトをインスタンス化し、それぞれが順番に oneFingerSwipe のターゲットとして設定されていました。足を踏み入れていたとき、たまたま信じられないほど似ているが異なる 2 つのオブジェクトを見て、一方が他方のコピーであるという誤った結論に達しました。この長い質問を見て、正しい方向に向けてくれたコメント投稿者に感謝します。今のところ、NSNotifications のみを使用するように切り替えましたが、デリゲートの使用についてもう少し調べて、それが私のニーズにより適しているかどうかを確認します。

于 2013-04-01T05:52:48.510 に答える