4

I recently discovering these classes like NSMapTable and NSPointerArray, which work like the traditional collections, but also let you store weak references or plain old C pointers. Unfortunately it looks like you can't use the for...in syntax to iterate over non-NSObject pointers. For example:

typedef struct Segment {
    CGPoint bottom, top;
} Segment;
...
NSPointerArray *segments = [[NSPointerArray alloc] 
                                 initWithOptions:NSPointerFunctionsOpaqueMemory];
...
Segment *s = malloc(sizeof(Segment));
[segments addPointer: s];
...
for (Segment *s in segments) {   // nope...

The compiler does not like that last line. The error:

Selector element type 'Segment *' (aka 'struct Segment *') is not a valid object

So, do I need to do this?

for (int i=0, len=segments.count; i<len; i++) {
    Segment *seg = [segments pointerAtIndex:i];
    ...

That's not the end of the world, but I just want to make sure.

Rob N
  • 15,024
  • 17
  • 92
  • 165
  • Have you tried removing the pointer? Its not an object so you can't have a pointer to it. i.e. remove the `*`. – Milo Mar 24 '14 at 00:12
  • Actually it doesn't matter. forin works for NSObjects only. So you are correct. – Milo Mar 24 '14 at 00:13
  • I will add the line of code where I malloc the struct, so it's clear that I'm putting pointers to Segment in the array. – Rob N Mar 24 '14 at 00:40

2 Answers2

4

(This might be more of theoretical interest.) NSPointerArray does conform to the NSFastEnumeration protocol, it is only the for (id object in collection) language construct that cannot be used with arbitrary pointers which are not Objective-C pointers.

But you can get a whole bunch of pointers from the array by calling the NSFastEnumeration method countByEnumeratingWithState:objects:count: directly. This is a bit tricky because that method need not fill the supplied buffer (as explained here: How for in loop works internally - Objective C - Foundation).

Here is a simple example how this would work:

__unsafe_unretained id objs[10];
NSUInteger count = [segments countByEnumeratingWithState:&state
                                                 objects:objs count:10];

// Now state.itemsPtr points to an array of pointers:
for (NSUInteger i = 0; i < count; i++) {
    Segment *s = (__bridge Segment *)state.itemsPtr[i];
    NSLog(@"%p", s);
}

So this does not help to make the code simpler and you probably want to stick with your explicit loop.

But for large arrays it might improve the performance because the pointers are "fetched" in batches from the array instead of each pointer separately.

Community
  • 1
  • 1
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
1

the for (... in ...) syntax won't work in this case because Segment is a struct, not an Objective C object. Your second for loop should work.

Lance
  • 8,872
  • 2
  • 36
  • 47
  • Okay, good to know. Any idea if it necessarily can't work, or if it's just that Apple didn't get around to implementing it? It seems like that `NSFastEnumeration` could work with `void*` just as easily as `id`. – Rob N Mar 24 '14 at 00:46