简体   繁体   English

ARC中的NSFastEnumeration对象转换

[英]NSFastEnumeration object casting in ARC

I'm trying to implement the countByEnumeratingWithState:objects:count: method from the NSFastEnumeration protocol on a custom class. 我正在尝试在自定义类上从NSFastEnumeration协议实现countByEnumeratingWithState:objects:count:方法。

So far I have it iterating through my objects correctly, but the objects that are returned aren't Objective-C objects but rather the core foundation equivalents. 到目前为止,我已经正确地遍历了我的对象,但是返回的对象不是Objective-C对象,而是等效的核心基础。

Here's the part of the code that sets the state->itemsPtr: 这是设置state-> itemsPtr的代码部分:

MyCustomCollection.m 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 ...
}

Then I call the 'for..in' loop somewhere else on like this 然后我像这样在其他地方调用“ for..in”循环

SomeOtherClass.m SomeOtherClass.m

MyCustomCollection *myCustomCollection = [MyCustomCollection new];
[myCustomCollection addObject:@"foo"];
for (id object in myCustomCollection) {
    NSLog(@"Object in loop: %@", object);
}

The console output is: 控制台输出为:

Object inside method: foo
Object in loop: __NSCFConstantString

As you can see, inside the NSFastEnumeration protocol method the object prints fine, but as soon as it gets cast to id __unsafe_unretained * I lose the original Objective-C corresponding class. 如您所见,在NSFastEnumeration协议方法内部,对象可以正常打印,但是一旦将其强制转换为id __unsafe_unretained *我将失去原始的Objective-C对应类。

To be honest I'm not quite sure how the (__unsafe_unretained id *)(__bridge void *) casting works in this case. 老实说,我不太确定在这种情况下(__unsafe_unretained id *)(__bridge void *)转换如何工作。 The (__unsafe_unretained id *) seems to cast to match the right type itemsPtr needs. (__unsafe_unretained id *)似乎强制转换为符合正确类型的itemsPtr所需。 The (__bridge void *) seems to cast to a pointer of type void with __bridge used to bridge the obj-c world to the CF world. (__bridge void *)似乎(__bridge void *)类型的指针,而__bridge用于将obj-c世界桥接到CF世界。 As per the llvm docs , for __bridge : 根据llvm文档 ,对于__bridge

There is no transfer of ownership, and ARC inserts no retain operations 没有所有权转移,并且ARC不插入保留操作

Is that correct? 那是对的吗?

From my understanding __NSCFConstantString is just the core foundation equivalent of NSString. 根据我的理解,__ NSCFConstantString只是NSString的核心基础。 I also understand that with ARC you need to bridge from Objective-C objects to CoreFoundation equivalents because ARC doesn't know how to manage the memory of the latter. 我也了解到,使用ARC您需要从Objective-C对象桥接到CoreFoundation等效项,因为ARC不知道如何管理后者的内存。

How can I get this working so that the objects in my 'for..in' loop are of the original type? 我如何才能使它正常工作,以使“ for..in”循环中的对象具有原始类型?

Also note that in this case I'm adding NSStrings to my collection but in theory it should support any object. 还要注意,在这种情况下,我将NSStrings添加到我的集合中,但是理论上它应该支持任何对象。

UPDATE UPDATE

Rob's answer is on the right track, but to test that theory I changed the for loop to this: Rob的答案是正确的,但是为了测试该理论,我将for循环更改为:

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

In theory that should work since the objects are equivalent but it crashes with this error: 从理论上讲,这应该起作用,因为对象是等效的,但由于以下错误而崩溃:

+[__NSCFConstantString length]: unrecognized selector sent to class

It almost looks like the objects returned in the for loop are classes and not instances. 看起来好像在for循环中返回的对象是类而不是实例。 Something else might be wrong here... Any thoughts on this? 这里可能还有其他问题...对此有何想法?

UPDATE 2 : SOLUTION 更新2:解决方案

It's as simple as this: (thanks to CodaFi 就这么简单:(感谢CodaFi

state->itemsPtr = &someObject;

You're incorrectly casting someObject . 您错误地投射了someObject What you meant is: 您的意思是:

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

(Let's get rid of those awful casts as well) (让我们也摆脱那些糟糕的演员)

state->itemsPtr = &someObject;

Without the address-of, your variable is shoved into the first pointer, which is dereferenced in the loop. 没有地址的,您的变量将被推入第一个指针,该指针在循环中被取消引用。 When it's dereferenced (basically, *id ), you get the underlying objc_object's isa class pointer rather than an object. 取消引用(基本上是*id )后,您将获得基础objc_object的isa类指针而不是对象。 That's why the debugger prints the string's value inside the enumerator call, and the class of the object inside the loop, and why sending a message to the resulting pointer throws an exception. 这就是为什么调试器在枚举器调用内打印字符串的值,并在循环内打印对象的类的原因,以及为什么向结果指针发送消息会引发异常。

Your code is fine the way it is. 您的代码按原样很好。 Your debug output is revealing an implementation detail. 您的调试输出将揭示实现细节。

NSString is toll-free-bridged with CFString . NSStringCFString免费桥接。 This means that you can treat any NSString as a CFString , or vice versa, simply by casting the pointer to the other type. 这意味着您只需将指针转换为另一种类型,就可以将任何NSString视为CFString ,反之亦然。

In fact, under the hood, compile-time constant strings are instances of the type __NSCFConstantString , which is what you're seeing. 实际上,实际上,编译时常量字符串是__NSCFConstantString类型的__NSCFConstantString ,这就是您所看到的。

If you put @"hello" in your source code, the compiler treats it as a NSString * and compiles it into an instance of __NSCFConstantString . 如果在源代码中添加@"hello" ,则编译器会将其视为NSString *并将其编译为__NSCFConstantString的实例。

If you put CFSTR("hello") in your source code, the compiler treats it as a CFStringRef and compiles it into an instance of __NSCFConstantString . 如果将CFSTR("hello")放在源代码中,则编译器会将其视为CFStringRef并将其编译为__NSCFConstantString的实例。

At run-time, there is no difference between these objects in memory, even though you used different syntax to create them in your source code. 在运行时,即使您在源代码中使用了不同的语法来创建内存,这些对象在内存中也没有区别。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM