Цель C: Где удалить наблюдателя для NSNotification?

У меня есть объективный класс C. В нем я создал метод init и настроил NSNotification в нем

//Set up NSNotification
[[NSNotificationCenter defaultCenter] addObserver:self 
                                         selector:@selector(getData)
                                             name:@"Answer Submitted"
                                           object:nil];

Где установить [[NSNotificationCenter defaultCenter] removeObserver:self] в этом классе? Я знаю, что для UIViewController я могу добавить его в метод viewDidUnload Итак, что нужно сделать, если я только что создал объектный класс c?

Ответ 1

Общий ответ будет "как только вам больше не понадобятся уведомления". Это, очевидно, не удовлетворительный ответ.

Я бы рекомендовал, чтобы вы добавили вызов [notificationCenter removeObserver: self] в метод dealloc тех классов, которые вы намереваетесь использовать в качестве наблюдателей, так как это последний шанс незарегистрировать наблюдателя. Это, однако, защитит вас только от сбоев из-за уведомления центра уведомлений о мертвых объектах. Он не может защитить ваш код от получения уведомлений, когда ваши объекты еще не находятся/не находятся в состоянии, в котором они могут надлежащим образом обрабатывать уведомление. Для этого... См. Выше.

Изменить (так как ответ, кажется, делает больше комментариев, чем я думал). Все, что я пытаюсь сказать здесь, - это очень сложно дать общий совет относительно того, когда лучше удалить наблюдатель из центра уведомлений, потому что это зависит:

  • В вашем случае использования (Какие уведомления соблюдаются? Когда они отправляются?)
  • Реализация наблюдателя (когда он готов получать уведомления? Когда он больше не готов?)
  • Предполагаемый срок службы наблюдателя (привязан ли он к другому объекту, скажем, к контроллеру вида или представления?)
  • ...

Итак, лучший общий совет, который я могу придумать: защитить ваше приложение. против хотя бы одного возможного сбоя, танцуйте removeObserver: в dealloc, так как это последняя точка (в жизни объекта), где вы можете сделать это чисто. Это не значит: "просто отложите удаление до тех пор, пока не будет вызвано dealloc, и все будет хорошо". Вместо этого удалите наблюдателя , как только объект больше не будет готов (или требуется) для получения уведомлений. Точный правильный момент. К сожалению, не зная ответов на любой из упомянутых выше вопросов, я даже не могу догадаться, когда это будет.

Вы всегда можете безопасно removeObserver: объект несколько раз (и все, кроме самого первого вызова с данным наблюдателем, будут nops). Итак: подумайте об этом (снова) в dealloc, чтобы быть уверенным, но в первую очередь: сделайте это в соответствующий момент (который определяется вашим прецедентом).

Ответ 2

Примечание. Это было протестировано и работает на 100%

Swift

override func viewWillDisappear(animated: Bool){
    super.viewWillDisappear(animated)

    if self.navigationController!.viewControllers.contains(self) == false  //any other hierarchy compare if it contains self or not
    {
        // the view has been removed from the navigation stack or hierarchy, back is probably the cause
        // this will be slow with a large stack however.

        NSNotificationCenter.defaultCenter().removeObserver(self)
    }
}

PresentedViewController

override func viewWillDisappear(animated: Bool){
    super.viewWillDisappear(animated)

    if self.isBeingDismissed()  //presented view controller
    {
        // remove observer here
        NSNotificationCenter.defaultCenter().removeObserver(self)
    }
}

Objective-C

В iOS 6.0 > version лучше удалить наблюдателя в viewWillDisappear, поскольку метод viewDidUnload устарел.

 [[NSNotificationCenter defaultCenter] removeObserver:observerObjectHere];

Во много раз лучше remove observer, когда представление было удалено из navigation stack or hierarchy.

