Objective-C对象实例化 - 被另一个类覆盖的地址(间歇性)

时间:2014-01-23 16:51:47

标签: objective-c object memory-management instantiation

当我遇到意外崩溃时,我试图准备一个使用NSNotificationCenter的简单演示,但有时只是。我有两个班,Scout和Shouter。在-init上,Shouter向NSNotificationCenter发布通知,Scout将其自身添加为相应通知的相同通知的观察者。

当应用程序崩溃时,它有“无法识别的选择器发送到实例0x100109480”(当然地址更改)。这导致我检查对象的地址,看看会发生什么。看来有时Scout init之后的Shouter init会覆盖Scout对象的地址。

代码如下。我希望能理解为什么会这样。我已经尝试删除id / idCounter机制并使用硬编码的int,以防我对静态的理解是错误的。

Shouter的相关代码:

@implementation Shouter{
    int _id;    //An ID-code to recognize different instances of Shouter.
}

-(id)init{

    NSLog(@"init test Shouter - what is self? %@", self);

    if(self=[super init]){
        static int _idCounter = 0;
        _id = _idCounter++;
        NSDictionary *dictionary = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:_id] forKey:@"ID-code"];
        [[NSNotificationCenter defaultCenter] postNotificationName:@"sample" object:nil userInfo:dictionary];
    }
    return self;
}

@end

Scout的相关代码:

@implementation Scout
-(id)init{    
    NSLog(@"init test Scout - what is self? %@", self);    
     if(self=[super init]){
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(messageReceived:)
                                                     name:@"sample"
                                                   object:nil];
    }    
    return self;
}

- (void) messageReceived:(NSNotification*)notification{
    NSDictionary *dictionary = [notification userInfo];
    id object = [dictionary objectForKey:@"ID-code"];
    int code = -1;
    if([object isKindOfClass:[NSNumber class]]){
        code = [((NSNumber*)object) intValue];
    }

    NSLog(@"Received a message from a Shouter instance.  ID-code was: %i", code);
}

@end

main的相关代码:

int main(int argc, const char * argv[])
{
    @autoreleasepool {        
        [[Shouter alloc] init];
        [[Scout alloc] init];
        [[Shouter alloc] init];        
    }
    return 0;
}

崩溃实例中的输出示例:

2014-01-23 11:38:30.800 Lab 3 Snippets[3461:303] init test Shouter - what is self? <Shouter: 0x100109480>
2014-01-23 11:38:30.802 Lab 3 Snippets[3461:303] init test Scout - what is self? <Scout: 0x1003007b0>
2014-01-23 11:38:30.803 Lab 3 Snippets[3461:303] init test Shouter - what is self? <Shouter: 0x1003007b0>
2014-01-23 11:38:30.803 Lab 3 Snippets[3461:303] -[Shouter messageReceived:]: unrecognized selector sent to instance 0x1003007b0

3 个答案:

答案 0 :(得分:1)

在取消分配对象时,应始终取消注册通知,例如:

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self name:@"sample" object:nil];
    [super dealloc]; // Skip this if you're using ARC
}

可能发生的是,您的Scout对象在其init方法完成后立即被释放,并且下一个Shooter对象在同一地址分配。 NSNotificationCenter不知道发生了这种情况,并尝试在前一个messageReceived:对象上调用Scout。相反,它会找到一个不响应该方法的Shooter对象,因此会出错。

NSNotificationCenter永远不会保留注册为观察者的对象,因为这会产生很多难以解决的保留周期。只要您需要它们存在,请确保对象保留在其中。

答案 1 :(得分:0)

猜猜ARC已经开启了。 ARC将看到该对象永远不会再次使用并立即释放它,但由于您从未取消注册通知订阅,通知中心会尝试与在其位置分配的新对象进行通信。

答案 2 :(得分:0)

您还可以在dealloc方法上使用[[NSNotificationCenter defaultCenter] removeObserver:self];,它会删除通知中心的所有自我引用。