2

Я пытаюсь реализовать метод countByEnumeratingWithState:objects:count: из протокола NSFastEnumeration в пользовательском классе.NSFastEnumeration object casting in ARC

До сих пор я выполнял итерацию через мои объекты правильно, но возвращаемые объекты не являются объектами Objective-C, а скорее базовыми эквивалентами ядра.

Вот часть кода, который устанавливает state-> itemsPtr:

MyCustomCollection.m

- (NSUInteger) countByEnumeratingWithState: (NSFastEnumerationState *)state 
            objects: (id __unsafe_unretained *)buffer 
            count: (NSUInteger)bufferSize { 

    // ... skip details ... 

    NSLog(@"Object inside method: %@", someObject); 
    state->itemsPtr = (__unsafe_unretained id *)(__bridge void *)someObject;  

    // ... skip details ... 
} 

Тогда я называю 'for..in' петлю где-нибудь еще в том же духе

SomeOtherClass.m

MyCustomCollection *myCustomCollection = [MyCustomCollection new]; 
[myCustomCollection addObject:@"foo"]; 
for (id object in myCustomCollection) { 
    NSLog(@"Object in loop: %@", object); 
} 

Выход консоли:

Object inside method: foo 
Object in loop: __NSCFConstantString 

Как вы можете видеть, внутри метода протокола NSFastEnumeration объектные печатает нормально, но как только он получает приведение к id __unsafe_unretained * я теряю оригинальную Objective-C, соответствующий класс.

Если честно, я не совсем уверен, как работает кастинг (__unsafe_unretained id *)(__bridge void *). Кажется, что (__unsafe_unretained id *) выбрано для соответствия правильному типу itemsPtr. (__bridge void *), кажется, бросается в указатель типа void с __bridge, используемым для соединения мира obj-c с миром CF. Согласно llvm docs, для __bridge:

There is no transfer of ownership, and ARC inserts no retain operations

Является ли это правильно?

С моей точки зрения __NSCFConstantString - это основной базовый эквивалент NSString. Я также понимаю, что с ARC вам нужно перейти от объектов Objective-C к эквивалентам CoreFoundation, потому что ARC не знает, как управлять памятью последнего.

Как я могу заставить это работать так, чтобы объекты в моем цикле «for..in» имели оригинальный тип?

Также обратите внимание, что в этом случае я добавляю NSStrings в свою коллекцию, но теоретически он должен поддерживать любой объект.

UPDATE

ответ Роба на правильном пути, но проверить эту теорию я изменил цикл для этого:

for (id object in myCustomCollection) { 
    NSString *stringObject = (NSString *)object; 
    NSLog(@"String %@ length: %d", stringObject, [stringObject length]); 
} 

В теории, которые должны работать, так как объекты эквивалентны, но она падает с этой ошибкой:

+[__NSCFConstantString length]: unrecognized selector sent to class 

это выглядит почти как объекты, возвращаемые в for цикл - это классы, а не экземпляры. Что-то еще может быть здесь не так ... Любые мысли об этом?

UPDATE 2: РЕШЕНИЕ

Это так просто, как это: (благодаря CodaFi

state->itemsPtr = &someObject; 

ответ

3

Вы неправильно набрали someObject. Что вы имели в виду:

состояние-> itemsPtr = (__unsafe_unreteded id *) (__ bridge void *) & someObject;

(Давайте избавимся от этих ужасных слепков, а)

state->itemsPtr = &someObject; 

Без адреса из вашей переменной будет запихнули в первый указатель, который разыменовано в петле. Когда он разыменован (в основном, *id), вы получаете основной указатель класса objc_object isa, а не объект. Именно поэтому отладчик печатает значение строки внутри вызова перечислителя и класс объекта внутри цикла и почему отправка сообщения в результирующий указатель выдает исключение.

+0

Вы правы! Глупая ошибка с моей стороны.Похоже, мне нужно пересмотреть синтаксис указателя на C. Как вы уже сказали, это немного случайность, что это сработало так, как это дало базовое структурное представление объектов. – nebs

+1

С '&' вы можете избавиться от обоих этих ужасных бросков. – CodaFi

+0

Блестящий! Не могли бы вы кратко объяснить, почему это работает? itemsPtr определяется как это в struct: 'id __unsafe_unretained * itemsPtr'. Из моего понимания 'id' означает« указатель на что-то ». Поэтому, если мы игнорируем часть '__unsafe_unretained', у нас есть' id * 'как тип, что означает« указатель на указатель на что-то »правильно? Тогда '&' означает 'адрес'. Поэтому, когда я делаю & someObject, он возвращает мне целое число, являющееся адресом памяти объекта. Я не уверен, что понимаю, почему & myObject можно назначить 'id *'. Указывает ли itemsPtr на другой указатель, который теперь содержит адрес myObject? – nebs

1

Ваш код прекрасен так, как это Ваш вывод отладки обнаруживая детали реализации

..

NSString is toll-free-bridged with CFString. Это означает, что вы можете обрабатывать любые NSString как CFString, или наоборот, просто наведя указатель на другой тип.

Фактически, под капотом постоянные строки времени компиляции являются экземплярами типа __NSCFConstantString, который является тем, что вы видите.

Если вы положили @"hello" в свой исходный код, компилятор рассматривает его как NSString * и скомпилирует его в экземпляр __NSCFConstantString.

Если вы положили CFSTR("hello") в свой исходный код, компилятор рассматривает его как CFStringRef и скомпилирует его в экземпляр __NSCFConstantString.

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

+0

Спасибо за ваш ответ, это имеет смысл, и это было примерно то, что я думал. См. Обновление к моему вопросу, хотя, я проверил вашу теорию с явным приложением, но я не могу назвать методы NSString для объекта __NSCFConstantString. Нужно ли мне делать что-то особенное для включения в объект obj-c? – nebs

+0

@Nebs Нет, кастинг не делает ** ничего вообще ** (за исключением, конечно, обмана компилятора). Он не изменит класс объекта и сообщения, на которые он отвечает. '__NSCFConstantString', однако, является надлежащим подклассом' NSString', поэтому он ** должен ** отвечать на все сообщения, на которые отвечает NSString'. Почему вы получаете ошибку, так это то, что вы пытаетесь отправить ее ** самому объекту класса **, а не одному из них. –

+0

Да, ты прав. Я передавал неправильную вещь в itemsPtr, поэтому моя проблема была там, а не с кастингом. В то время как ваш ответ технически написан, ответ CodaFi попал в корень этого вопроса, поэтому я приму его ответ. Спасибо за разъяснения! – nebs