- (void)viewWillDisappear:(BOOL)animated{
 if (![[self.navigationController viewControllers] containsObject: self]) //any other hierarchy compare if it contains self or not
    {
        // the view has been removed from the navigation stack or hierarchy, back is probably the cause
        // this will be slow with a large stack however.

        [[NSNotificationCenter defaultCenter] removeObserver:observerObjectHere];
    }
}

PresentedViewController

- (void)viewWillDisappear:(BOOL)animated{
    if ([self isBeingDismissed] == YES) ///presented view controller
    {
        // remove observer here
        [[NSNotificationCenter defaultCenter] removeObserver:observerObjectHere];
    }
}

Ответ 4

Если наблюдатель добавлен в контроллер представления, я настоятельно рекомендую добавить его в viewWillAppear и удалить его в viewWillDisappear.

Ответ 5

-(void) dealloc {
      [[NSNotificationCenter defaultCenter] removeObserver:self];
      [super dealloc];
}

Ответ 6

В общем, я поместил его в метод dealloc.

Ответ 7

* edit: Этот совет относится к iOS <= 5 (даже там вы должны добавлять в viewWillAppear и удалять в viewWillDisappear), однако совет применяется, если по какой-то причине вы добавили наблюдателя в viewDidLoad)

Если вы добавили наблюдателя в viewDidLoad, вы должны удалить его как в dealloc, так и в viewDidUnload. В противном случае вы добавите его дважды, когда viewDidLoad вызывается после viewDidUnload (это произойдет после предупреждения о памяти). Это не обязательно в iOS 6, где viewDidUnload устарел и не будет вызван (поскольку представления больше не будут автоматически выгружены).

Ответ 8

По моему мнению, следующий код не имеет смысла в ARC:

- (void)dealloc
{
      [[NSNotificationCenter defaultCenter] removeObserver:self];
      [super dealloc];
}

В iOS 6 также нет смысла удалять наблюдатели в viewDidUnload, потому что теперь он устарел.

Подводя итог, я всегда делаю это в viewDidDisappear. Однако, это также зависит от ваших требований, как сказал @Dirk.

Ответ 9

При быстром использовании deinit, поскольку dealloc недоступен:

deinit {
    ...
}

Документация Swift:

Деинициализатор вызывается непосредственно перед экземпляром класса перераспределена. Вы пишете деинициализаторы с ключевым словом deinit, аналогичным о том, как intializers записываются с ключевым словом init. Deinitializers доступны только для типов классов.

Обычно вам не нужно выполнять ручную очистку, когда экземпляры освобождаются. Однако, когда вы работаете со своими собственными ресурсов, вам может потребоваться выполнить дополнительную очистку сам. Например, если вы создаете пользовательский класс для открытия файла и напишите на него некоторые данные, вам может потребоваться закрыть файл до экземпляр класса освобождается.

Ответ 10

Я думаю, что нашел надежный ответ! Я должен был, так как ответы выше неоднозначны и кажутся противоречивыми. Я просмотрел Cookbooks и руководства по программированию.

Во-первых, стиль addObserver: в viewWillAppear: и removeObserver: в viewWillDisappear: не работает для меня (я его тестировал), потому что я отправляю уведомление в контроллере дочернего представления для выполнения кода в родительском просмотрите контроллер. Я бы использовал этот стиль только в том случае, если я отправлял и слушал уведомление в одном и том же контроллере.

Ответ, на который я опишу больше всего, я нашел в iOS Programming: Big Nerd Ranch Guide 4th. Я верю парням BNR, потому что у них есть учебные центры iOS, и они не просто пишут другую кулинарную книгу. Вероятно, в их интересах быть точным.

Пример BNR: addObserver: в init:, removeObserver: в dealloc:

Пример BNR: addObserver: в awakeFromNib:, removeObserver: в dealloc:

... при удалении наблюдателя в dealloc: они не используют [super dealloc];

Надеюсь, это поможет следующему человеку...

