Подтвердить что ты не робот

Использование слабой функции self в функции dispatch_async

Я прочитал много сообщений об использовании __weak self внутри dispatch_async, и теперь я немного перепутал бит.

если у меня есть:

self.myQueue = dispatch_queue_create("com.biview.core_data", NULL);

dispatch_async(self.myQueue, ^(void){
    if (!self.var1) {
        self.var1 = ...;
    }
    dispatch_async(dispatch_get_main_queue(), ^(void) {
        if ([self.var2 superview]) {
            [self.var2 removeFromSuperview];
        }

        [self.Label setText:text];
    });
});

Мне нужно использовать __weak self. Поскольку я читал, что в некоторых случаях dispatch_async не требуется a __weak self.

Смотрите последний комментарий здесь

4b9b3361

Ответ 1

Предполагая, что self является указателем объекта на UIViewController.

Что нужно учитывать:

  • UIViewController является объектом "UIKit". Объекты UIKit не должны отправляться методами в неосновных потоках, то есть эти методы должны выполняться только в основном потоке!

  • Блок, который был помещен в очередь - будь то синхронно или асинхронно - в конечном итоге будет выполнен - несмотря ни на что! Ну, если программа не завершится до того, как это произойдет.

  • Захваченные сохраняемые сильные указатели будут сохранены, когда блок будет скопирован (например, при асинхронной отправке), и снова освобождены, когда блок будет уничтожен (после его завершения).

  • Захваченные сохраняемые слабые указатели НЕ будут сохранены и не освобождены.

В вашем сценарии, когда вы фиксируете себя в блоке, который отправляется в главную очередь, вам не нужно беспокоиться о том, что случится что-то плохое.

Итак, почему? А что происходит на самом деле?

Поскольку self будет захвачено в блоке, который отправляется асинхронно, self будет неявно сохранено и освобождено снова, когда блок будет завершен.

Это означает, что время жизни себя продлится до окончания блока. Обратите внимание, что ваш второй блок отправляется в основной поток, и он гарантировал, что self все еще живо, когда этот блок исполняется.

Эта "расширенная жизнь", описанная выше, может быть желательной функцией вашей программы.

Если вы явно не хотите продлевать время жизни объекта UIViewController и вместо этого хотите, чтобы блок - когда он наконец выполняется - проверил, существует ли этот объект UIViewController вообще, вы можете использовать __weak указатель self. Обратите внимание, что блок в конечном итоге исполняется, независимо от того, UIViewController ли UIViewController или он был освобожден за это время.

Вы можете захотеть, чтобы блок ничего не делал, если UIViewController был освобожден до того, как блок будет выполнен:

MyController* __weak weakSelf = self;
dispatch_async(queue, ^{
    MyController* strongSelf = weakSelf;
    if (strongSelf) {
        ...
    }
    else {
       // self has been deallocated in the meantime.
    }
});

См. Также: Переход на заметки о выпуске ARC.

Помните: объекты UIKit не должны отправляться методами в неосновных потоках!

Еще одна тонкая ошибка может возникнуть из-за того, что объекты UIKit должны выполнять методы только в основном потоке.

Это может быть нарушено, если блок захватывает объект UIKit который отправляется асинхронно и выполняется в неосновном потоке. Тогда может случиться, что блок содержит последнюю сильную ссылку на этот объект UIKit. Теперь, когда блок в конце концов будет выполнен, блок будет уничтожен, а объект UIKit будет освобожден. Поскольку это последняя сильная ссылка на объект UIKit, он будет освобожден. Однако это происходит в потоке, в котором был выполнен блок - и это не основной поток! Теперь плохие вещи могут (и обычно будут) происходить, поскольку метод dealloc по-прежнему является методом, отправляемым объекту UIKit.

Вы можете избежать этой ошибки, отправив блок, фиксирующий сильный указатель на этот объект UIKit, и отправьте ему фиктивный метод:

UIViewController* strongUIKitPointer = ... 
dispatch_async(non_main_queue, ^{
    ... // do something 
    dispatch(dispatch_get_main_queue(), ^{
        [strongUIKitPointer self];  // note: self is a method, too - doing nothing
    });
});

Однако в вашем сценарии последняя сильная ссылка может быть только в блоке, который выполняется в главном потоке. Таким образом, вы защищены от этой тонкой ошибки. ;)

Редактировать:

В вашей настройке у вас никогда не будет цикла сохранения. Цикл сохранения происходит, если сохраняемый объект A строго ссылается на другой сохраняемый объект B, а объект B строго ссылается на A. Обратите внимание, что "Блок" также является сохраняемым объектом.

Придуманный пример с циклической ссылкой:

typedef void(^my_completion_block_t)(NSArray* result);

@interface UsersViewController : UIViewController
@property (nonatomic, copy) my_completion_block_t completion;
@property (nonatomic) NSArray* users;
@end

Здесь у нас есть завершение свойства, тип значения которого - Block. То есть мы получаем ивар с именем _completion, типом которого является блок.

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

Неосторожный подход может случайно ввести циклическую ссылку:

Где-то в "UsersViewController.m"

self.completion = ^(NSArray* users){
    self.users = users;
}

[self fetchUsers];  // start asynchronous task

Здесь self содержит сильную ссылку на ivar _completion, который является блоком. И сам блок захватывает себя, что заставляет сохранять себя, когда блок копируется, когда он отправляется. Это классический эталонный цикл.

Чтобы избежать этой циклической ссылки, у нас есть несколько альтернатив:

  1. Использование __weak квалифицированного указателя self

    UsersViewController* __weak weakSelf = self;
    self.completion = ^(NSArray* users) {
        UsersViewController* strongSelf = weakSelf;
        if (strongSelf) {
            strongSelf.users = users;
        }
        else {
            // the view controller does not exist anymore
        }
    }   
    [usersViewController fetchUsers];
    
  2. Использование квалифицированного __block указателя self и, в конце концов, установка его nil в блоке, когда он завершится:

    UsersViewController* __block blockSelf = self;
    self.completion = ^(NSArray* users) {
        blockSelf.users = users;
        blockSelf = nil;
    }   
    [usersViewController fetchUsers];
    

См. Также: Переход на заметки о выпуске ARC.

Ответ 2

Быстрое обновление:

Пример этого так называемого сильного-слабого танца в быстром:

Swift 4.2:

func doSomeThingAsynchronously() {
    DispatchQueue.global().async {
        // Do task in default queue
        DispatchQueue.main.async { [weak self] in
            // Do task in main queue
            guard let self = self else { return }
            self.updateView()
        }
    }
}

Свифт 3 и 4:

func doSomeThingAsynchronously() {
    DispatchQueue.global().async {
        // Do task in default queue
        DispatchQueue.main.async { [weak self] in
            // Do task in main queue
            guard let strongSelf = self else { return }
            strongSelf.updateView()
        }
    }
}

Свифт 2:

func doSomeThingAsynchronously() {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { () -> () in
        // Do task in default queue
        dispatch_async(dispatch_get_main_queue(), { [weak self] () -> () in
            guard let strongSelf = self else { return }
            // Do task in main queue
            strongSelf.updateView()
        })
    }
}

Популярный проект с открытым исходным кодом Alamofire использует этот подход.

Продлите время жизни объекта, используя [слабое "я"], и защитите let strongSelf = self иначе {return}.

Для получения дополнительной информации проверьте Swift-Style-Guide