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

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

Это недавнее обсуждение SO меня смутило. Прототипом NSMutableArray для addObject: является

- (void)addObject:(id)anObject

и id определяется в objc.h как

typedef struct objc_class *Class;
typedef struct objc_object {
    Class isa;
} *id; 

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

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

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

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

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

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

Ответ 3

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

Ответ 4

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

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

Ответ 5

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

Ответ 6

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

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

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

и

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

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

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

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