Skip to content


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]];
}

Posted in iPhone & iPod Touch, Programming, Tips & tricks.

Tagged with , .


40 Responses

Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.

  1. Mark says

    Cool! This is exactly what I was looking for. It’s weird that some obvious functionality like this is not in the Interface!

  2. Cédric Luthi says

    Thanks for sharing. This method should definitely be added to NSMutableArray. Have you filed a radar that we can dupe?

  3. Alexander Alexander says

    @Cédric Luthi
    No, I haven’t filed this to Apple yet.

  4. Uday says

    Alternatively, you could this:

    [array insertObject:[array objectAtIndex:sourceIndexPath.row] atIndex:destinationIndexPath.row];
    [array removeObjectAtIndex:(sourceIndexPath.row+1)];

  5. Alexander Alexander says

    @Uday
    Your solution has a problem when the “from” index is smaller than the “to” index. Here’s an example:

    The array has the following content:
    0 1 2 3 4 5 6

    You want to move item at index 2 to index 5. The first line of your code will result in the following array content (the content of index 2 (2) will be inserted at index 5):
    0 1 2 3 4 2 5 6

    The second line of code will remove the element at 2+1 = 3, and now the array has the following content:
    0 1 2 4 2 5 6

    But this is wrong. You’ve deleted element 3 and the element 2 is now twice in the array.

    Only if the “to” index is smaller than the “from” index, your solution would work.

  6. Uday says

    @Alexander
    You’re totally right, I posted an incomplete solution. Here is the code from one of my apps:

    [array insertObject:[people objectAtIndex:sourceIndexPath.row] atIndex:destinationIndexPath.row+(sourceIndexPath.rowdestinationIndexPath.row?1:0))];

  7. Uday says

    Hmm, that didn’t show up right. Retry:

    [people insertObject:[people objectAtIndex:sourceIndexPath.row] atIndex:destinationIndexPath.row+(sourceIndexPath.rowdestinationIndexPath.row?1:0))];

  8. Uday says

    Still not showing up right. Basically, the checks for the source index row being smaller or greater than the destination index row are contained in both the ‘insertObject’ and ‘removeObjectAtIndex’ message statements, using the conditional operator ‘?’.

  9. Hebbian says

    [assetArray insertObject: object atIndex: row];
    if (draggedIndex > row)
    [assetArray removeObjectAtIndex:draggedIndex+1];
    else
    [assetArray removeObjectAtIndex:draggedIndex];

  10. Nick says

    Great solution, thanks so much for this.

    If anyone else came across the warning ‘NSMutableArray’ may not respond to ‘-moveObjectsFromIndex:toIndex:’ don’t forget to #import “MoveArray.h” in your ViewController class!

  11. loosy says

    hi Alexander,

    In table view can we move rows programmatically with animation instead using “Re-ordering Control” using manual. I really needs it in my application.

    I’ve seen this functionality in the app:
    http://www.grocerygadgets.com/how-grocery-gadgets-work.aspx

    Any help will be appreciated.

  12. Alexander Alexander says

    @loosy
    Yes, you can reorder the rows programatically. Just look at the UITableView class, here you’ll find the following methods you can use for this task…

    beginUpdates
    endUpdates
    insertSections:withRowAnimation:
    deleteSections:withRowAnimation:
    reloadSections:withRowAnimation:
    insertRowsAtIndexPaths:withRowAnimation:
    deleteRowsAtIndexPaths:withRowAnimation:
    reloadRowsAtIndexPaths:withRowAnimation:

  13. Boy says

    Great work, thanks for sharing.

  14. PaulG says

    Great solution. Made my life that much easier.

  15. David says

    Fantastic, thanks for sharing!!!

  16. Ignacio R. says

    Great! That helped me a lot, thank you for sharing

  17. Aaron H says

    I would like to point out an inefficiency in your method. By removing/inserting an object from a NSMutableArray you potentially affect every row after the deletion/insertion. I say ‘potentially’ because it’s not clear what internal method Apple uses to maintain their mutable arrays. However, assuming it’s a simple c-array, then every row after that deletion/insertion index will be need to be moved down/up. In a very large array, this could be inefficient if the items moved are at the beginning. However, replacing items in an array are not inefficient at all. Thus the following (note this code is under ARC, so no memory management):

    - (void) moveObjectAtIndex:(NSUInteger)fromIndex toIndex:(NSUInteger)toIndex{
        if (fromIndex == toIndex) return;
        if (fromIndex >= self.count) return; //there is no object to move, return
        if (toIndex > self.count) toIndex = self.count; //toIndex too large, assume a move to end
        id movingObject = [self objectAtIndex:fromIndex];
    
        if (fromIndex < toIndex){
            for (int i = fromIndex; i <= toIndex; i++){
                [self replaceObjectAtIndex:i withObject:(i == toIndex) ? movingObject : [self objectAtIndex:i + 1]];
            }
        } else {
            id cObject;
            id prevObject;
            for (int i = toIndex; i <= fromIndex; i++){
                cObject = [self objectAtIndex:i];
                [self replaceObjectAtIndex:i withObject:(i == toIndex) ? movingObject : prevObject];
                prevObject = cObject;
            }
        }
    }
    

    This will only affect the rows complicit in the move.

  18. Aaron H says

    A small correction: the toIndex should have been constrained to ‘self.count -1′, not ‘self.count’.

  19. Alexander Alexander says

    @Aaron H
    Thanks for the alternate implementation. As you already pointed out, the problem is that we don’t know the internal implementation of the array object.

    It is possible that repeatingly calling the replaceObjectAtIndex: could be more expensive because all of the overhead for calling an Objective-C method.
    For example if the internal implementation would just use something like memcpy to copy the elements of a c-array (which would be just pointers to the real objects), it’s probably faster then looping manually through the array and calling the replacing method, because you only have one simple function call (memcpy) instead of many Objective-C method calls (replaceObjectAtIndex:). And memcpy is probably optimized for speed, so it’s most likely very fast, therefore the overhead for the method calls might be much more important.

    I guess, only a speed test could give us an answer, though this doesn’t mean that the answer would be still the same in the next release of the OS.

    In the end, I guess for “normal” use cases it probably doesn’t matter which implementation is used. Both methods are probably fast enough so no user would see any difference. Especially when this method is used for reordering items because of a user interaction. The user interaction itself is much slower, therefore the time for moving an array element is irrelevant.

  20. Ev says

    Has everyone forgotten about

    - (void)exchangeObjectAtIndex:(NSUInteger)idx1 withObjectAtIndex:(NSUInteger)idx2

    ??

  21. Alexander Alexander says

    @Ev
    Not really. Exchanging two items within an array is something different than moving one item from one location to another location.

    Assume, we have the following array:
    0,1,2,3,4,5,6

    Now we exchange the items at index 1 and 5 and we get:
    0,5,2,3,4,1,6

    But if we move item from index 1 to index 5, we get:
    0,2,3,4,5,1,6

    The result is different, so it’s clear that “exchangeObjectAtIndex:withObjectAtIndex:” can not be used to move objects within an array, like “moveObjectFromIndex:toIndex:” is doing this. The important detail is here that moving an item within an array to another location will cause other items (those between the start end end location) also have to change their location, to make room for the item that is moved in the first place.

  22. Darrin Massena says

    The “if (to >= [self count])” clause is unnecessary because insertObject adds to the end of the array if the index is the same as the array length.

    For example, moveObjectFromIndex:0 toIndex:1 of a 2 element array removes the first element, then inserts at 1 (same as new array length) which results in the elements being swapped as expected.

    So the whole thing can be reduced to:

    if (from == to)
    return;
    id object = [[[self objectAtIndex:from] retain] autorelease];
    [self removeObjectAtIndex:from];
    [self insertObject:object atIndex:to];

  23. jgs says

    @Darrin

    The “if (to >= [self count])” clause is unnecessary because insertObject adds to the end of the array if the index is the same as the array length.

    But what if the index is greater than the array? For example:

    moveObjectFromIndex:0 toIndex:17 of a 2 element array

    insertObject:AtIndex will throw an NSRangeException here. So the = may not be necessary, but the > is.

  24. Alexander Alexander says

    @jgs
    This is exactly why I’ve done it this way. But on the other hand, Darrin is right too, the standard methods of an NSArray don’t accept indexed out of range, so if a category doesn’t do this either, it just matches the normal behavior of that class.

  25. jgs says

    Fair enough, but that’s a different argument. Darin’s argument is:

    A. insertObject:object AtIndex:index addsobject to the end of
    the array if index is equal to the length of ther array.

    B. Part of your code adds :object to the end of the array if index is greater
    than or equal to the length of ther array

    C. A impiles that all of B is redundant.

    A purist will argue that moveObjectFromIndex:toIndex should handle an index parameter value the same way that insertObject:AtIndex does. You have a different philosophy, and given that you do, C is incorrect.

    How great would it be if it were this easy to resolve philosophical differences? No war. More love.

  26. Dan says

    @Aaron H and Alexnader,
    This a nice answer to solve many question! Thank you for posting it!
    Can you please give me a way that i can implement a array that i can delete the last element of the array and move the other elements by one position forward(by one index) and insert a new element to the 1st position of the array, Basically, it’s something like a buffer. It would be nice if you people can suggest me a method which has lowest computational cost!
    Thank you!
    You people should get courage n power to do more and more bright work like these to enlighten students like us!

  27. Alexander Alexander says

    @Dan
    To remove the last item in an array (NSMutableArray) just use [array removeLastObject]. And to insert a new item at the beginning, use [array insertObject:item atIndex:0]. All existing items will automatically move to the next location to make room for the newly inserted item.

  28. Dan says

    @Alexander
    Thank you for your valuable answer,
    can you please help in this matter too,
    The element that i want to add to the array is stored in a variable of type float,
    Eg:- float variable_name =2.345;

    But when i code,
    [array insertObject:variable_name atIndex:0];
    it gives a error message!
    How can i add an element to this array from a variable.
    Thank you!

  29. Dan says

    @Alexander,
    And also
    can i multiply a element in a array like this? But it get errors?

    NSMutableArray *x;
    x=[NSMutableArray arrayWithObjects:@"3",@"3",nil);
    float output;
    

    i want to multiply the 1st element by number 3.34 and store in the variable output
    I code…

    output=([x objectAtIndex:0])*3.34;
    

    But it gives an error!
    Please help me!

  30. Simon Tillson says

    @Dan
    You must be pretty new to Objective-C, Dan.
    A float variable is what is known as a value type whereas Objective-C containers like NSMutableArray can only store object types.
    To do this, create an object-type container for your float variable using:

    float myFloat = 123.4;
    NSNumber *myNumberObject = [NSNumber numberWithFloat: myFloat];
    

    Then you can add it to an NSArray, store it in NSUserDefaults, whatever…

    To get it back out again as a float value, use this;

    float myRetrievedFloat = [myNumberObject floatValue];
    
  31. Simon Tillson says

    @Dan
    Just saw your other post – nearly fell off my chair laughing, sorry…

    You don’t want to try multiplying a value type by an object type. That won’t work, ever.
    It’s probably easiest if you do all of your calculations on your float values, and just use NSNumber to store and retrieve them from your arrays.

    Good luck, happy learning! :-)

  32. Dan says

    @Simon!
    Yes, i m new to objective C, Thank you for your valuable answer! I think i learn a good lesson about objective C from you.
    But sir, Problem i encounter is… If i explain you from the beginning…
    What i want is to run a loop that get a reading(float value) from variable(Numbers_of float) and insert it to a array of 4 elements. Every time I get a new reading(float value) i insert in to the first position of the array and delete the last element.
    Then with those 4 numbers stored in the array, i need to perform a mathematical operation.
    As Alexander said, i implement the code to add and remove elements from the array, but the problem is how to retrieve the numbers(float) from the array and multiply or add them with others. Since i m new to objective C, i have no idea to be honest. But with your answers, i m somewhere there, but can’t figure out the exact way.. please help me~

  33. Simon Tillson says

    @Dan
    Ok – I can sympathise with you – you’re trying to do something quite complex, and as a beginner I guess you deserve some help…

    The first part, adding new numbers to the top and only keeping 4 elements in the array, is really easy:

    float myNewFloat = 1.0f;
    NSNumber *myNewNumber = [NSNumber numberWithFloat: myNewFloat];
    [myNSMutableArray insertObject: myNewNumber atIndex: 0];
    if (myNSMutableArray.count > 4) {
        [myNSMutableArray removeLastObject];
    }
    

    As for your calculations, you’ll want to enumerate your array, which means to step through it one element at a time. Here’s an example which computes the total sum of all elements, assuming you have an array of NSNumber objects:

    float myTotal = 0.0f;
    for (NSNumber *num in myNSMutableArray)
    {
        float fnum = [NSNumber floatValue];
        myTotal += fnum;
    }
    NSLog(@"Total of all elements in array is: %0.3f", myTotal);
    

    If you get stuck again, I’d recommend reading the documentation on Cocoa and NSObject in general. The basic stuff is really well written, so don’t be scared of it. Just takes time!

  34. Dan says

    @Simon and @Alexander.
    Thank you very much for the help you people provided! I came up with the code required to implement my task. But unfortunately i was stuck in a different area, I was trying this for last 24 hours and was unable to figure out my mistakes. I dnt have teachers to show this, so please help me to find the bug in this,
    Task.
    I wanted to create a counter that updates by one, every time it satisfied a condition in the if block of my code. To achieve this, i implemented a single tone instead of defining a global variable
    In .h file………

    @interface MONObject : NSObject {
       int *counterplus;
    }
    @property(nonatomic) int *counterplus;
    +(MONbject*) sharedinstance;
    @end
    

    In the .m file

    static MON object * sharedinstance;
    @implementation MONbject;
    @synthesize counterplus;
    
    +(MONbject*)sharedinstance
    {
       if(!sharedinstance){
          sharedinstance=[[MONObject alloc]init];
       }
       return sharedinstance
    }
    -(MONObject*) int
    {
       self=[super init];
       if(0!=self){
          counterplus=0;
       }
       return self;
    }
    @end
    

    I call the above as…

    if(condition){
       [MONObject sharedinstance].counterplus++;
    }
    

    when i try to print the value of it using NSlog, program stops and give this error
    “single stepping until exit from function objc_msgSend, which has no line number informatiopn. warning remote failure reply: E37

    I have no clue at all. I tried to used a global variable by defining it as extern and tried to
    increment. But it give the same error.
    Is there any way to achieve this? Can you please point me the error in it~
    Pleaseeeeeeeeeeeeee…………

  35. Alexander Alexander says

    @Dan
    First of all, you need to initialize the static variable shared instance with nil when you declare it, otherwise it might have a random value and within the method “sharedinstance” this object is never properly created (because here the object is created only when this pointer is nil).

    Also I guess your variable “counterplus” should be an “int” and not a pointer to an int (“int*”).

  36. Dan says

    @Alexander thank you for the help!

    When i call this if condition, The condition inside the if does not get executed. Code used to call an element

    if([NSNumber numberWithFloat:(([[[shared instance].y objectAtIndex:0] floatValue]))>10])
    {
    [MONObject sharedInstance1[.counterPlus++; //counter used to update the value
    }

    Counter value gets updated irrespective of the condition inside if? Is this the way to get a element from the array and compare?

    Note: i have created a single tone array! Please help me!
    Seems like the there is a bug inside the condition! Any thought?

  37. Alexander Alexander says

    @Dan
    Yes, I believe that this is not working. Maybe you should try to split up this line into multiple statements instead of squeezing all into one line. Then it will be more likely that you’re setting the brackets correctly ;-)

    What you’re actually doing here is to get a float value from an array “y”, check if this is larger than 10 and the result (a boolean “YES” or “NO”) is then used to create a NSNumber again… where the BOOL is interpreted as float.
    So the condition is actually only checking if creating a float from a BOOL did create an object.

    My advice, especially if you are new to programming:

    • Use the debugger to check the content of variables.
    • Split up lines into more basic calculations and assign the intermediate results to separate variables. This way you can check these intermediate results and you’ll be able to find the location where it starts to go wrong much more easier. Such a line as in your code where everything is squeezed into one line makes it only hard to find errors.
    • Watch out for compiler warnings. If the compiler warns you about something, look closer, it almost always has a reason that there’s a warning. And usually there warnings already give you an idea about what is wrong.
  38. Keary says

    I am afraid that your may be misleading your readers. Your algorithm will present a different row placement depending on whether the source row is before or after the destination row, and hence provide an inconsistent user experience. Specifically, consider the content:
    0 1 2 3 4 5 6

    Moving row 2 to row 5 results in:
    0 1 3 4 5 2 6
    or the row being placed *after* the target row.

    While moving row 5 to row 2 results in:
    0 1 5 2 3 4 6
    or the row being placed *before* the target row.

  39. Alexander Alexander says

    @Keary
    Please think about it again. The solution is doing exactly what one would expect. The task is to move the element from the source index exactly to the destination index, not before or after the element at this index. Which means the elements between the source and target index have to move one step to the beginning or end, depending where the source and destination is.

  40. Quinn Taylor says

    For what it’s worth, radar://4800587 requests -[NSMutableArray moveObjectsAtIndexes:toIndex:], like NSMutableOrderedSet has. Please file dupes if you’d like to see this method.



Some HTML is OK

or, reply to this post via trackback.