Как self передается методам (или как предотвратить сильные циклы ссылок)

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

@property (strong, nonatomic) NSOperationQueue *queue;

- (void)methodA {
    __weak id *weakSelf = self;
    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        [weakSelf methodB];
    }];
    [self.queue addOperation:operation];
}

Но что, если methodB выглядит так:

- (void)methodB {
    [self someOtherMethod];
}

Будет ли это по-прежнему вызывать сильный эталонный цикл? Или метод B получит слабую ссылку на себя от метода А в качестве ссылки на себя? (То есть, является ли ссылка метода B на себя просто строгой ссылкой на слабую ссылку из метода A?)


person aus    schedule 10.12.2012    source источник
comment
Как это ссылочный цикл, если используется только один объект?   -  person trojanfoe    schedule 10.12.2012
comment
Мое понимание таково: если бы блок имел сильную ссылку на себя, а не слабую, то объект, на который ссылается я, не мог бы быть освобожден, потому что блок будет содержать ссылку на него. Блок, с другой стороны, не может быть освобожден, потому что на него ссылается self (через очередь). Вопрос в том, повторно ли метод B с синтаксически строгой ссылкой на self повторно вводит этот цикл, даже если ссылка блока слабая.   -  person aus    schedule 10.12.2012
comment
возможно, вы правы, однако это не freed, а released (сохранить счет--), поэтому, когда блок завершится, объект будет уничтожен, так что вероятно все будет в порядке.   -  person trojanfoe    schedule 10.12.2012
comment
Я думаю, вы могли бы сказать освобождено вместо освобождения. Я имел в виду удаление объекта из памяти после того, как счетчик сохранения достиг нуля.   -  person aus    schedule 10.12.2012
comment
@trojanfoe Предположим, что очередь приостанавливается и все сильные ссылки на объект (на которые ссылается сам), кроме тех, которые находятся внутри блока, удаляются.   -  person aus    schedule 10.12.2012
comment
Но после возобновления очереди вы бы хотели, чтобы эти объекты существовали, не так ли?   -  person trojanfoe    schedule 10.12.2012
comment
Не было бы никакого способа возобновить очередь, если на содержащий объект больше не ссылается никто, кроме приостановленных блоков. Но это не моя проблема. Мой вопрос носит более общий характер: действительно ли слабая ссылка препятствует циклу сильной ссылки, даже если метод B ссылается на строго ссылочную личность?   -  person aus    schedule 10.12.2012
comment
Включите предупреждения компилятора, и компилятор скажет вам, где именно ваш код неверен.   -  person gnasher729    schedule 03.02.2015


Ответы (2)


Или метод B получит слабую ссылку на себя от метода А в качестве ссылки на себя? (То есть, является ли ссылка метода B на себя просто строгой ссылкой на слабую ссылку из метода A?)

Метод не "получает" сильную или слабую ссылку. Получает ссылку. «Сильный» или «слабый» применяются только к переменной (обычно это переменная экземпляра или переменная, захваченная блоком).

weakSelf — слабая ссылка в блоке. Поскольку weakSelf является обнуляемой слабой ссылкой (__weak), либо она указывает на допустимый объект, либо ее значение равно nil. Если первое, оно указывает на действительный объект, и для него вызывается methodB. Если последнее, отправка сообщения nil ничего не делает, поэтому ничего не происходит.

Вы спрашиваете, является ли self в methodB сильной ссылкой. Сильная ссылка означает, что она сохраняется, поэтому, если self была сильной ссылкой в ​​methodB, это означает, что self сохраняется в начале метода и освобождается в конце. Но какая разница, сохраняет ли метод такой аргумент, как self? Циклы сохранения ссылаются на объекты, строго ссылающиеся друг на друга. Функция или метод запустится, а затем остановится; любое сохранение, которое они делают с локальными переменными, должно быть временным в соответствии с правилами управления памятью и не влиять на циклы сохранения. (Технический ответ: нет, self не сохраняется в ARC, а аргументы, включающие self, обычно никогда не сохраняются в MRC. Но, как я уже сказал, это действительно не имеет значения.)

person newacct    schedule 10.12.2012
comment
Это не совсем так. В ARC self является строгой ссылкой. Однако в качестве оптимизации при вызове данного метода ARC просто заимствует ссылку вызывающей стороны на self вместо того, чтобы сохранять/освобождать ее. Это означает, что вы можете непреднамеренно освободить self во время его использования, заставив единственную оставшуюся ссылку на него выйти за пределы области действия (не считая переменной self), но это небольшая цена за выигрыш в производительности, заключающийся в том, что вы не сохраняете/выпускаете self все. время. - person Lily Ballard; 11.12.2012
comment
@KevinBallard: это именно то, что я бы назвал несильной ссылкой. Для любого другого параметра объекта функции, которая является строгой ссылкой в ​​ARC, он не может быть непреднамеренно освобожден таким образом. - person newacct; 11.12.2012
comment
Теоретически это сильная ссылка. Тот факт, что он не поддерживает активное сохранение на self, является просто оптимизацией. ARC также может избегать ложного сохранения/освобождения других сильных переменных, если он может доказать, что они не нужны (например, один и тот же объект находится в двух переменных с одинаковым временем жизни, и компилятор знает об этом, поэтому на самом деле нужен только один сохранить). - person Lily Ballard; 11.12.2012
comment
@KevinBallard: это не оптимизация, если она ведет себя по-другому. С другими сильными переменными ARC сохраняет их, если не может доказать, что они не нужны (поэтому поведение неотличимо от того, если бы оно сохранялось всегда). С self ARC явно никогда не сохраняет его, даже если не может доказать, что он не нужен. Таким образом, поведение с self неотличимо от чего-то небезопасного_неудержанного. - person newacct; 11.12.2012
comment
Нет, это не так. Тривиальный контрпример: self = nil фактически освобождает старое значение self. Поведение вокруг self, когда он постоянно не сохраняется/не освобождается, считается оптимизацией, которая, к сожалению, дает возможность уничтожить self, когда он не используется, но это необходимо для хорошей производительности. - person Lily Ballard; 12.12.2012
comment
@KevinBallard: self = nil разрешено только в init, то есть ns_consumes_self, так что это явно другое. Я говорю о любом нормальном методе. Вы продолжаете говорить об оптимизации, но оптимизация по определению — это то, что не влияет на поведение, определенное в спецификации, например. не сохраняя объектный аргумент, потому что он может доказать, что он не имеет значения. Таким образом, сильный аргумент объекта не может быть случайно уничтожен. Спецификация ARC специально говорит, что self никогда не сохраняется, даже если это может быть небезопасно. - person newacct; 12.12.2012
comment
@KevinBallard: и вообще, я только что сказал, что self не сохраняется, с чем ты должен согласиться. И все это не имеет отношения к делу, потому что смысл всего ответа в том, сохраняется ли он или нет, не имеет отношения к вопросу. - person newacct; 12.12.2012

Наконец-то я нашел часть документации, отвечающая на этот вопрос. Таким образом, действительно похоже, что methodB получает слабую ссылку блока на себя.

person aus    schedule 10.12.2012
comment
Неа. Любое чтение из слабой ссылки оценивается как objc_loadWeak(ref), возвращающее автоматически освобожденную ссылку на данную переменную, или, возможно, как objc_loadWeakRetained(ref), возвращающее сильную ссылку. В любом случае метод вызывается для самого объекта, а не для слабой ссылки. - person Lily Ballard; 11.12.2012