2

我正在尝试在自定义类上实现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'循环

其他类.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 *)似乎使用 __bridge 转换为 void 类型的指针,用于将 obj-c 世界连接到 CF 世界。根据llvm 文档,对于__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;
4

2 回答 2

3

你投错了someObject。你的意思是:

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

(让我们也摆脱那些可怕的演员表)

state->itemsPtr = &someObject;

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

于 2013-06-15T06:19:03.687 回答
1

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

NSString与免费桥接CFString 这意味着您可以将 anyNSString视为 a CFString,反之亦然,只需将指针转换为其他类型即可。

事实上,在后台,编译时常量字符串是 type 的实例__NSCFConstantString,这就是您所看到的。

如果您@"hello"输入源代码,编译器会将其视为 aNSString *并将其编译为__NSCFConstantString.

如果您CFSTR("hello")输入源代码,编译器会将其视为 aCFStringRef并将其编译为__NSCFConstantString.

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

于 2013-06-15T06:07:54.103 回答