【问题标题】:NSFastEnumeration object casting in ARCARC中的NSFastEnumeration对象转换
【发布时间】:2013-06-15 05:56:59
【问题描述】:

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

到目前为止,我已经正确地遍历了我的对象,但返回的对象不是 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 docs,对于__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;

【问题讨论】:

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


    【解决方案1】:

    您错误地投射了someObject。你的意思是:

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

    (让我们也摆脱那些糟糕的演员表)

    state->itemsPtr = &someObject;
    

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

    【讨论】:

    • 你是对的!我犯了一个愚蠢的错误。看起来我必须检查我的 C 指针语法。就像你说的那样,这有点侥幸,因为它在给定对象的底层结构表示的情况下得到了解决。
    • 使用&,您也可以摆脱这两个糟糕的演员阵容。
    • 太棒了!你能简要解释一下为什么会这样吗? itemsPtr 在结构中定义如下:id __unsafe_unretained *itemsPtr。据我了解,id 的意思是“指向某物的指针”。因此,如果我们忽略__unsafe_unretained 部分,我们将id * 作为类型,这意味着pointer to a pointer to something 对吗?然后& 表示“地址”。因此,当我执行 &someObject 时,它会返回一个整数,它是对象的内存地址。我不确定我是否理解为什么 &myObject 可以分配给id *。 itemsPtr 是否指向另一个现在保存 myObject 地址的指针?
    • (抱歉问题太长了,我可能应该去复习 C 指针)
    • id 不是指向某物的指针,而是指向objc_object 的指针。 id* 是一个指向objc_object 的双指针。您需要 & 因为指向类的指针只是指向 objc_object 的单个指针。这就是为什么你必须写NSString *var,但只能写id var。当你取消引用 id 时,你会得到它指向的结构布局中的第一个值,即Class isa
    【解决方案2】:

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

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

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

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

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

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

    【讨论】:

    • 感谢您的回答,这完全有道理,这就是我所想的大致情况。不过,请参阅我的问题的更新,我使用显式强制转换测试了您的理论,但我似乎无法在 __NSCFConstantString 对象上调用 NSString 方法。我需要做一些特殊的事情才能转换成 obj-c 对象吗?
    • @Nebs 不,强制转换根本没有任何作用(当然,除了愚弄编译器)。它不会改变对象的类,也不会改变它响应的消息。然而,__NSCFConstantStringNSString 的正确子类,因此它必须响应 NSString 响应的所有消息。为什么您收到错误是您试图将其发送到 类对象本身,而不是它的一个实例。
    • 是的,你是对的。我将错误的东西传递给了 itemsPtr,所以我的问题就在那里,而不是投射。虽然您的回答在技术上是书面的,但 CodaFi 的回答触及了这个问题的根源,所以我会接受他的回答。感谢您的澄清!
    • 这是因为如果objc_object 在内存中的布局方式不会比它已经发生的更灾难性地失败。我想我已经在回答中解释过了
    猜你喜欢
    • 2015-08-12
    • 1970-01-01
    • 2014-11-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-01
    相关资源
    最近更新 更多