Question

I'm implementing an application where both instances of a class as well as the class itself have "children" (placed inside a NSMutableArray). It's a pretty complicated application, but thanks to Objective-C, it's a breeze: classes are themselves objects and can have methods and "variables" (with the help of static variables and such).

To make it myself easy, I implemented accessor/setter method on both my instances (using -) and classes (using +) to fetch and manipulate the "children" without having direct access to the arrays. I like to have my objects as much as possible closed and the methods do some data validation.

I also implemented a fast enumeration instance method from the NSFastEnumeration protocol. And here comes the question: can I implement a fast enumeration class method and use it with Objective-C's for...in construct? In order words, can I implement this:

+ (NSUInteger)countByEnumeratingWithState: (NSFastEnumerationState *)state objects: (__unsafe_unretained id [])buffer count: (NSUInteger)len;

And then use it somewhere like that:

for (id child in [MyClass class]) {
    // Do magic stuff…
}

I peeked in GNUStep's GSFastEnumeration.h file which implements fast enumeration as a macro, which affirms the above is possible, but I'm not sure if Apple does the same.

Even if I can't associate the NSFastEnumeration protocol to my class object, is fast enumeration without that protocol possible (and future-proof)?

Was it helpful?

Solution

The method -countByEnumeratingWithState:objects:count: is the whole of fast enumeration—the protocol is, I believe, mostly there for description (it's easier to implement a protocol than declare the method with the correct signature). I would expect it to work just fine, but I don't have a reference for that. You would probably want to loop over [MyClass class], though.

I would probably consider it future-proof. Note that it'd be really trivial to make a tiny wrapper class around your class object that does nothing but implement NSFastEnumeration and forward the instance method -countByEnumeratingWithState:objects:count: to your class's method +countByEnumeratingWithState:objects:count:.

OTHER TIPS

I would recommend creating a protocol with a class method that is identical to the NSFastEnumeration method. You could then iterate over the [MyClass class] as John Calsbeek mentioned.

//Protocol implementation
@protocol FastClassEnumeration <NSObject>
@required
+ (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id [])buffer count:(NSUInteger)len;
@end

//Class implementation
@interface EnumeratedClass : NSObject<FastClassEnumeration>
@end
@implementation EnumeratedClass

+ (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id [])buffer count:(NSUInteger)len
{
    static const unsigned long items_length = 4;
    static NSString * items[items_length] = { @"item1", @"item2", @"item3", @"item4" };

    if(state->state >= items_length)
    {
        return 0;
    }

    state->itemsPtr = items;
    state->state = items_length;
    state->mutationsPtr = (unsigned long *)self;

    return items_length;
}

@end

//Usage
...
    for(NSString *item in [EnumeratedClass class])
    {
        NSLog(@"%@", item);
    }
...

can I ... ?

Well, did you try it? Does it work? If you've tried it, you would notice that it does indeed compile and work.

And why shouldn't it? Class objects are objects just like other objects. Class methods are just methods that happen to be on the class object. If you send a message to a class object, it will call a class method; whereas if you send a message to a non-class object, it will call an instance method. So pretty much, you can put class methods on a class and use the class object the same way you can use a normal object by putting instance methods on its class.

The only possible difference is that the class object won't explicitly conform to the NSFastEnumeration protocol, similar to if you loop over a normal object whose class does not explicitly specify that it conforms to the NSFastEnumeration protocol. So the question is, do they check that an object explicitly conforms to the protocol before using it (as opposed to checking if it responds to the selector)?

In my experience, for pretty much all of Cocoa, for APIs that say they require an object that conforms to a protocol, you can give an object that does not explicitly conform to the protocol, but implements all of the protocol's methods, and it will work fine. (How would they check it anyway? If they use conformsToProtocol:, that won't work for class objects since there's a +conformsToProtocol:, which has a different meaning. They would have to use runtime functions or special-case class objects probably.) For example, the NSDictionary documentation says its keys are required to conform to NSCopying, but if you have an object that does not conform to NSCopying, but does implement copyWithZone:, it works fine. (In fact, there is a +copyWithZone: method, whose stated purpose is to allow class objects to be used as dictionary keys, so obviously it is intended that keys don't need to explicitly conform to NSCopying.)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top