393

How do I convert NSMutableArray to NSArray in ?

Sam Spencer
  • 8,492
  • 12
  • 76
  • 133
marcy
  • 4,213
  • 3
  • 23
  • 14

9 Answers9

524
NSArray *array = [mutableArray copy];

Copy makes immutable copies. This is quite useful because Apple can make various optimizations. For example sending copy to a immutable array only retains the object and returns self.

If you don't use garbage collection or ARC remember that -copy retains the object.

Georg Schölly
  • 124,188
  • 49
  • 220
  • 267
  • 2
    this answer is great, but how can I tell from the docs that `copy` creates a normal NSArray and not an NSMutableArray? – Dan Rosenstark May 16 '10 at 23:52
  • 22
    @Yar: You look at [the documentation of *NSCopying*](http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Protocols/NSCopying_Protocol/Reference/Reference.html#//apple_ref/occ/intfm/NSCopying/copyWithZone:) It states there: **copyWithZone** *The copy returned is immutable if the consideration “immutable vs. mutable” applies to the receiving object; otherwise the exact nature of the copy is determined by the class.* – Georg Schölly Nov 20 '10 at 09:58
  • 1
    Very neat - I prefer this to m5h's solution. Both terse and more efficient. – David Snabel-Caunt Aug 19 '11 at 09:40
  • 1
    I agree David, updated my response to point to this response. – hallski Feb 17 '13 at 11:09
  • @Georg I see lots of up votes for your comment, but frankly I don't have a clue what that Apple doc comment actually means. How does one determine if "the consideration immutable vs mutable applies to the receiving object"? – Bradley Thomas Sep 26 '14 at 16:56
  • 2
    @Brad: That just means classes that have both mutable and immutable implementations. E.g. `NSArray` & `NSMutableArray`, `NSString` & `NSMutableString`. But not for example `NSViewController` which always contains mutable state. – Georg Schölly Sep 28 '14 at 09:49
362

An NSMutableArray is a subclass of NSArray so you won't always need to convert but if you want to make sure that the array can't be modified you can create a NSArray either of these ways depending on whether you want it autoreleased or not:

/* Not autoreleased */
NSArray *array = [[NSArray alloc] initWithArray:mutableArray];

/* Autoreleased array */
NSArray *array = [NSArray arrayWithArray:mutableArray];

EDIT: The solution provided by Georg Schölly is a better way of doing it and a lot cleaner, especially now that we have ARC and don't even have to call autorelease.

Community
  • 1
  • 1
hallski
  • 123,625
  • 4
  • 33
  • 21
  • 7
    Can I just do (NSArray *) myMutableArray ? – Vojto Nov 13 '10 at 21:37
  • 2
    Yes, as NSMutableArray is a subclass of NSArray that is valid. – hallski Nov 15 '10 at 11:53
  • 4
    However, casting to (NSArray *) still allows a cast back up to (NSMutable *). Ain't that the case? – sharvey Nov 20 '10 at 02:22
  • 1
    @sharvey: Yes, that's correct. You'll get a warning if you don't cast but assign a superclass to a subclass directly. Usually, you want to return a immutable copy, because that's the only way to be sure, that your array really won't get modified. – Georg Schölly Nov 20 '10 at 09:55
  • 1
    Formerly That's known as "Upcasting" (NSArray *) myMutableArray and the inverse is called "Downcasting" – Francisco Gutiérrez Feb 07 '13 at 16:52
116

I like both of the 2 main solutions:

NSArray *array = [NSArray arrayWithArray:mutableArray];

Or

NSArray *array = [mutableArray copy];

The primary difference I see in them is how they behave when mutableArray is nil:

NSMutableArray *mutableArray = nil;
NSArray *array = [NSArray arrayWithArray:mutableArray];
// array == @[] (empty array)

NSMutableArray *mutableArray = nil;
NSArray *array = [mutableArray copy];
// array == nil
Richard Venable
  • 8,310
  • 3
  • 49
  • 52
  • This is so wrong. Just checked. [mutableArray copy] also returns an empty array. – durazno Jul 22 '16 at 06:27
  • 3
    @durazno It is not wrong. Double check your test. If [mutableArray copy] is returning an empty array, then mutableArray must have been an empty array. My answer only applies when mutableArray is nil. – Richard Venable Jul 22 '16 at 16:29
  • Although this is true, I feel like you're missing a more fundamental truth in your example. Since you've assigned mutableArray to be nil, `[mutableArray copy]` can be simplified to `[nil copy]`. In objective-c *any* message sent to nil will always be nil. It's important to remember the distinction between "nothing" and "an array with nothing in it". – mkirk Jul 27 '16 at 00:18
  • 1
    @mkirk Let's not distract from the question at hand: "How do I convert NSMutableArray to NSArray?" There are 2 primary solutions, and the primary difference between them is how they behave when the mutable array is nil. – Richard Venable Jul 27 '16 at 13:28
18

you try this code---

NSMutableArray *myMutableArray = [myArray mutableCopy];

and

NSArray *myArray = [myMutableArray copy];
Jerry Thomsan
  • 1,409
  • 11
  • 9
8

Objective-C

Below is way to convert NSMutableArray to NSArray:

//oldArray is having NSMutableArray data-type.
//Using Init with Array method.
NSArray *newArray1 = [[NSArray alloc]initWithArray:oldArray];

//Make copy of array
NSArray *newArray2 = [oldArray copy];

//Make mutablecopy of array
NSArray *newArray3 = [oldArray mutableCopy];

//Directly stored NSMutableArray to NSArray.
NSArray *newArray4 = oldArray;

Swift

In Swift 3.0 there is new data type Array. Declare Array using let keyword then it would become NSArray And if declare using var keyword then it's become NSMutableArray.

Sample code:

let newArray = oldArray as Array
Sakir Sherasiya
  • 1,562
  • 1
  • 17
  • 31
  • The Swift part is not entirely right. See [here](https://docs.swift.org/swift-book/LanguageGuide/CollectionTypes.html) and [here](https://developer.apple.com/documentation/foundation/nsmutablearray?changes=_6) for the difference between mutable/immutable `Array`s and `NSArray`/`NSMutableArray`s. They are not the same. – sleighty Jul 10 '18 at 21:00
3

In objective-c :

NSArray *myArray = [myMutableArray copy];

In swift :

 var arr = myMutableArray as NSArray
Vikram Biwal
  • 2,618
  • 1
  • 25
  • 36
1
NSArray *array = mutableArray;

This [mutableArray copy] antipattern is all over sample code. Stop doing so for throwaway mutable arrays that are transient and get deallocated at the end of the current scope.

There is no way the runtime could optimize out the wasteful copying of a mutable array that is just about to go out of scope, decrefed to 0 and deallocated for good.

Anton Tropashko
  • 5,486
  • 5
  • 41
  • 66
  • 1
    Assigning an `NSMutableArray` to an `NSArray` variable will not covert it (which is what the OP's question is about). Exposing such a value (for example as a return value or as a property) could result in very hard to debug issues if that value was unexpectedly mutated. This applies to both internal and external mutations since the mutable value is shared. – David Rönnqvist Oct 10 '18 at 11:38
  • To mutate that array you'd have to [NSMutableArray arrayWithArray:... or something like that. – Anton Tropashko Oct 11 '18 at 09:14
  • Not necessarily. Simply assigning it to a `NSMutableArray` variable is enough. Since the object never stopped being a mutable array, calling mutating methods on it will succeed and mutate the shared data. If on the other hand the original mutable array was copied—changing it to an immutable array—and then assigned to a `NSMutableArray` variable and had mutating methods called on it, those calls would fail with `doesNotRespondToSelector` errors because the object that received the mutating method call is in fact immutable and doesn't respond to those methods. – David Rönnqvist Oct 11 '18 at 10:41
  • ok, this might have some merit for developers that don't heed the compiler warning about tyope conversion assigments – Anton Tropashko Oct 12 '18 at 12:59
0

If you're constructing an array via mutability and then want to return an immutable version, you can simply return the mutable array as an "NSArray" via inheritance.

- (NSArray *)arrayOfStrings {
    NSMutableArray *mutableArray = [NSMutableArray array];
    mutableArray[0] = @"foo";
    mutableArray[1] = @"bar";

    return mutableArray;
}

If you "trust" the caller to treat the (technically still mutable) return object as an immutable NSArray, this is a cheaper option than [mutableArray copy].

Apple concurs:

To determine whether it can change a received object, the receiver of a message must rely on the formal type of the return value. If it receives, for example, an array object typed as immutable, it should not attempt to mutate it. It is not an acceptable programming practice to determine if an object is mutable based on its class membership.

The above practice is discussed in more detail here:

Best Practice: Return mutableArray.copy or mutableArray if return type is NSArray

Community
  • 1
  • 1
pkamb
  • 33,281
  • 23
  • 160
  • 191
0

i was search for the answer in swift 3 and this question was showed as first result in search and i get inspired the answer from it so here is the swift 3 code

let array: [String] = nsMutableArrayObject.copy() as! [String]
Amr Angry
  • 3,711
  • 1
  • 45
  • 37