【问题标题】:Why is my NSNotificationObserver deallocated while a message runs on it?为什么我的 NSNotification Observer 在消息运行时被释放?
【发布时间】:2014-01-16 15:30:57
【问题描述】:

我有一种可能发生的情况,即在观察者处理传入通知时,对观察者的最后一个强引用被删除。

这会导致观察者立即被释放。我通常希望,当前正在运行的方法可以在对象被释放之前完成。这就是正常消息发送期间发生的情况。

代码的简化版本:

TKLAppDelegate.h:

#import <UIKit/UIKit.h>
#import "TKLNotificationObserver.h"

@interface TKLAppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) TKLNotificationObserver *observer;

@end

TKLAppDelegate.m:

#import "TKLAppDelegate.h"

@implementation TKLAppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

  // Create an observer and hold a strong reference to it in a property
  self.observer = [[TKLNotificationObserver alloc] init];

  // During the processing of this notification the observer will remove the only strong reference
  // to it and will immediatly be dealloced, before ending processing.
  [[NSNotificationCenter defaultCenter] postNotificationName:@"NotificationName" object:nil];

  // Create an observer and hold a strong reference to it in a property
  self.observer = [[TKLNotificationObserver alloc] init];

  // During the manual calling of the same method the observer will not be dealloced, because ARC still
  // holds a strong reference to the message reciever.
  [self.observer notificationRecieved:nil];

  return YES;
}

@end

TKLNotificationObserver.m:

#import "TKLNotificationObserver.h"
#import "TKLAppDelegate.h"

@implementation TKLNotificationObserver

- (id)init {
  if (self = [super init]) {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notificationRecieved:) name:@"NotificationName" object:nil];
  }
  return self;
}

- (void)notificationRecieved:(NSNotification *)notification {
  [self doRemoveTheOnlyStrongReferenceOfThisObserver];
  NSLog(@"returing from notification Observer");
}

- (void)doRemoveTheOnlyStrongReferenceOfThisObserver {
  TKLAppDelegate * delegate = [[UIApplication sharedApplication] delegate];
  delegate.observer = nil;
}

- (void)dealloc {
  [[NSNotificationCenter defaultCenter] removeObserver:self name:@"NotificationName" object:nil];
  NSLog(@"dealloc was called");
}

@end

这样使用 App Delegate 风格不好,只做演示,实际代码不涉及 App Delegate。

输出是:

 dealloc was called
 returing from notification Observer
 returing from notification Observer
 dealloc was called

在第一种情况下,dealloc 在通知处理完成之前被调用。在第二种情况下,它的行为符合我的预期。

如果我在notificationReceived 中保持对self 的强引用,则dealloc 仅在处理后发生。我的期望是,ARC、运行时或其他为我保留此强参考的人。

我的代码有什么问题? 还是我的期望有问题? 是否有任何 Apple 或 Clang 提供的文档?

【问题讨论】:

  • 对你来说是不是很奇怪,那个对象没有引用和解除分配,但是你把它从NSNotificationCenter中删除了?如果NSNotificationCenter 持有您的TKLNotificationObserver 的引用,则不应调用dealloc
  • NSNotificationCenter 对观察者没有强引用。

标签: ios objective-c automatic-ref-counting nsnotificationcenter


【解决方案1】:

我的期望是,ARC、运行时或其他任何人保留它 对我来说很有参考价值。

事实并非如此,如Clang/ARC documentation 中所述:

Objective-C 方法的 self 参数变量实际上永远不会 由实施保留。这是未定义的行为,或者至少是 危险的,在消息发送期间导致对象被释放 到那个对象。

因此,如果调用doRemoveTheOnlyStrongReferenceOfThisObserver 可能有释放self的副作用,你必须使用 避免释放的临时强引用:

- (void)notificationRecieved:(NSNotification *)notification {
   typeof(self) myself = self;
   [self doRemoveTheOnlyStrongReferenceOfThisObserver];
   NSLog(@"returing from notification Observer");
}

更好的解决方案可能会避免这种副作用。

【讨论】:

  • 这回答了我的问题。非常感谢。我没有找到那个。也许是因为我在寻找完全相反的结果。
  • 是的,你是对的。在这种情况下,我必须重新考虑我的设计。
【解决方案2】:

第一次解除分配可能发生在您两次设置 appDelegate 的观察者属性时,因此第一个实例在您第二次设置后立即解除分配

【讨论】:

  • 没错,但是两个分配之间的通知是同步运行的,并且是执行第二个分配的时间,输出的第一行已经打印出来。
猜你喜欢
  • 2015-03-10
  • 2011-05-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-17
  • 1970-01-01
  • 2023-03-28
相关资源
最近更新 更多