Should I subclass the NSMutableArray class - objective-c

I have an NSMutableArray object that I want to add custom methods to. I tried subclassing NSMutableArray but then I get an error saying "method only defined for abstract class" when trying to get the number of objects with the count method. Why is the count method not inherited?
I read somewhere else that I will have to import some NSMutableArray methods into my custom class if I want to use them. I just want to add a custom method to the NSMutableArray class. So should I subclass NSMutableArray, or should I do something else?

NSMutableArray is not a concrete class, it is just the abstract superclass of a class cluster. The documentation for NSMutableArray does have information about how to subclass, but also strongly advises you not to! Only subclass if you have a special need for actual storage.
A class cluster means that the actual class will be chosen at run-time. An array created empty, may not use the same class as an array created with 1000 items. The run-time can do smart choices of what implementation to use for you. In practice NSMutableArray will be a bridged CFArray. Nothing you need to worry about, but you might see it if you inspect the type of your arrays in the debugger, you will never see NSArray, but quite often NSCFArray.
As mentioned before, subclassing is not the same as extending a class. Objective-C has the concept of categories. A category is similar to what other programming languages call mix-ins.
If you for example want a convenience method on NSMutableArray to sort all members on a property, then define the category interface in a .h file as such:
#interface NSMutableArray (CWFirstnameSort)
-(void)sortObjectsByProperty:(NSString*)propertyName;
#end
And the implementation would be:
#implementation NSMutableArray (CWFirstnameSort)
-(void)sortObjectsByProperty:(NSString*)propertyName;
{
NSSortDescriptor* sortDesc = [NSSortDescriptor sortDescriptorWithKey:propertName ascending:YES];
[self sortUsingDescriptors:[NSArray arrayWithObject:sortDesc]];
}
#end
Then use it simply as:
[people sortObjectsByProperty:#"firstName"];

If you're just adding a custom method, use a category on NSMutableArray. It's a class cluster, so the implementation is provided by undocumented subclasses. You need to provide a few methods to generate your own subclass. However, if you just add a category then your custom method will work on all NSMutableArrays in your app.
For comparison, here's an example I wrote a while back of implementing a custom NSMutableArray subclass.

Objective-C has a mechanism for adding methods to existing classes called Categories. That way you don't have to create your own subclass.

This is an old post, but thought I'd add my experience. #PayloW's answer is a good answer and I think answers your question perfectly, however, no one really answered your question the other way around, so I'll do that here.
Should you subclass NSMutableArray (or NSArray)? Depends on what you want to achieve. If you only want to add a method to extend an array's BASIC functionality, like sorting, then #PayloW's answer Categories are the way. However, if you want to create a custom class that behaves like an array then yes, subclassing NSMutableArray is quite easy. But because it's a Class Cluster it doesn't exactly subclass as you'd expect. Normally in subclassing the methods available in the Super Class are available to your subclass or you may override them. With Class Clusters you MUST instead include the Super's methods that you're going to use and provide a _backend instance of the super class to wrap those methods around.
Below is an example of how you'd subclass NSMutableArray (or any Class Cluster):
The interface:
#interface MyCustomArrayClass : NSMutableArray {
// Backend instance your class will be using
NSMutableArray *_backendArray;
}
// *** YOUR CUSTOM METHODS HERE (no need to put the Super's methods here) ***
-(bool)isEmpty;
-(id)nameAtIndex:(int)index;
-(int)rowAtIndex:(int)index;
-(int)columnAtIndex:(int)index;
#end
The implementation:
#implementation MyCustomArrayClass
-(instancetype)init {
if (self = [super init]) {
_backendArray = [#[] mutableCopy];
}
return self;
}
// *** Super's Required Methods (because you're going to use them) ***
-(void)addObject:(id)anObject {
[_backendArray addObject:anObject];
}
-(void)insertObject:(id)anObject atIndex:(NSUInteger)index {
[_backendArray insertObject:anObject atIndex:index];
}
-(void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject {
[_backendArray replaceObjectAtIndex:index withObject:anObject];
}
-(id)objectAtIndex:(NSUInteger)index {
return [_backendArray objectAtIndex:index];
}
-(NSUInteger)count {
return _backendArray.count;
}
-(void)removeObject:(id)anObject {
[_backendArray removeObject:anObject];
}
-(void)removeLastObject {
[_backendArray removeLastObject];
}
-(void)removeAllObjects {
[_backendArray removeAllObjects];
}
-(void)removeObjectAtIndex:(NSUInteger)index {
[_backendArray removeObjectAtIndex:index];
}
// *** YOUR CUSTOM METHODS ***
-(bool)isEmpty {
return _backendArray.count == 0;
}
-(id)nameAtIndex:(int)index {
return ((MyObject *)_backendArray[index]).name;
}
-(int)rowAtIndex:(int)index {
return ((MyObject *)_backendArray[index]).row;
}
-(int)columnAtIndex:(int)index {
return ((MyObject *)_backendArray[index]).column;
}
#end
Then to use like so:
MyCustomArrayClass *customArray = [[MyCustomArrayClass alloc] init];
// Your custom method
int row = [customArray rowAtIndex:10];
// NSMutableArray method
[customArray removeLastObject];
// Your custom class used just like an array !!!
index = 20;
MyObject *obj = customArray[index];
It all works very nicely, is clean and actually pretty cool to implement and use.
Hope it helps.

I have to agree with both node ninja and PeyloW because technically they have both right. Actually, that does not help me much.
Preamble:
There are many arrays in code that all to one contain only one but different type of data e.g. classA, classB, classC.
Problem:
I can easily mix arrays by passing wrong one to e.g. some selector because they are all NSMutableArray. There is no static check, only runtime one.
Solution - 1st try:
Make subclass of NSMutableArray so compiler makes static check and warns about wrong data type.
That is good because compiler warns you even when you pass wrong type to -addObject or -objectAtIndex when you overload that ones.
That is bad because you cannot instantiate NSMutableArray superclass this way.
Solution - 2nd try:
Make new (proxy) class of some type e.g. NSObject as for NSMutableArray and add class member of type NSMutableArray.
This is good because you can instantiate NSMutableClass and compiler checks when you pass wrong type to -addObject or -objectAtIndex when you overload that ones.
The bad side of that is that you need to overload every selector of the NSMutableArray that you use, not only that ones that differs in class that array contains.
Conclusion:
When you build some sophisticated code that has many class types in its arrays, believe me it is worth to try. Simply by doing this compiler showed me several errors that I would not recognize until I will face it in runtime. Or even worse, when end user would face it.

From the Apple reference for NSArray, in the Methods to Override section:
Any subclass of NSArray must override the primitive instance methods count and objectAtIndex:. These methods must operate on the backing store that you provide for the elements of the collection. For this backing store you can use a static array, a standard NSArray object, or some other data type or mechanism. You may also choose to override, partially or fully, any other NSArray method for which you want to provide an alternative implementation.
As a side note, in Objective-C, there is no actual feature that allows you to declare a class as an abstract class, per se, as in Java, for instance. So, what they do instead is call something like the code below, from within some method that they want to force to be overridden by a subclass. In effect, they give the class 'abstract class' semantics.
This method definition acts as an abstract method, which raises an Exception if not overridden, with the following output:
-someAbstractFooMethod only defined for abstract class. Define -[YourClassName someAbstractFooMethod]!
- (void) someAbstractFooMethod
{
//Force subclassers to override this method
NSString *methodName = NSStringFromSelector(_cmd);
NSString *className = [self className];
[NSException raise:NSInvalidArgumentException
format:#"-%# only defined for abstract class. Define -[%# %#]!", methodName, className, methodName];
}

Related

How to allocate an NSObject subclass instance FROM an instance of its superclass?

Given a class structure such as...
#interface SuperClassView : NSView #end
#interface SubClassedView : SuperClassView #property int aProp; #end
How can one instantiate a SubClassedView from an instance of a SuperClassView?
Say a method returns an instance of the superclass SuperView....
SuperClassView *superInstance = [ViewFactory makeSuperClassView];
but I want to get an instance of the subclass SubClassedView? It is not possible to simply "cast" it...
SubClassedView *subClsInstance = (SubClassedView*)[ViewFactory makeSuperClassView];
and there is no built-in (or easily-imagined implementation of an) NSObject method like
self = [super initWithInstance:[superInstance copy]];`
Is the only way to either copy the superclass instance's desired properties to the newly instantiated subclass object, like...
SubClassedView *subClsInstance = SubClassedView.new;
for (NSString* someKey in #["frame",#"color",#"someOtherProperty])
[subClsInstance setValue:[superInstance valueForKey:someKey] forKey:someKey];
Or add (swizzle at runtime) the subclass' "additional property methods" (in this case setAProp: and aProp) to the superclass instance (and also cast it upwards)...
SubClassedView *subClsInstance = (SubClassedView*)[ViewFactory makeSuperClassView];
[subClsInstance addSwizzleMethod:#selector(setAProp:) viaSomeMagic:....];
[subClsInstance addSwizzleMethod:#selector(aProp) viaSomeMagic:....];
Hopefully this is an easy runtime trick that I simply don't know... not a sad sign that I am haplessly trying to trick ObjC into multiple-inheritance via some embarrassing anti-pattern. Either way, ideas?
EDIT: Pending #BryanChen posting his comment as an answer this is achieved easily via his suggested runtime function, or as a category on NSObject á la..
#implementation NSObject (SettingClass)
- (void)setClass:(Class)kls { if (kls) object_setClass(self, kls); } #end
What you are trying to do is pretty non-idiomatic... it feels like you are trying to do something like prototype based OOP. A couple of quick points:
Don't do the swizzle. You can't swizzle onto an instance, you swizzle onto the class definition, so if you do that you won't be adding the subclasses methods onto "an" instance of the superclass, you will be adding them onto all instances of the superclass.
Yes, if you want to do this you just need to copy the the properties you want from the super into the new instance of the subclass.
You can have a factory method in the superclass that returns a subclass, and encapsulate all the the copying in there (so, -[SuperClassView makeSubclassView] that returns SubClassedView *. That is actually relatively common, and is how many of the class clusters are implemented (though they return private subclasses that conform to the implementation of the superclass)
object_setClass is not the droid you're looking for.
Yes, it will change the class of the instance. However, it will not change the size of it. So if your SubClassView declares extra properties or instance variables that are not declared on SuperClassView, then your attempts to access them on this frankenstein instance will result in (at best) buffer overflows, (probably) corrupted data, and (at worst) your app crashing.
It sounds like you really just want to use self in your factory method:
+ (instancetype)makeView {
return [[self alloc] init];
}
Then if you call [SuperClassView makeView], you get back an instance of SuperClassView. If you call [SubClassView makeView], you get back an instance of SubClassView.
"But," you say, "how do I customize the properties of the view if it's a SubClassView?"
Just like you would with anything else: you override the method on SubClassView:
#implementation SubClassView
+ (instancetype)makeView {
SubClassView *v = [super makeView];
v.answer = 42;
return v;
}
#end
object_setClass may or may not be the "runtime trick" you are looking for. It does isa swizzle which change the class of an instance at runtime. However it does have many constrains such as that the new class cannot have extra ivars. You can check this question for more details.
I think the better way to do is that instead of making view using [ViewFactory makeSuperClassView], make it [[SuperClassView alloc] initWithSomething]. Then you can do [[SubClassView alloc] initWithSomething]
or if you really want use ViewFactory, then make it [ViewFactory makeViewOfClass:]

Does Objective-C have an equivalent to java annotations?

Does Objective-C have an equivalent to java annotations?
What's I'm trying to do is create a property and be able to somehow access some metadata about it.
I want to be able to determine what type of classes should go in my array so I'd like to annotate it somehow to say so. Then later be able to access that annotation via something like the runtime library where I can access lists of properties and their names.
//Put some sort of annotation giving a class name.
#property (strong) NSArray *myArray;
You said:
I want to be able to determine what type of classes should go in my array so I'd like to annotate it somehow to say so. Then later be able to access that annotation via something like the runtime library where I can access lists of properties and their names.
There are a few ways to do this sort of thing in Objective-C. Apple's frameworks do this sort of thing by adding a class method that returns the required information. Examples: dependent keys in KVO, +[CALayer needsDisplayForKey:] and related methods.
So, let's create a class method that returns an array of classes that can go into your container property, given the property name. First, we'll add a category to NSObject to implement a generic version of the method:
#interface NSObject (allowedClassesForContainerProperty)
+ (NSArray *)allowedClassesForContainerPropertyWithName:(NSString *)name;
#end
#implementation NSObject (allowedClassesForContainerProperty)
+ (NSArray *)allowedClassesForContainerPropertyWithName:(NSString *)name {
if (class_getProperty(self, name.UTF8String)) {
return #[ [NSObject class] ];
} else {
[NSException raise:NSInvalidArgumentException
format:#"%s called for non-existent property %#", __func__, name];
abort();
}
}
#end
As you can see, this default version of the method doesn't do anything particularly useful. But adding it to NSObject means we can send the message to any class without worrying about whether that class implements the method.
To make the message return something useful, we override it in our own classes. For example:
#implementation MyViewController
+ (NSArray *)allowedClassesForContainerPropertyWithName:(NSString *)name {
if ([name isEqualToString:#"myArray"]) {
return #[ [UIButton class], [UIImageView class] ];
} else {
return [super allowedClassesForContainerPropertyWithName:name];
}
}
...
We can use it like this:
SomeViewController *vc = ...;
SomeObject *object = ...;
if ([[vc.class allowedClassesForContainerPropertyWithName:#"bucket"] containsObject:object.class]) {
[vc.bucket addObject:object];
} else {
// oops, not supposed to put object in vc.bucket
}
There is no native support of this functionality, but you may to take a look at following solution — https://github.com/epam/lib-obj-c-attr/ It is compile time implementation of attributes. Definition of attributes based on defines but not on comments as in other solutions like ObjectiveCAnnotate.
Objective C does not support generics like in Java but ofcourse the language is very flexible that you can accomplish almost anything with simple tricks and knowledge. To implement a generic like feature you could create a category on NSArray class and create your own method to initialize the array and then check to see if the object is really the type of the object you want.
I would write a simple category on NSArray to have such functionality. Say suppose, I want my array to hold objects of class MyClass only then my category would look like,
#interface NSArray(MyCategory)
#end
#implementation NSArray(MyCategory)
-(NSArray*)arrayWithMyClasses:(NSArray*)classes{
if([classes count] > 0){
NSMutableArray *array = [[NSMutableArray alloc] init];
for(id anObj in classes){
NSAssert([anObj isKindOfClass:[MyClass class]], #"My array supports only objetcts of type MyClass");
[array addObject:anObj];
}
return array;
}
return nil;
}
#end
Of course, there is some limitations to it. Since you have created your own category, you should use your own method to initialize and create your own array.
No, Objective-C has no annotation or generics support.
A way to implement such a thing would be to hack Clang to read comments and associate a metadata object to the original object. But, you would be tied to your hacked compiler.
NSString *v1 = [[NSString alloc] init];
// associate
static char key;
NSString *v2 = [[NSString alloc] init];
objc_setAssociatedObject (
v1,
&key,
v2,
OBJC_ASSOCIATION_RETAIN
);
// retrieve
NSString *associate = (NSString *)objc_getAssociatedObject(v1, &key);
Qualifying with a protocol wouldn't be much trouble, and you could test if the collection implements it, but along the way you would need to create a category for each type on the same collection. This would require a different collection at compile time using macros. Overly complicated.
#interface Tomato:NSObject #end
#implementation Tomato #end
#protocol TomatoNSArray <NSObject>
- (Tomato*)objectAtIndexedSubscript:(NSUInteger)index;
- (void)setObject:(Tomato*)tomato atIndexedSubscript:(NSUInteger)index;
#end
// here is the problem, you would need to create one of this for each type
#interface NSMutableArray (TomatoNSArray) <TomatoNSArray>
#end
int main(int argc, char *argv[]) {
#autoreleasepool {
NSMutableArray<TomatoNSArray> *tomatoes = [[NSMutableArray alloc] initWithCapacity:2];
tomatoes[0] = [Tomato new];
tomatoes[1] = [NSObject new]; // warning: incompatible pointer types
}
}
Does Objective-C have an equivalent to java annotations?
Not exactly an equivalent, but there is, and it's better. In Objective-C, the compiler has to store some type and name information in the compiled code (because the language is highly dynamic, a lot of things happen at runtime as opposed to compile time), for example method names ("selectors"), method type signatures, data about properties, protocols, etc. The Objective-C runtime library then has access to this data. For example, you can get the list of properties an object has by writing
id object = // obtain an object somehow
unsigned count;
objc_property_t *props = class_copyPropertyList([object class], &count);
Or you can check what class an object belongs to:
if ([object isKindOfClass:[NSArray class]]) {
// do stuff
}
(Yes, part of the runtime library is itself wrapped into some methods of NSObject for convenience, others only have C function APIs.)
If you specifically want to store custom metadata about an object or a class, you can do that using associated references.
I expect it should be clear now, the answer is NO, not at the moment.
Some people found some alternatives which seem to work in their specific use cases.
But in general there is no comparable feature yet in objective-c. IMHO clang metadata seems to provide a good foundations for this, but as long as there is not support from Apple this will not help, as far as i understood it.
Btw. I guess it should be clear, but just to repeat for all: two changes are required to support annotations as provided in java.
The language need an extension the annotate e.g. methodes, properites, classes, ... in the source code.
A standard interface is required to access the annotated information. This can only provide by apple.
Most alternativ soltuions move the annotation information into runtime and define their own interface. The objective-c runtime provide a standard interface but only with some trick you can annotate properties and still the isse of runtime population.
The typical use case for suche a feature is an IOC container (in Java e.g. Spring) which use the annotated information to inject other objects.
I would suggest to open an feature requrest for Apple to support this.
The answer to your question is that Objective-C does not have a direct equivalent of annotations as found in Java/C#, and though as some have suggested you might be able to engineer something along the same lines it probably is either far too much work or won't pass muster.
To address your particular need see this answer which shows how to construct an array which holds objects of only one type; enforcement is dynamic and not static as with parametric types/generics, but that is what you'd be getting with your annotation so it probably matches your particular need in this case. HTH.
What you need maybe a metadata parser for Objective-C. I have used ObjectiveCAnnotate (compile time retrievable) and ROAnnotation(runtime retrievable).

Fast enumeration on a class object

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)?
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:.
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.)

Class Name with a "+"

I am working on an iOS project in Xcode and I see some classes that have names with a "+"; for example:
TableViewController+TableView.h and then the class is named: #interface RKTableViewController (TableView) as opposed to RKTableViewController+TableView.
What is this + and the (TableView)? If its subclassing UITableView shouldn't the class be declared as: Subclassed name : Parent class name format?
The + in the filename isn't semantically important. Naming a file "ClassName+CategoryName.h/m" is just a popular convention for naming files containing categories.
#interface RKTableViewController (TableView)
#end
declares a category called "TableView" on the RKTableViewController class. Categories are used to add methods to a class outside its main implementation. See the Apple documentation on categories here: http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/objectivec/chapters/occategories.html
These are categories. The are very helpful at times.
You can add methods to a class by declaring them in an interface file
under a category name and defining them in an implementation file
under the same name. The category name indicates that the methods are
additions to a class declared elsewhere, not a new class. You cannot,
however, use a category to add additional instance variables to a
class.
The methods the category adds become part of the class type. For
example, methods added to the NSArray class in a category are included
as methods the compiler expects an NSArray instance to have in its
repertoire. Methods added to the NSArray class in a subclass, however,
are not included in the NSArray type. (This matters only for
statically typed objects because static typing is the only way the
compiler can know an object’s class.)
Category methods can do anything that methods defined in the class
proper can do. At runtime, there’s no difference. The methods the
category adds to the class are inherited by all the class’s
subclasses, just like other methods.
http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/objectivec/chapters/occategories.html
Example:
Here is an example of a category I use all the time. I don't own NSMutableArray but I would love for there to be a simple move function. Instead of subclassing just to add a simple function I attach a category.
// NSMutableArray+Move.h
#interface NSMutableArray (move)
- (void)moveObjectFromIndex:(NSUInteger)from toIndex:(NSUInteger)to;
#end
// NSMutableArray+Move.m
#implementation NSMutableArray (move)
- (void)moveObjectFromIndex:(NSUInteger)from toIndex:(NSUInteger)to
{
if (to != from) {
id obj = [self objectAtIndex:from];
[self removeObjectAtIndex:from];
if (to >= [self count]) {
[self addObject:obj];
} else {
[self insertObject:obj atIndex:to];
}
}
}
This allows me to do new things with a class thats already been created all over my app. So anywhere I use an NSMutableArray I can call my added method like so
NSMutableArray *myArray = [NSMutableArray arrayWithObjects:#"Object A", #"Object B", #"Object C", nil];
[myArray moveObjectFromIndex:0 toIndex:2];

How do I extend an NSArray?

Here's my try:
H file:
#interface Strings : NSArray
#end
M file:
#implementation Strings
- (id) init
{
[self initWithObjects:
#"One.",
nil];
return self;
}
#end
When I run I get this:
'NSInvalidArgumentException', reason: '* -[NSArray initWithObjects:count:]: method only defined for abstract class. Define -[Strings initWithObjects:count:]!'
This is what I did instead:
H file:
#interface Strings : NSObject
+ (NSArray*) getStrings;
#end
M file:
#implementation Strings
+ (NSArray*) getStrings
{
NSArray* strings = [[NSArray alloc] initWithObjects:
#"One.",
nil];
return strings;
}
#end
NSArray is a class cluster (link to Apple's documentation). This means that when you try to create an NSArray, the system creates some private subclass of NSArray. The NSArray class just defines an interface; subclasses of NSArray provide implementations of the interface.
You can write your own subclass of NSArray, but you have to provide your own storage for the objects in the array. You have to initialize that storage yourself. The error message is telling you this, by saying that you need to override initWithObjects:count: in your subclass. Your override needs to put the objects into whatever storage you allocate as part of your class implementation.
The NSArray implementation of the variadic initWithObjects: method is just a wrapper around initWithObjects:count:, so you don't have to implement initWithObjects:.
Deriving from NSArray is something you should avoid. From the documentation:
Remember that NSArray is the public interface for a class cluster and what this entails for your subclass. The primitive methods of NSArray do not include any designated initializers. This means that you must provide the storage for your subclass and implement the primitive methods that directly act on that storage.
What this means is that when you initialize an array, you don't get an instance of NSArray. You'll get an instance of a totally different class that merely has the same interface. That is why subclassing doesn't work the way you think it works: you'll have to completely implement the storage yourself. This is why the documentation further states:
Any subclass of NSArray must override the primitive instance methods count and objectAtIndex:. These methods must operate on the backing store that you provide for the elements of the collection. For this backing store you can use a static array, a standard NSArray object, or some other data type or mechanism. You may also choose to override, partially or fully, any other NSArray method for which you want to provide an alternative implementation.
Last but not least you would have had the initializing wrong anyway. You would have needed to call super:
- (id)init
{
self = [super initWithObjects:#"One", #"Two", nil];
if (!self) return nil;
return self;
}
But as I just said, it just doesn't work that easily. You'll get the same exception again. So you should simply avoid doing deriving from NSArray.
What you can do is add a category to add methods to all NSArray instances.
NSArray doesn't support being subclassed in this way. You can add a category, though, although that's not universally recommended.
See Objective C - Subclassing NSArray for more thoughts.
perhaps
self = [super initWithObjects:...];
You need to assign self, and call your superclass' init method.
if (self = [super initWithObjects:...]) {
...
}
return self;