В Objective-C право собственности на объект, а не на переменные или указатели?

В книге говорится следующее:

Итак, как вы знаете, когда объект принадлежит, и кем? Рассмотрим следующий пример:

NSString *str = [[NSString alloc] initWithString:@"Hello"];  
NSString *str2 = str;

В этом примере вы используете ключевое слово alloc для str, поэтому вы владеете str. Поэтому вам нужно освободить его, когда он больше не нужен. Однако, str2 просто указывает на str, поэтому вы не являетесь str2, что означает, что вы не нужно отпускать str2, когда вы закончите использовать его.

Я думал, что собственность - это объект, а не переменная или указатель... поэтому мы не можем сказать, что мы "владеем str" или "own str2"... у нас есть объект, на который указывает либо str или str2, и если мы используем [str release] или [str2 release], это все равно.

Другое описание:

Например, рассмотрим пример, использованный в предыдущем разделе:

NSString *str = [[NSString alloc] initWithString:@"Hello"]; 
NSString *str2 = str;
[str release];
[str2 release]; //---this is not OK as you do not own str2---

Попытка освободить str2 приведет к ошибке выполнения, поскольку вы не может освободить объект, не принадлежащий вам.

Мы можем использовать [str2 release], если он вызывается до [str release]. Если мы это сделаем, то строка [str release] вызовет ошибку, так как теперь str, а также str2 являются обманутыми указателями, и предположительно, когда release был отправлен объекту в первый раз, счетчик ссылок стал 0 и dealloc вызывается немедленно, а память освобождается функцией C free().

Правильно ли это, или, возможно, что-то еще нужно исправить?

Ответ 1

Не думайте об этом с точки зрения управления памятью, а с точки зрения владения объектами. Вы получаете право собственности на объект при его размещении, сохраняете его или копируете. Вы несете ответственность за то, что вы выбрали именно те объекты, которые у вас есть, а не другие.

В вашем примере присвоение str2 не принимает права собственности на объект, но если вам действительно нужна вторая "владеющая" ссылка на него, то вы должны сделать [str2 retain], после чего это не ошибка сделать [str release]; [str2 release];. Это также будет происходить автоматически с использованием ARC, если вы не аннулировали str2 как слабую ссылку. (Конечно, в этом простом случае ненужное сохранение/освобождение может быть оптимизировано внутри компилятором.)

Ответ 2

Ваша догадка правильная: в книге используется нечеткий язык (хотя последствия правильны), чтобы упростить указатели:

У вас есть объект, на который указывают str и str2.

Это, конечно, означает, что вы можете только один раз отпустить объект (или, скорее, часто его сохранить - один раз в вашем примере, неявно, на alloc), и делаете ли вы это через str или str2 (или любые другие средства) является незначительным.

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

Хорошая практика (но не обязательно), чтобы установить все переменные, которые затем указывают на освобожденный экземпляр до nil.

Вникать в технические детали (которые действительно должны рассматриваться как то, что они есть: детали реализации, артефакты частного API):

Технически никто не владеет объектом. У объекта есть счетчик количества раз, когда он был сохранен (что вы можете узнать, вызвав anObject retainCount - но вы не должны, не в последнюю очередь потому, что у некоторых объектов есть фиктивный keepCount, и потому что это действительно не ваша проблема), Когда объект alloc ed, его keepCount равен 1. Каждый раз, когда он отправляется retain ( "он сохраняется" ), его keepCount увеличивается на 1, и каждый раз, когда он отправляется release ( "это выпущенный" ), его значение сохраняется на 1.

Как только объект сохранения достигнет нуля, он освобождается (и вызывается его метод dealloc).

Кто отправил все те сообщения retain/release (и через какие переменные) не важно.

Снова: это детали реализации. Они являются артефактами способа Objective-C/Cocoa управления памятью. Относитесь к ним, как к любому частному API: хорошо быть любопытным, но никогда не полагаться на внутренности. Только когда-либо используйте публичный API (в данном случае, retain/release и пулы автоопределений) в производственном коде.

ПРИМЕЧАНИЕ. Некоторые объекты (например, некоторые синглтоны) переопределяют методы сохранения/выпуска по умолчанию. Никогда не доверяйте retainCount, который вы получаете от объекта, за исключением любопытства (например, посмотрите на saveCount [UIColor clearColor]).

Для получения дополнительной информации по этой теме, этот вопрос, и его ответы, вероятно, являются хорошим резюме/отправной точкой.

Тем не менее, рассмотреть возможность переключения на ARC, что избавит вас от почти всех проблем с управлением памятью.

Ответ 3

Я думал, что собственность - это объект, а не переменная или указатель... поэтому мы не можем сказать, что мы "владеем str" или "own str2"... у нас есть объект, который на которые указывает либо str, либо str2, и если мы используем [str release] или [str2 релиз], это все равно.

Это правильно.

Я думаю, что автор означает "мы владеем str" и что вы владеете этим экземпляром строки. Не указатель или переменная. Теоретически вы можете освободить объект, используя другой указатель. Но, как правило, лучше выслать эту идею, используя переменную, которую вы использовали для инициализации объекта.

Ответ 4

Я думаю, что другие ответы неверны или неполны.

Собственность определяется тем, кто выпускает ресурс. Объект не имеет (обычно) собственного, он принадлежит.

Снова: владелец - это тот, кто несет ответственность за освобождение памяти.

В вашем коде str объявила о своем владении объектом, str2 не имеет. Чтобы str2 также владел объектом (чтобы передать str s право собственности), вам нужно retain it:

[str2 retain];

Теперь вы можете сказать,

[str2 release];

отказаться от претензии str2 s в отношении собственности, а также для str.

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

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

Концепция слабого указателя не имеет смысла, если объекты принадлежат сами (поскольку все указатели будут слабыми).

Ответ 5

Если вы отпустите либо str, либо str2, точка памяти str и str2 указывает на освобождение. Таким образом, вам нужно только позвонить str или str2, а не обе, потому что они указывают на то же место в памяти.

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