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

IOS Правильное использование @weakify (self) и @strongify (self)

Я начинаю интегрировать libextobjc (https://github.com/jspahrsummers/libextobjc) в свое приложение iOS, прежде всего, для использования EXTScope @strongify и @weakify, но несколько вопросов, прежде чем переходить слишком глубоко в процесс.

Вот пример, который преднамеренно чрезмерно-сложный, чтобы попытаться понять, как справиться с этим:

- (void)someMethod {
    if (self.someBOOL) {
        _someObjectInstanceVar = [Object objectWithCompletionHandler:^{
            // self reference #1
            if (self.someProperty) {
                // self reference #2
                [[Async HTTPRequest] sendAWithID:self.property.id completionHandler:^(void (^)(NewObject *newObject) {
                    // self reference #3
                    [self showViewWithObject:newObject handler:^{
                        // self reference #4
                        [self reloadData];
                    }];
                }];
            }
        }];

    else {
        [[Async HTTPRequest] sendBWithID:self.property.id completionHandler:^{
            // self reference #5
            [self reloadData];
        }];
    }
}

Я понимаю, что если я хочу сделать что-либо вроде асинхронного HTTP-запроса, а внутри справки self-обработчика завершения, например [self reloadData], мне не нужно ничего делать с сильным/слабым, поскольку сам блок запроса isn сохраняя блок завершения, поэтому нет проблем с циклами сохранения. В приведенном выше примере кода я думаю, что # 5 - это случай, когда циклы сохранения не являются проблемой.

Основная проблема - все объекты, которые принимают блок как параметр property/init, которые внутренне удерживают свойства блока. Внутри метода objectWithCompletionHandler, где someObject удерживается в блоке completeHandler как переменная экземпляра, есть несколько ссылок на self, которые, как я знаю, вызовут утечку. Мой главный вопрос в таком случае, как вам нужно обрабатывать weakify и strongify, чтобы сделать его "более безопасным"? Достаточно ли одного вызова @weakify и @strongify, как показано ниже:

- (void)someMethod {
    @weakify (self);

    _someObjectInstanceVar = [Object objectWithCompletionHandler:^{
        @strongify(self);
    ...
}

Будет ли приведенная выше ссылка @strongify(self) достаточной для использования для самостоятельных ссылок # 1, 2, 3 и 4, или мне нужно (и даже работать) получить новую слабую/сильную ссылку для использования внутри sendAWithID и вложенный reloadData?

РЕДАКТИРОВАТЬ: исправленный код, чтобы вопрос стал более понятным и исправить некоторые синтаксические ошибки.

4b9b3361

Ответ 1

Как работает @strongify

После вызова @strongify, self будет иметь другой адрес указателя внутри блока, чем вне блока. Это потому, что @strongify каждый раз объявляет новую локальную переменную self. (Вот почему он подавляет предупреждение -Wshadow, которое будет "предупреждать, когда локальная переменная затеняет другую локальную переменную".) Стоит прочитать и понять реализацию эти функции. Поэтому, хотя имена одинаковы, рассматривайте их как отдельные ссылки strong.

Используя @strongify в вашем коде

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

- (void)someMethod {
    if (self.someBOOL) {
        @weakify(self);
        _someObjectInstanceVar = [Object objectWithCompletionHandler:^{
            @strongify(self);
            // self reference #1
            if (self.someProperty) {
                @weakify(self);
                // self reference #2
                [[Async HTTPRequest] sendAWithID:self.property.id completionHandler:^(void (^)(NewObject *newObject) {
                    @strongify(self);
                    // self reference #3
                    @weakify(self);
                    [self showViewWithObject:newObject handler:^{
                        // self reference #4
                        @strongify(self);
                        [self reloadData];
                    }];
                }];
            }
        }];
    // etc…
}

Однако помните, что после первого использования @strongify, self будет ссылаться на локальные переменные стека. Обычно они будут уничтожены, когда область, в которой они определены, заканчивается (если вы не храните их в свойствах или не используете их во вложенном блоке). Поэтому, основываясь на коде, который вы показали, вам нужно только после // self reference #1.

См. также

Чтение unit test, охватывающее @weakify и @strongify, поможет прояснить правильное использование этих функций.

Ответ 2

Чтобы ответить на вопрос о том, работает ли несколько экземпляров weakify/strongify на каждом вложенном уровне ваших блоков, да, да. Но нет необходимости делать это, потому что ваше первое определение @strongify уже определяет self для всей внутренней области вашего блока (и блоков, которые в нем вложены).

Однако, учитывая, что ваши блоки имеют разные сроки жизни, вы можете добавить @strongify для каждого вложенного блока, чтобы убедиться, что все они сохраняют свой собственный цикл сохранения в своей внутренней области.

Здесь поток ответов github, который объясняет этот случай: https://github.com/jspahrsummers/libextobjc/issues/45

Ответ 3

Вызов "self" внутри блока, который удерживается "self" , приведет к "Сохранению циклов" и, следовательно, утечкам памяти. В идеале это выглядит так:

@interface A: NSObject // Some interface A

@property (nonatomic, copy, readwrite) dispatch_block_t someBlock; // See block is strongly retained here.

@end

*******************************************************
@implementation A

- (void) someAPI
{
    __weak A * weakSelf = self; // Assign self to weakSelf and use it
    // enter code here inside block to break retain cycles.
    self.someBlock = 
    ^{
        A * strongSelf = weakSelf; // Assign weak self to strongSelf before
       // using it. This is because weakSelf can go nil anytime and it may happen
       // that only few lines from block get executed before weakSelf goes nil,
       // and hence code may be in some bad state.
        if (strongSelf != nil)
        {
            // Use strongSelf.
            [strongSelf doSomethingAwesome];
            [strongSelf doSomethingAwesomeAgain];
        }
    };
}

@end

Если блок не удерживается "self" , тогда его безопасно использовать "я" внутри блоков, и они не будут создавать циклы сохранения.

Примечание. Концепция управления памятью остается прежней с использованием библиотеки libextobjc.