循环如何在内部工作 - 目标C - 基础

Pau*_*ski 5 objective-c foundation fast-enumeration for-in-loop ios

我找到了这个答案:

/sf/answers/361433411/

其中介绍了如何for in实现循环.

NSFastEnumerationState __enumState = {0};
id __objects[MAX_STACKBUFF_SIZE];
NSUInteger __count;
while ((__count = [myArray countByEnumeratingWithState:&__enumState objects:__objects count:MAX_STACKBUFF_SIZE]) > 0) {
    for (NSUInteger i = 0; i < __count; i++) {
        id obj = __objects[i];
        [obj doSomething];
    }
}
Run Code Online (Sandbox Code Playgroud)

问题是,我发现它错了.

首先,当您启用自动引用计数(ARC)时,您会收到错误消息

Sending '__strong id *' to parameter of type '__unsafe_unretained_id*' changes retain/release properties of pointer

错误的屏幕截图

但即使我关闭ARC,我发现__object数组似乎表现得很奇怪:

error2的屏幕截图

这是实际代码(我假设MAX_STACKBUFF_SIZE为40):

@autoreleasepool {

        NSArray *myArray = @[@"a", @"b", @"c", @"d", @"e", @"f", @"g"];
        int MAX_STACKBUFF_SIZE = 40;
        NSFastEnumerationState __enumState = {0};
        id __objects[MAX_STACKBUFF_SIZE];
        NSUInteger __count;
        while ((__count = [myArray countByEnumeratingWithState:&__enumState objects:__objects count:MAX_STACKBUFF_SIZE]) > 0) {
            for (NSUInteger i = 0; i < __count; i++) {
                id obj = __objects[i];
                __enumState.itemsPtr
                NSLog(@" Object from __objects ! %@", obj);  // on screenshot different message

            }
        }

    }
    return 0;
Run Code Online (Sandbox Code Playgroud)

当我尝试获取__object数组的内容时,我得到了EXC_BAD_ACESS.我还发现当你尝试迭代__enumState.itemsPtr它实际上是有效的.

你能解释一下这里发生了什么吗?为什么我__objects似乎"萎缩".为什么它不包含所需的对象?当ARC打开时,为什么会出现这种错误.

非常感谢您提前花时间和精力!(我提供了截图,以便更好地了解导致错误的原因)

Mar*_*n R 3

首先,强指针不能在 C 结构中使用,如“过渡到 ARC 发行说明”中所述,因此对象数组已声明为

__unsafe_unretained  id __objects[MAX_STACKBUFF_SIZE];
Run Code Online (Sandbox Code Playgroud)

如果你用ARC编译。

现在,从文档中(对我来说)并不明显,但在Cocoa With Love:Implementing countByEnumerateWithState:objects:count:NSFastEnumeration中解释了 该实现不需要填充提供的对象数组,而只需设置 为现有数组(例如一些内部存储)。在这种情况下,数组的内容 是未定义的,这会导致崩溃。__enumState.itemsPtr__objects

更换

id obj = __objects[i];
Run Code Online (Sandbox Code Playgroud)

经过

id obj = __enumState.itemsPtr[i];
Run Code Online (Sandbox Code Playgroud)

给出了预期的结果,这就是您观察到的结果。

另一个参考可以在“FastEnumerationSample”示例代码中找到:

实现此方法时您有两种选择:

1)使用stackbuf提供的基于堆栈的数组。如果您这样做,那么您必须尊重“len”的值。

2) 返回您自己的对象数组。如果执行此操作,请返回所返回数组的完整长度,直到用完对象为止,然后返回 0。例如,链接数组实现可能会按顺序返回每个数组,直到迭代所有数组为止。

无论哪种情况,state->itemsPtr都必须是有效的数组(非零)。...