Я не понимаю этого, если только это не потому, что я выпускаю собственность вместо ivar. Может ли кто-то пролить свет на проблему?Неправильный декремент ссылочного счета, который не принадлежит на данный момент
self.dataToBeLoaded = [[NSMutableData alloc] initWithLength:10000];
[self.dataToBeLoaded release];
Предупреждение: Incorrect decrement of the reference count of an object that is not owned by the caller
.
Свойство dataToBeLoaded
имеет атрибут сохранения, связанный с его установщиком.
Мое понимание - это инициализация alloc увеличивает счетчик удержания, а присвоение свойства увеличивает счетчик удержания. Поскольку я только один, чтобы сохранить его один раз, поэтому я освобождаю его сразу после назначения.
UPDATE - некоторые экспериментальные результаты:
Поскольку я отметил в своих комментариях ниже, что я получил противоречивые советы о том, что сохраняют свойство делает синтезированную сеттер, я думал, что я хотел бы сделать небольшой эксперимент с использованием приведенный выше код, модифицированный с некоторым протоколированием:
NSLog(@"retain 1 = %d", [dataToBeLoaded_ retainCount]);
self.dataToBeLoaded = [[NSMutableData alloc] initWithLength:10000];
NSLog(@"retain 2 = %d", [dataToBeLoaded_ retainCount]);
[self.dataToBeLoaded release];
NSLog(@"retain 3 = %d", [dataToBeLoaded_ retainCount]);
результатов в каждом журнале заявлении было 0, 2 и 1.
Видимо, это не представляется возможным вмешаться на адрес или код инициализации, чтобы увидеть, что счет сохранения идет от 0 до 1 до 2. Я мог бы подклассифицировать класс NSMutableData, но был коротким во времени.
Я знаю, что много сказано, что вы не можете полагаться на значение свойства retainCount, но то, что у меня кажется последовательным, и я ожидал бы разумного поведения в короткой области кода, как показано в примере. Поэтому я склонен полагать, что предыдущие советы верны - свойство сохранения - это обещание включить сохранение в сеттер. Итак, здесь у меня есть сохранение из alloc/init и сохранение из вызова setter. Таким образом, сохранить счетчик устанавливается равным 2.
Когда я запускаю этот код:
NSMutableData *theData;
NSLog(@"retain 1 = %d", [theData retainCount]);
theData= [[NSMutableData alloc] initWithLength:10000];
NSLog(@"retain 1a = %d", [theData retainCount]);
self.dataToBeLoaded = theData;
NSLog(@"retain 2 = %d", [theData retainCount]);
[self.dataToBeLoaded release];
NSLog(@"retain 3 = %d", [theData retainCount]);
Сохранять количество в каждом журнале утверждения 0, 1, 2, 1.
Так что я доказательства, что предполагает, что сеттер предоставляет retain
. Это кажется скорее обещанием, чем подсказкой, потому что на самом деле это происходит.
Я открыт для других объяснений. Я не хочу быть высокомерным об этом. Я просто хочу понять, что происходит. Похоже, что предупреждение (в теме этого вопроса) действительно ложно и не о чем беспокоиться.
Еще один эксперимент выполняется с использованием assign
, а не retain
как атрибут в заявлении @property. С тем же кодом:
NSMutableData *theData;
NSLog(@"retain 1 = %d", [theData retainCount]);
theData= [[NSMutableData alloc] initWithLength:10000];
NSLog(@"retain 1a = %d", [theData retainCount]);
self.dataToBeLoaded = theData;
NSLog(@"retain 2 = %d", [theData retainCount]);
[self.dataToBeLoaded release];
NSLog(@"retain 3 = %d", [theData retainCount]);
сохранить счетчик на каждый срубе 0, 1, 1 (сеттер не сохранил), то сообщение об ошибке: message sent to deallocated instance
. В последнем выпуске установлено, что счетчик удержания равен нулю, что вызвало освобождение.не
UPDATE 2
Окончательное обновление - когда синтезированный сеттер перекрывается с собственным кодом, то сохранить атрибут больше не наблюдается, если ваш сеттер явно не включает его. По-видимому (и это противоречит тому, что мне рассказывали в других разделах здесь), вы должны включить свое собственное сохранение в сеттер, если это то, что вы хотите. Хотя я не тестировал его здесь, вам, вероятно, нужно сначала выпустить старый экземпляр, иначе он будет просочиться. не
Этот обычай сеттер больше не имеет свойств атрибутов декларации @propety:
- (void) setDataToBeLoaded:(NSMutableData *)dataToBeLoaded {
dataToBeLoaded_ = dataToBeLoaded;
}
Это имеет смысл. Переопределите синтезированный сеттер и переопределите все объявленные свойства. Используйте синтезированный сеттер, и объявленные свойства наблюдаются в синтезированной реализации.
Атрибуты @property представляют собой «обещание» относительно того, как реализован синтезированный сеттер. Когда вы пишете пользовательский сеттер, вы по своему усмотрению.
Я понимаю, что сеттер является вызовом метода. Возможно, я до сих пор не совсем понимаю, что сеттер в том случае, когда свойство содержит атрибут сохранения. Я думал, что установщик для сохраненного свойства сначала освобождает ранее упомянутый экземпляр (или, по вашим словам, отбрасывает его), затем назначает переданную ссылку экземпляра, а затем сохраняет его. Теперь переданный экземпляр уже был сохранен один раз alloc/init. - Кстати, отброшенный ссылочный экземпляр не просочился в этот процесс, если его счетчик ссылок обращается в нуль. Я что-то пропустил в вашем объяснении? – Jim
Вы предполагаете слишком много о своей конкретной реализации, а не обещаниях API. Вы предполагаете, что 'setDataToBeLoaded:' назначает ivar и сохраняет его. Это не обещано вашим API. Атрибут «сохранить» не является обещанием в API. Это подсказка, и она используется @synthesize (но @synthesize не имеет ничего общего с API). Надеюсь, что вы будете еще яснее: никогда не выпускайте то, что вы не приняли на себя. Вызов 'dataToBeLoaded' не получает права собственности на возвращаемое значение. У метода нет имени/copy/alloc в имени. Вы не владеете им. –
Вы упоминаете, что вы не просачиваетесь. Вы на самом деле. Вы просачиваетесь, и вы перевыпускаете, и два из них балансируют, поэтому вы не видите утечки в «Инструменты».Но статический анализатор должен отметить оба. –