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

Objective-C указатели?

Я новичок в кодировании и стараюсь ускориться с Objective-C. Пришел через какой-то код, который я не понял. Я надеялся, что кто-то сможет уточните это для меня. В приведенном ниже случае я не уверен, как работает * foo2 и почему он не выпущен?

ClassOne *pointer = [[ClassOne alloc]init];

ClassTwo *foo = [[ClassTwo alloc]init], *foo2; 

 foo2 = [foo add: pointer];
 [foo release]; 
 foo = foo2

[pointer release];

[foo release];
4b9b3361

Ответ 1

С Objective-C Cocoa мы работаем с полуавтоматическим управлением памятью подсчета ссылок. При распределении памяти для объекта, сохранении объекта или вызове метода copy для объекта, счетчик удержания (счетчик ссылок) увеличивается на 1. При вызове release объекта, он уменьшает количество задержек на единицу. При вызове autorelease на объекте в какой-то момент в будущем будет вызываться release в объекте (во время цикла основного запуска, когда ни один из ваших собственных кодов не выполняется, поэтому он не вытащит ссылку из под вами, когда вы пытаетесь его использовать). Когда счетчик сохранения достигает 0, объект может быть освобожден.

В общем, если вы вызываете retain на объект, вы сигнализируете о своем интересе к нему, и вы несете ответственность за вызов release или autorelease в какой-то момент, когда вы не являетесь больше интересуется объектом. Аналогично, если вы вызываете метод alloc или copy для объекта, вы сигнализировали о своем интересе к объекту и должны соответствовать ему с release или autorelease где-то по строке.

Эта ссылка в значительной степени охватывает руководящие принципы, которые Apple использует (и вы должны использовать) для управления памятью: Простые правила управления памятью в Cocoa

Пропустите код по строке:

ClassOne *pointer = [[ClassOne alloc]init];

pointer указывает на недавно выделенный объект ClassOne с сохранением числа в 1, так как мы назовем его alloc. В какой-то момент в будущем мы должны будем называть release или autorelease на pointer.

ClassTwo *foo = [[ClassTwo alloc]init], *foo2;

foo указывает на недавно выделенный объект ClassTwo с сохранением числа в 1, поскольку мы назовем его alloc. В какой-то момент в будущем мы будем называть release или autorelease на foo.

foo2 не указывает на что-либо в особенности прямо сейчас. Это небезопасно использовать.

foo2 = [foo add: pointer];

pointer был добавлен в foo (что бы это ни значило, мы не знаем реализации). foo мог бы называть retain на pointer, чтобы выразить свою заинтересованность в нем, и добавил его как поле, или он мог бы добавить pointer в коллекцию (в этом случае ответственность за сбор для вызова retain на нем, когда объект добавлен, и release, когда объект удален). В любом случае, это не влияет на наш блок кода, поэтому нам все равно, что происходит под капотом

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

В любом случае наша задача не называть release на этом объекте. Если у метода было copy в имени или если мы вызвали retain в возвращаемой ссылке (например, foo2 = [[foo add:pointer] retain];), то счет сохранения увеличился бы на 1, и мы были бы обязаны отвечать release или autorelease на нем.

[foo release];

Объект, на который ссылается foo, был освобожден, что означает, что его количество удержания уменьшилось на 1. В этом примере это пары с вызовом alloc, который мы сделали в строке 2, поэтому показатель сохранения упадет до 0, делая foo право на освобождение.

В целом, однако, нам все равно, если объект был освобожден или нет; нам просто нужно убедиться, что мы соединяем любые вызовы alloc, copy или retain с тем же числом вызовов release или autorelease. Если мы регистрируем интерес к объекту в любое время, мы несем ответственность за выпуск нашего интереса, иначе у нас будут утечки памяти.

 foo = foo2;

foo теперь указывает на тот же объект, на который ссылается foo2. Помните, что мы не вызывали метод alloc или copy, когда мы получили foo2, и мы не зарегистрировали интерес к нему, вызвав retain. Поскольку мы не несем ответственности за вызов release на foo2, мы не несем ответственности за вызов release на foo.

[pointer release];

pointer Удержание счета уменьшилось на 1. Это могло привести к тому, что счетчик его сохранения равен 0 или нет, это зависит от того, что foo сделал с ним, когда мы его добавили. Тем не менее, нам все равно. мы ответили на pointer, набрав release на нем в соответствии с вызовом alloc, который мы сделали в начале. Хотя pointer может все еще быть вокруг после этого вызова, мы не можем сделать это предположение, и попытка сделать что-либо с объектом, ранее указанным указателем, была бы ошибкой (хотя мы могли бы изменить pointer, чтобы указать на что-то другое свободно).

