为什么使用与NSNotificationCenter不同的实例对象从单例调用方法?

时间:2013-04-01 02:34:51

标签: ios singleton nsnotifications

简短版本:

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

长版:

我有一个“Puzzle”对象实例,由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实例的子对象上设置'selected'标志)。

正确滑动调用拼图对象上的oneFingerSwipe方法,但由于某种原因无法检测到哪个对象已被“选中”。

仔细观察之后,我注意到当我进入singleTap时,我会看到拼图对象的地址,该地址与步入Control单例调用的oneFingerSwipe方法时显示的地址不同。

因此,有效地,Control单例操作在发送通知(或反向)的对象的双实例上。每个实例中的所有对象似乎都有不同的内存地址。

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

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

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

我只使用Objective C工作了大约6个月,并且有很多我不明白的事情。

感谢您浏览文字墙,感谢您抽出时间来观看。

1 个答案:

答案 0 :(得分:0)

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