Приведение объекта NSFastEnumeration в ARC

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

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

Вот часть кода, которая устанавливает состояние-> 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, для __bridge:

Передача права собственности не происходит, и ARC не вводит операции по сохранению

Это верно?

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

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

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

ОБНОВИТЬ

Ответ Роба на правильном пути, но чтобы проверить эту теорию, я изменил цикл for на следующий:

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

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

+[__NSCFConstantString length]: unrecognized selector sent to class

Это почти похоже на объекты, возвращенные в for Цикл это классы, а не экземпляры. Что-то еще может быть не так... Есть мысли по этому поводу?

ОБНОВЛЕНИЕ 2: РЕШЕНИЕ

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

state->itemsPtr = &someObject;

2 ответа

Решение

Вы неправильно кастуете someObject, Что вы имели в виду:

state-> itemsPtr = (__unsafe_unretained id *) (__ bridge void *) & someObject;

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

state->itemsPtr = &someObject;

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

Ваш код так и есть. Ваш отладочный вывод показывает детали реализации.

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

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

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

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

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

Другие вопросы по тегам