开发者

Adding a category to NSArray

I added a category to NSArray with a helper method for sorting. 开发者_如何学运维My unit tests all pass, but when running the app in the simulator it blows up. Could this be because of the NSMutableArray / NSCFArray class cluster stuff?

Here is the error: 'NSInvalidArgumentException', reason: '*** -[NSCFArray sortBySequenceAsc]: unrecognized selector sent to instance 0x489c1f0'

Anyway, what is the proper way to add a category to NSArray and NSMutableArray?

@interface NSArray (Util) 
- (NSArray *)sortBySequenceAsc;
@end 

@implementation NSArray (Util)
- (NSArray *)sortBySequenceAsc {
    //my custom sort code here
}
@end


I've used categories on NSArray many times with no problems, so I'm guessing your problem lies elsewhere (since your category looks correct).

Since you're getting an "unrecognized selector" error, that means the runtime doesn't know about your category, which means it wasn't linked into your binary. I would check and make sure your category's .m file is included in the appropriate target, clean, and build again.

EDIT: for example, this blog post shows how to create a category for NSArray for creating a shuffled copy of the original array.

EDIT #2: Apple's documentation on categories uses the specific example of extending the functionality of NSArray, so I find it difficult to believe that the "recommended" approach would be to wrap the array in a helper object. Citation (search the page for the five "NSArray" references)


The recommended way is to create a new object containing an NSArray. Underneath NSArray there's a lot of gunk that Apple doesn't tell us about; when you add new methods, you're unable to access that stuff, leading to errors.

Basically, do this:

@interface MySortingArray : NSObject {
     NSArray *theArray
}

- (int)count;
- (NSArray *)sortBySequenceAsc;

@end

@implementation MySortingArray

- (int)count {
    return [theArray count];
}

- (NSArray *)sortBySequenceAsc {
   // your code
}

@end


I want to chime in with something, even though it's late to the party.

Sometimes I'm lazy when I make methods which return an NSArray. I actually return the NSMutableArray I build up in the method. I figure, "What could possibly go wrong?". I use it as a pattern to return an immutable version of the mutable instance I'm working with.

Except this is exactly an instance of where things goes wrong. Apparently Apple also, lazily, return an NSMutableArray from an interface which states it's an NSArray. And if you add a category on NSArray, it won't get added to NSMutableArray. Because an NSMutableArray is an NSArray, but an NSArray isn't an NSMutableArray.


it is a bad idea to put a sorting category on an NSArray... which is supposed to be un-modifiable... that type of functionality is the purview of a NSMutableArray unless you plan to extract all the elements and return an entirely different NSArray instance, that is confusing behavior.


Indeed, it's because of the class cluster. You created a category on NSArray, but you're calling the method on a private class __NSCFArray which doesn't have your method. I found a workaround: create explicitly a new NSArray with your existing array. The new array's class (the object's isa pointer) will point to the correct class where the category is.

So, instead of doing this:

NSArray *array = [someObject someMethod];
// "array" may be a private class with the same methods as "NSArray", but not yours.

[array yourMethod];

Do this:

NSArray *array = [NSArray arrayWithArray: [someObject someMethod]];
// "array" is now explicitly a "NSArray"

[array yourMethod];

It works, but I hope that remains so. It's pretty unpredictable. That's why I created my own array implementation using open source code over here. I left out methods that I don't need and I added new ones that I may need (like shuffle). I'm testing it thoroughly now.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