1

简短版:

我遇到了一个问题,即在一个对象的不同实例上调用了一个方法,而不是 NSNotificationCenter 将通知推送到(尽管只创建了一个实例)。

长版:

我有一个由 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 方法时显示的地址不同。

如此有效,出于某种原因,Control 单例正在对通知发送到的对象的孪生实例进行操作(或相反)。每个实例中的所有对象似乎都具有与相应双胞胎不同的内存地址。

因此,当调用 oneFingerSwipe 时,子对象上的“选定”标志尚未更新,因此不会调用滑动逻辑。

当我切换到使用通知进行滑动时,问题就消失了。

我真的不明白这里发生了什么。当我将拼图实例分配给 oneFingerSwipe 属性时,控制单例是否会创建拼图实例的副本?

我使用Objective C 只工作了大约6 个月,还有很多我不明白的地方。

感谢您通过那堵文字墙,感谢您抽出时间来看看。

4

1 回答 1

0

看起来直接方法调用发生在与 NSNotificationCenter 将通知推送到的对象不同的对象上的原因是,它实际上是一个不同的对象。我在一个循环中实例化了许多不同的拼图对象,而每个拼图对象又将自己设置为 oneFingerSwipe 的目标。当我逐步浏览时,我碰巧看到了两个非常相似但不同的物体,并错误地得出了一个是另一个的副本的结论。非常感谢评论者花时间研究这个冗长的问题并为我指明正确的方向。现在我已经切换到只使用 NSNotifications,但我将更多地阅读使用委托,看看它是否更适合我的需要。

于 2013-04-01T05:52:48.510 回答