Я обновляю этот пост, потому что Apple теперь почти полностью ушла со Storyboards, поэтому вышеупомянутое может не применяться ко всем ситуациям. Важная вещь (и причина, по которой я добавил этот пост в первую очередь), - это обратить внимание, если ваш viewWillDisappear: получает вызов. Это было не для меня, когда приложение вошло в фолд.

Ответ 11

Принятый ответ небезопасен и может вызвать утечку памяти. Пожалуйста, оставьте незарегистрировать в dealloc, но также отмените регистрацию в viewWillDisappear (это, конечно, если вы зарегистрируетесь в viewWillAppear).... ЧТО Я ЧТО Я ЛЮБЛЮ, И ЭТО РАБОТАЕТ ВЕЛИКОЕ!:)

Ответ 12

Важно также заметить, что viewWillDisappear вызывается также, когда контроллер представления представляет новый UIView. Этот делегат просто указывает, что основной вид контроллера просмотра не отображается на дисплее.

В этом случае освобождение уведомления в viewWillDisappear может быть неудобным, если мы используем уведомление, чтобы позволить UIview связываться с родительским контроллером представления.

В качестве решения я обычно удаляю наблюдателя одним из этих двух методов:

- (void)viewWillDisappear:(BOOL)animated {
    NSLog(@"viewController will disappear");
    if ([self isBeingDismissed]) {
        NSLog(@"viewController is being dismissed");
        [[NSNotificationCenter defaultCenter] removeObserver:self name:@"actionCompleted" object:nil];
    }
}

-(void)dealloc {
    NSLog(@"viewController is being deallocated");
    [[NSNotificationCenter defaultCenter] removeObserver:self name:@"actionCompleted" object:nil];
}

По тем же причинам, когда я выдаю уведомление в первый раз, мне нужно учитывать тот факт, что в любое время, когда появляется надпись над контроллером, тогда viewWillAppear метод запускается. Это, в свою очередь, будет генерировать несколько копий одного и того же уведомления. Поскольку нет способа проверить, является ли уведомление уже активным, я устраняю проблему, удалив уведомление перед его добавлением:

- (void)viewWillAppear:(BOOL)animated {
    NSLog(@"viewController will appear");
    // Add observers
    [[NSNotificationCenter defaultCenter] removeObserver:self name:@"imageGenerated" object:nil]; // This is added to avoid duplicate notifications when the view is presented again
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receivedImageFromCameraOrPhotolibraryMethodOnListener:) name:@"actionCompleted" object:nil];

}

Ответ 13

SWIFT 3

Существует два случая использования уведомлений: - они нужны только тогда, когда на экране установлен контроллер вида; - они необходимы всегда, даже если пользователь открыл другой экран по току.

В первом случае правильное место для добавления и удаления наблюдателя:

/// Add observers
///
/// - Parameter animated: the animation flag
override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    NotificationCenter.default.addObserver(...)
}

/// Remove observers
///
/// - Parameter animated: the animation flag
override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    NotificationCenter.default.removeObserver(self)
}

для второго случая правильный способ:

/// Add observers
override func viewDidLoad() {
    super.viewDidLoad()
    NotificationCenter.default.addObserver(...)
}

/// Remove observers
///
/// - Parameter animated: the animation flag
override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    if self.isBeingDismissed // remove only when view controller is removed disappear forever
    || !(self.navigationController?.viewControllers.contains(self) ?? true) {
        NotificationCenter.default.removeObserver(self)
    }
}

И никогда не ставьте removeObserver в deinit{ ... } - это MISTAKE!

Ответ 14

override func viewDidLoad() {   //add observer
  super.viewDidLoad()
  NotificationCenter.default.addObserver(self, selector:#selector(Yourclassname.method), name: NSNotification.Name(rawValue: "NotificationIdentifier"), object: nil)
}

override func viewWillDisappear(_ animated: Bool) {    //remove observer
    super.viewWillDisappear(true)
    NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: "NotificationIdentifier"), object: nil)
}