57

I know I can create an NSArray with @[@"foo", @"bar"] or an NSDictionary with @{@0 : @"foo", @1 : @"bar"}.

Is there a literal syntax for creating an NSMutableArray or an NSMutableDictionary?

jscs
  • 63,694
  • 13
  • 151
  • 195
ma11hew28
  • 121,420
  • 116
  • 450
  • 651
  • Just don't forget that its `NSDictionary *dictionary = @{@"key" : @"value"};`, might be confusing with he way you have it written. Different from `:objectsWithKeys`. – serge-k Mar 15 '16 at 16:13

5 Answers5

104

There isn't a built in way, but I just usually use mutableCopy like this:

NSMutableArray *array = [@[ @"1", @"2", @"3" ] mutableCopy];
pkamb
  • 33,281
  • 23
  • 160
  • 191
danielbeard
  • 9,120
  • 3
  • 44
  • 58
  • 5
    This seems less efficient and not much shorter than `[NSMutableArray arrayWithObjects:@"1", @"2", @"3", nil]`. – ma11hew28 Sep 19 '12 at 01:19
  • 1
    Or alternatively: "NSMutableArray *array = [NSMutableArray arrayWithArray:@[ @"1", @"2", @"3" ]];" - though I like your example better :) – Marchy Nov 28 '12 at 18:45
  • There is a builtin way in [`NSJSONSerialization`](http://stackoverflow.com/a/16287860/1971013). – meaning-matters Apr 29 '13 at 20:44
  • @meaning-matters that is definitely not a literal syntax. – danielbeard Apr 29 '13 at 23:45
  • Got to agree with @MattDiPasquale's comment; this is barely shorter than `NSMutableArray arrayWithObjects:...`, is presumably less efficient for what little it matters, and - to my eye - slightly less readable too. A concise literal syntax would've been nice, but if this is the nearest thing to it, then I think I'll stick to the less hacky-feeling `arrayWithObjects` for initialising mutable arrays. – Mark Amery Jul 29 '13 at 17:05
  • 1
    @MarkAmery How is that less hacky? The literal syntax expands to `+[NSArray arrayWithObjects:count:]`, not `arrayWithObjects` so the literal syntax validates that all items are non-nil. – danielbeard Jul 29 '13 at 17:31
  • @danielbeard Creating an unnecessary intermediate object and then taking a copy of it just to save a few characters compared to directly creating the `NSMutableArray` with one of its own initialiser methods is what feels hacky to me. – Mark Amery Jul 29 '13 at 17:52
  • As meaning-matters commented on his own NSJSONSerialization reply, mutableCopy only makes a shallow copy. That may be sufficient, but if not his NSJSONSerialization solution is the only general one so far. – ecotax Sep 02 '14 at 09:32
  • 1
    You can also do `NSMutableArray *array = @[].mutableCopy;` which seems more readable. – Matt Mc Jan 06 '15 at 03:28
18

No. Just as how there isn't a syntax for creating an NSMutableString either. Mutable objects are not particularly suited to literal values.

Lily Ballard
  • 182,031
  • 33
  • 381
  • 347
  • 20
    Have to disagree with that last sentence. Programming in Python, for example, collections are created literally and mutable by default. It can be very handy. – jscs Sep 14 '12 at 06:27
  • @JoshCaswell: Does python even have immutable collections? – Lily Ballard Sep 14 '12 at 07:16
  • 6
    Yes, there's things called ["tuples"](http://docs.python.org/tutorial/datastructures.html#tuples-and-sequences) which are immutable. `> (1, 2, 3) # tuple (immutable array)` `> [1, 2, 3] # list (mutable array)` – jscs Sep 14 '12 at 07:49
  • 2
    as posted below you do the following `NSMutableArray *list = [@[] mutableCopy];` i.e. you add mutableCopy at the end. That is how the literal is specified – maninvan Jan 10 '16 at 20:32
  • 1
    @maninvan That's not a literal. That's an expression that uses a literal. – Lily Ballard Jan 12 '16 at 23:07
  • You are correct in semantics. But from what happens from the programming perspective is that the expression is how you initialize a mutable object with some data in a short form syntax. The compiler will optimize this so it's probably reduced cpu instructions and memory copies. And it's less to write. It's more succinct. Which is what is desired. The answer 'no' is technically correct but not as helpful as showing the expression. – maninvan Jan 20 '16 at 23:00
  • @maninvan No, the compiler does not optimize that. Obj-C does not *allow* the compiler to optimize that. It's required to construct an immutable array from the literal, and then invoke `mutableCopy` on the result. The expression `[@[] mutableCopy]` simply cannot be called a literal. Even if it was optimized, it's still not a literal. – Lily Ballard Jan 21 '16 at 19:32
  • @KevinBallard You are correct, but I get his point. Using literal syntax and adding mutableCopy to the end looks better than most alternatives and is easy to type. It's not an answer to OP's question, but it can be a solution to OP's problem. – Kevin Apr 07 '16 at 07:43
17

But, is there a literal syntax for creating an NSMutableArray or an NSMutableDictionary?

No. Best alternative:

[@[ @"foo", @"bar"] mutableCopy]
Anne
  • 26,765
  • 9
  • 65
  • 71
9

Yes. But not quite. Take a look at this;

NSMutableArray *list = [@[] mutableCopy];

This creates a non-mutable array @[] and calls mutableCopy which returns a NSMutableArray *. In place of @[], you can give any array literal.

nidheeshdas
  • 1,097
  • 1
  • 11
  • 20
3

If you have a nested literal of arrays and dictionaries, you can turn this into a fully mutable version by going through NSJSONSerialization. For example:

NSArray* array = @[ @{ @"call" : @{ @"devices" : @[ @"$(devices)" ] } } ];
NSData* data   = [NSJSONSerialization dataWithJSONObject:array 
                                                 options:0 
                                                   error:nil];

NSJSONReadingOptions options = NSJSONReadingMutableContainers | 
                               NSJSONReadingMutableLeaves;
NSMutableArray* mutableArray = [NSJSONSerialization JSONObjectWithData:data 
                                                               options:options
                                                                 error:nil];

It's a bit of a detour, but at least you don't have to write out the code yourself. And the good thing is that NSJSONSerialization is very fast.

meaning-matters
  • 21,929
  • 10
  • 82
  • 142
  • 1
    Uh dude, JSONKit is faster. By 5x. Plus, this is too circuitous. Just use -mutableCopy, then you can optionally use -autorelease. – Nate Symer Jul 12 '13 at 00:00
  • 1
    @NathanielSymer Come on dude: The two year old [JSONKit](https://github.com/johnezang/JSONKit) readme --well maintained stuff btw-- itself says it was just 25% - 40% faster. And [`-mutableCopy` only does a shallow copy](http://stackoverflow.com/questions/8661770/does-mutablecopy-make-contained-objects-mutable). The only way is to do something 'circuitous'. – meaning-matters Jul 12 '13 at 04:48
  • 1
    you could also just do this: [[NSMutableArray alloc] initWithArray:@[@"A",@"B"]]. sorry for the downvote, but serializing and deserializing is insane and won't work with many types of objects. – kritzikratzi Apr 22 '14 at 13:23
  • 2
    @kritzikratzi First, insane? You're taking a very simple example, not my example. Please write out `@[ @{ @"call" : @{ @"devices" : @[ @"$(devices)" ] } } ]` and you'll see how insane that is. Second, this works for the types given in @MattDiPasquale's and my example. Even more, I explicitly mention that this works for literals. So please don't apologise! – meaning-matters Apr 26 '14 at 05:14