ARC中的NSFastEnumeration对象强制转换

时间:2013-06-15 05:57:00

标签: objective-c automatic-ref-counting nsfastenumeration toll-free-bridging

我正在尝试在自定义类的NSFastEnumeration协议中实现countByEnumeratingWithState:objects:count:方法。

到目前为止,我已经正确地迭代了我的对象,但返回的对象不是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 *)强制转换为匹配itemPtr需要的正确类型。 (__bridge void *)似乎转换为void类型的指针,__ bridge用于将obj-c世界桥接到CF世界。根据{{​​3}},__bridge

  

没有所有权转让,ARC不会保留任何保留操作

这是对的吗?

从我的理解__NSCFConstantString只是NSString的核心基础。我也理解,使用ARC,你需要从Objective-C对象桥接到CoreFoundation等价物,因为ARC不知道如何管理后者的内存。

我怎样才能使这个'for..in'循环中的对象属于原始类型?

另请注意,在这种情况下,我将NSStrings添加到我的集合中,但理论上它应该支持任何对象。

更新

Rob的回答是正确的,但为了测试这个理论,我将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 个答案:

答案 0 :(得分:3)

您输错了someObject。你的意思是:

<击>     state-&gt; itemsPtr =(__ innafe_unretained id *)(__ bridge void *)&amp; someObject;

(让我们摆脱那些可怕的演员阵容)

state->itemsPtr = &someObject;

如果没有address-of,则将您的变量推入第一个指针,该指针在循环中被取消引用。当它被解除引用时(基本上是*id),你得到了底层的objc_object的isa类指针而不是一个对象。这就是为什么调试器在枚举器调用中打印字符串的值,以及循环内对象的类,以及为什么向结果指针发送消息会引发异常。

答案 1 :(得分:1)

你的代码很好。您的调试输出显示了实现细节。

NSString is toll-free-bridged with CFString.这意味着您可以将任何NSString视为CFString,反之亦然,只需将指针投射到其他类型即可。

事实上,编译时常量字符串是__NSCFConstantString类型的实例,这就是你所看到的。

如果您将@"hello"放入源代码中,编译器会将其视为NSString *并将其编译为__NSCFConstantString的实例。

如果您将CFSTR("hello")放入源代码中,编译器会将其视为CFStringRef并将其编译为__NSCFConstantString的实例。

在运行时,这些对象在内存中没有区别,即使您使用不同的语法在源代码中创建它们。