Добавление не NSObjects в NSMutableArray


person jbat100    schedule 26.10.2011    source источник
comment
Почему вы хотите использовать объект, не производный от NSObject?   -  person semisight    schedule 26.10.2011
comment
Я не знаю, мне просто интересно, как NSMutableArray (и все изменяемые коллекции) работает внутри управления памятью.   -  person jbat100    schedule 26.10.2011
comment
Хотя это не отвечает на ваш вопрос, retain и release на самом деле объявлены в протоколе NSObject, а не в интерфейсе NSObject, поэтому нет необходимости спускаться с NSObject.   -  person Matt Wilding    schedule 26.10.2011


Ответы (6)


Жестокая правда о большинстве контейнеров Foundation (и, по степени, о большинстве классов, разработанных Apple, а также о большинстве классов, разработанных третьими сторонами) заключается в том, что когда метод принимает тип id, он должен действительно читать id<NSObject>, что означает любой тип, который отвечает к протоколу NSObject. Экземпляры классов, не входящие в иерархию NSObject, вряд ли будут реагировать на -retain и -release, что особенно неудобно при попытке добавить их в контейнер. Они также вряд ли ответят на -hash, -isEqual:, -description, -copy и на все другие методы, которые контейнеры Foundation могут использовать для своего содержимого по любой причине.

Например, если вы попытаетесь добавить Class объектов в контейнер Foundation (кроме NSMapTable, поскольку этот был разработан с учетом большой гибкости), вы упретесь в стену, потому что ожидается, что «современные» классы ObjC наследуются от NSObject. , или хотя бы реализовать протокол NSObject.

Хотя это довольно редкая ситуация. Class — практически единственный полезный класс, который не наследуется от NSObject.

person zneak    schedule 26.10.2011

Означает ли это, что если тип id, который не является NSObject или подклассом, добавляется в NSMutableArray, он должен отвечать на сохранение и освобождение сообщений?

По умолчанию Да, хотя есть обходные пути с использованием CF-API.

Определение id, похоже, не требует этого. Является ли объективной директивой C то, что любой тип идентификатора должен отвечать на стандартные сообщения управления памятью?

Именно так были написаны библиотеки; Корневые классы (не наследуемые от NSObject) очень необычны. Альтернативой может быть - (void)addObject:(id<NSObject>), но это потребует довольно большого расширения вашего корневого класса... возможно, лучшим решением был бы протокол NSReferenceCounted, который берет соответствующие биты из NSObject.

Однако типы NS-коллекций действительно предполагают, что они имеют дело с NSObjects (например, словари используют hash и description).

person justin    schedule 26.10.2011
comment
хорошая идея для протокола NSReferenceCounted, хотя, как указывает zneak, другие сообщения, не связанные с управлением памятью, используются коллекциями (-hash, -isEqual:, -description, -copy), поэтому, возможно, протокол NSCollectable... Было бы разумной поправкой на будущее объектива C. Спасибо за ответ. - person jbat100; 27.10.2011

Нет, не существует соглашения о том, что объекты типа "id" должны отвечать на сообщения сохранения/освобождения; на самом деле, можно сказать, что обеспечение существования таких методов является целью протокола NSObject (а не класса). Однако «id» говорит компилятору «не беспокоить проверку типов», поэтому, когда вы добавляете объект в nsarray, который не реализует эти методы, он будет компилироваться, но вы получите сбой во время выполнения. См. http://unixjunkie.blogspot.com/2008/03/id-vs-nsobject-vs-id.html для более подробного объяснения.

person edsko    schedule 26.10.2011

Вы не должны помещать необъектные типы в NSArray, например, если вы хотите сохранить массив CGRects, вы оборачиваете их в объекты NSValue и сохраняете их (то же самое для CGPoint, CGSize и т. д.).

Если у вас есть пользовательская структура, вам понадобится оболочка-потомок NSObject, что в первую очередь противоречит цели!

person Simon Lee    schedule 26.10.2011
comment
Я знаю, и испытал боль от этого несколько лет назад, мне просто интересно, почему прототипом для addObject: является - (void)addObject:(id)anObject, а не - (void)addObject:(id‹ NSObject›)объект; - person jbat100; 26.10.2011
comment
@ jbat100 Полностью согласен, это вводит в заблуждение! - person Simon Lee; 26.10.2011
comment
Я не думаю, что он спрашивает о помещении C-структур в массив. Он спрашивает о размещении id, которые не происходят от NSObject (например, NSProxy). - person Matt Wilding; 26.10.2011
comment
NSProxy соответствует NSObject, так что это, вероятно, плохой пример. - person Simon Lee; 26.10.2011
comment
@ Саймон Ли, да, это так, но это первое, о чем я мог подумать. - person Matt Wilding; 26.10.2011
comment
История — вот почему; <NSObject> на самом деле не вступал в игру до тех пор, пока интерфейс NSArray не был уже написан, чтобы принять (id) под ошибочным уведомлением о том, что могут быть другие корневые классы, которые все еще совместимы с классом коллекции. Многолетний опыт показал, что это предположение было неверным. - person bbum; 26.10.2011

Если вам действительно нужен массив, содержащий элементы, которые вы не хотите сохранять/выпускать, вы можете создать его с помощью CFArrayCreateMutable и передать ему соответствующие обратные вызовы. (CFMutableArray соединен с NSMutableArray по бесплатному номеру.)

person David Dunham    schedule 26.10.2011

Помимо технических обсуждений выше, я думаю, что ответ связан с историей языка и его предпочтением соглашений над синтаксисом. Формальные протоколы изначально не были частью языка — все было неформальным. Впоследствии Apple в основном перешла на формальные протоколы, но неформальные протоколы являются частью языка и используются частями официального API. Таким образом, они являются полностью поддерживаемой первоклассной частью Objective-C.

Если вы посмотрите на документацию к NSArray, там, среди прочего, говорится:

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

И:

NSArray «бесплатно связан» со своим аналогом Core Foundation, CFArray [...] иногда вы можете делать с CFArray вещи, которые вы не можете легко сделать с NSArray. Например, CFArray предоставляет набор обратных вызовов, некоторые из которых предназначены для реализации пользовательского поведения удержания-освобождения. Если вы укажете реализации NULL для этих обратных вызовов, вы можете легко получить несохраняемый массив.

Таким образом, ваш вопрос основан на ложной предпосылке, в частности, либо на частичном прочтении документации, либо на неполном рассмотрении языка.

id <NSObject> правильно не используется, потому что протокол NSObject не соответствует протоколу, который должны реализовывать объекты, чтобы их можно было использовать с NSArray. Вместо этого в документации установлен неформальный протокол, который является подмножеством NSObject (хотя и со вторым удалением неформальности, что бывает редко). Хотя неформальные протоколы становятся все более редкими, они действительны и еще не исключительны.

Итак, краткая версия моего ответа такова: NSArray документирует неформальный протокол, этот протокол не совпадает с NSObject. Синтаксис неформальных протоколов не представлен, поэтому id.

person Tommy    schedule 27.10.2011