当我遇到意外崩溃时,我试图准备一个使用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
答案 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];
,它会删除通知中心的所有自我引用。