Moving objects within an NSMutableArray

In this blog post I’d like to address a common task in iPhone Apps: letting the user reorder entries of a list that is managed by a UITableView. In many cases this would internally simply result in moving an object within an array from one index to another index. The standard array classes of Cocoa don’t have a method for this task, so we simply implement our own. But there are a few “traps” and concepts we should be aware of (like “abstract classes”, “categories”), and which I like to mention here as well.

When developing Apps for the iPhone and iPod Touch, it’s likely that you’re using UITableView objects to display the content of your App. UITableView is one of the most important Views of the iPhone OS and used for all kinds of lists and data that can be provided as list. In most cases you would store your data in an NSArray or NSMutableArray object and each row of the table represents an item of this array. In case your app allows the user to reorder the table entries, you would have to implement the following delegate method of the UITableView class:

- (void)tableView:(UITableView *)tableView
         moveRowAtIndexPath:(NSIndexPath *)fromIndexPath
                toIndexPath:(NSIndexPath *)toIndexPath

This delegate method is called whenever the user has enabled the “edit” mode of the table and has moved a table entry to another location. Within this method, you have to reorder the items of your own data storage (the array) so that the new representation of the table matches the order of the items within your array again. But if you look at the methods of the NSMutableArray class, you won’t find a method to do this directly. There’s no method to move an object of the array from one index to another. But because this is a task which we need very often (almost all iPhone Apps do have tables and in many cases it makes sense that the users of our App should be able to reorder the table entries), we will add a new method to the NSMutableArray class which is doing this, so we don’t have to implement the same code over and over again.

Subclassing NSMutableArray to add the new method would be possible, but is not the way I would choose here. NSMutableArray is an abstract class and a subclass would not inherit the data storage of the parent class. This means when subclassing, we would have to create and maintain our own data storage and would also have to implement all of the basic methods to set and get array items. Many of the basic data storage classes of the Cocoa Frameworks are abstract classes, so be aware when you plan to subclass these classes. Remember that you have to provide your own data storage if you do so.

But Objective-C provides other ways to add methods to an existing class, so subclassing is not necessary. We can define a category for the new method. A category can be used to add new methods to existing classes without subclassing them. This has the benefit that these new methods are even available for objects which are created by code which we can’t control, like objects created and returned by the system frameworks. But the downside is that a category can not add any additional member variables or additional data storage to the class. The reason is simple, the memory that is occupied by an object of the base class must remain the same. The other frameworks and code which doesn’t know about the new methods are still allocating only the memory for the base class when they create a new object. So a category can only add new behavior (methods), but no new data (member variables).

For our task, a category is just what we need: we don’t need additional variables or data storage, we just need to implement a new behavior (method) to move an array object from one index to another index. We name this new method “moveObjectFromIndex:toIndex:”

The implementation of the category would look like this:

MoveArray.h:

@interface NSMutableArray (MoveArray)

- (void)moveObjectFromIndex:(NSUInteger)from toIndex:(NSUInteger)to;

@end

MoveArray.m:

#import "MoveArray.h"

@implementation NSMutableArray (MoveArray)

- (void)moveObjectFromIndex:(NSUInteger)from toIndex:(NSUInteger)to
{
    if (to != from) {
        id obj = [self objectAtIndex:from];
        [obj retain];
        [self removeObjectAtIndex:from];
        if (to >= [self count]) {
            [self addObject:obj];
        } else {
            [self insertObject:obj atIndex:to];
        }
        [obj release];
    }
}
@end

The code is very simple: we first remove the object from its original location and insert it at the new location or add it to the end of the array when the new location is beyond the array bounds. As you can see, though we do not subclass NSMutableArray, we can still use the “self” keyword just like we would have done this in a subclass. Also note when defining a category there’s no block where you can define member variables (within “{…}” like you can do this when subclassing), but instead you can only give the category a name (in this case “(MoveArray)”). This is because you can’t change the memory allocation for the class (see above).

Now, whenever we need to move an array object from one index to another (in a NSMutableArray object), we can simply call

[array moveObjectFromIndex:from toIndex:to];

As you can see, though we haven’t sub-classed NSMutableArray, the new method is called exactly in the same way as any other methods of the NSMutableArray class. This makes categories a very powerful and flexible feature.

For our initial UITableView example, implementing the UITableView delegate method “tableView:moveRowAtIndexPath:toIndexPath:” would be very simple now. In most cases we could just do the following:

- (void)tableView:(UITableView *)table
               moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath
                      toIndexPath:(NSIndexPath *)destinationIndexPath
{
    [array moveObjectFromIndex:[sourceIndexPath row]
                       toIndex:[destinationIndexPath row]];
}