Приведение объекта 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
,
Во время выполнения нет разницы между этими объектами в памяти, даже если вы использовали другой синтаксис для их создания в исходном коде.