[foo release];

Если автор этого кода соблюдает соглашения об управлении памятью Apple, то это необязательно. Мы не несем ответственности за вызов release на foo или foo2 (они указывают на один и тот же объект, помните). Это не приведет к тому, что код break; вызывает что-либо на ссылке nil, по существу, не работает. Однако это может вызвать путаницу для тех, кто просматривает код.

Теперь автор этого кода, возможно, нарушил соглашения управления памятью. Возможно, он сделал, что вызов add возвращает копию pointer без вызова autorelease на нем, и в этом случае вызывающий отвечает за вызов release на нем. Это очень плохая форма, и если вам нужно запустить код, который нарушает соглашение об управлении памятью, документ, в котором вы его используете, и как он нарушает соглашение, чтобы избежать путаницы в будущем.

Ответ 2

ClassOne *pointer = [[ClassOne alloc]init];

Переменная pointer является указателем на объект класса ClassOne. Он присвоил значение вновь созданного объекта.

ClassTwo *foo = [[ClassTwo alloc]init], *foo2;

*foo и *foo2 являются объектами класса ClassTwo. Только foo присваивается вновь созданный объект. foo2 может указывать на что угодно, поэтому небезопасно использовать его, прежде чем назначать ему значение.

 foo2 = [foo add: pointer];

foo2 присваивается значение: Я предполагаю, что сообщение add: класса ClassTwo создает объект (подпись метода должна быть -(ClassTwo*)add:(ClassOne*);)

 [foo release];

Объект, на который указывает foo, больше не нужен.

 foo = foo2;

Переменной foo присваивается значение foo2: оба указывают на один и тот же объект.

[pointer release];

Объект, на который указывает pointer, больше не нужен.

[foo release];

Объект, на который указывает foo (а также foo2), больше не нужен.

Ответ 3

Потому что вы его не выпустили. Вы выпускаете ссылки здесь, не освобождая указателей. Это не совсем то же самое. Когда вы выполняете [foo release] во второй раз, вы выпускаете ссылку foo, которую вы создали, когда вы назначили foo2 для foo.

Чтобы освободить ссылку foo2, вам действительно нужно вызвать release по этой ссылке, а не копию ссылки.

Ответ 4

Из вашего простого примера действительно сложно сказать, что происходит. Обычно методы типа [class add:] возвращают void, поэтому они должны поднять предупреждение компилятора о том, что "значение void не игнорируется, как должно быть".

Таким образом, без дополнительной информации, это немного сложно понять.

Несколько вещей, которые нужно иметь в виду:

  • вы можете отправлять команды в 'nil' в objc. Итак, если [foo add: pointer] возвращает nil, вы можете называть его "release" весь день без каких-либо изменений.

  • keepCount - ваш друг. Вы можете вызвать его в любом NSObject, чтобы увидеть, сколько объектов держится на нем. Это также поможет вам выявить проблему.

  • Наконец, есть сборка мусора?

Ответ 5

Это действительно зависит от того, что делает [foo add:pointer];. Похоже, что он возвращает копию foo и сохраняет ее. Это явно плохой дизайн, потому что это должно быть очевидно из метода, если возвращаемый объект является копией/ссылкой. Методы с именем add: не должны возвращать копию.

Шаг за шагом:

// this somehow creates a retained copy of foo.
foo2 = [foo add:pointer];

 // foo is released and gets destroyed.
[foo release];

// makes foo point to the same object as foo2
// (`foo` has no connection to the former object anymore)
foo = foo2;

// foo (and foo2 as they point to the same object) are released
[foo release];

Ответ 6

Вау, спасибо всем за отличные ответы!

Я предполагаю, что на самом деле я имею в виду ссылку на объект, а не на указатель. Тем не менее, я предполагаю, что добавленный , *foo2 находится в той же памяти, что и foo. Также foo2 освобождает память формы одновременно с foo. У меня все еще есть намного больше, чтобы опираться, но один день за раз!

Ответ 7

На самом деле вы пытаетесь сосредоточиться на том, как работают указатели. Я думаю, вы еще не поняли разницу между указателями и объектами.

Здесь ссылка на указатели на wikipedia.