NSArray initialization methods - ios

What is the difference between initializing array with
NSArray * array = [NSArray array];
and
NSArray * array = #[];

#[] is shorthand for:
id a = nil;
NSArray* array = [NSArray arrayWithObjects:&a count:0];
Which is really just shorthand for [NSArray array], for all intents and purposes.
This is a feature added in a particular version of the compiler (and doesn't actually require runtime support for this particular syntax).
It is not at all like the #"" shorthand in that #"" produces a compile time constant and will cause no messaging at runtime. In fact, #"" (any #"sequence") is a special case in that it emits a compile time constant that is realized in the runtime with zero messaging; zero dynamism. A #"..." is more similar to an Objective-C class than it is to a regular instance of an object.

NSArray * array = #[]; is the new way of doing NSArray * array = [NSArray array];

Related

NSArray alloc and init

I'm a little confused about NSArray initialization. The confusing thing is we have different init methods to init it, and if I just init it without giving any size and any objects, how compiler knows it object count or size in RAM.
What's the logic/difference behind init method? Can anybody explain it briefly?
Edit:
NSArray *sortedArray = [[NSArray alloc] init];
NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:#"quota" ascending:NO];
NSArray *sortDescriptors = [NSArray arrayWithObject:descriptor];
//How is that possible to modifying to immutable object
sortedArray = [sortedContainers sortedArrayUsingDescriptors:sortDescriptors];
I know the differences between NSArray and NSMutableArray and I'm not looking for the differences. I just wonder how that code can compile/execute without an error.
[[NSArray alloc] init] returns an empty, immutable array. There's nothing surprising about that. What else would you return from a method to mean "no items?"
how compiler knows it object count or size in RAM
The object count is 0. It's empty. Its size in RAM is the size of an empty NSArray. NSArray is an object, so it has some standard objc_object overhead, plus some instance variables for NSArray.
That said, as an implementation detail, Cocoa optimizes this out and always returns the same singleton for all empty NSArray objects. You can demonstrate this with:
NSArray *a = [[NSArray alloc] init];
NSArray *b = [[NSArray alloc] init];
NSLog(#"%p", a);
NSLog(#"%p", b);
if (a == b) { // Pointer equality, not isEqual:
NSLog(#"Same object");
}
So, what is this doing?
NSArray *sortedArray = [[NSArray alloc] init]; // (1)
// ... code that doesn't matter ...
sortedArray = [sortedContainers sortedArrayUsingDescriptors:sortDescriptors]; // (2)
At line 1, you're creating a local pointer variable, sortedArray and pointing it to an empty array (it happens to be a singleton, but that's an optimization detail).
At line 2, you create a completely different NSArray by calling sortedArrayUsingDescriptors:. You then point sortedArray at that object. ARC sees that you're no longer pointing at the empty array, and releases it. (Again, optimization details jump in here and the actual steps may be different, but the effect will be the same.)
The correct code here would be:
// ... Code that sets up sortDescriptors ...
NSArray *sortedArray = [sortedContainers sortedArrayUsingDescriptors:sortDescriptors];
Now, in very modern ObjC, you won't see [[NSArray alloc] init] written out very often. It's easier to write #[] (which is really just syntactic sugar for a method call). But before array literals were added to language, [[NSArray alloc] init] or [NSArray array] were reasonably common.
If you just init NSArray without giving any size and any objects it will remain empty during lifetime. NSArray is immutable
If your question is about NSMutableArray, read here at the SO

Capitalized NSArray of Strings?

I have an NSarray called array. And it look like this
array = #[#"one", #"two", #"three"];
I want this array to be capitalized. What is the best way to go about this. I can only think of making an NSMutableArray called mutableArray.
And do something like this
for(int i = 0; i < array.lenght; i++) {
self.mutableArray = addObject:[array[i] capitalizedString];
}
Or is there another better way?
The magic method you are looking for does in fact exist.
NSArray *array = #[#"one", #"two", #"three"];
NSArray *capArray = [array valueForKeyPath:#"capitalizedString"];
SWIFT
You Can use map
let array = ["one", "two", "three"]
let upercaseArray = array.map({$0.uppercased()})
now you have upercaseArray like ["ONE","TWO","THREE""]
What you really want is a sort of transform method, which takes an array and a selector, then returns an array of the results of performing that selector on each object. Unfortunately that doesn't exist in vanilla objective-C.
Your approach is generally fine, but I would be careful of two points. Firstly, make sure you create the NSMutableArray with the capacity of the NSArray you are copying, as this will avoid any reallocation overhead as you add objects to it. Secondly, you might want to copy the mutable array so you end up with an immutable NSArray as the final result.
So I would use something like this:
- (NSArray *)capitalizeStringArray:(NSArray *)array {
// Initialize tempArray with size of array
NSMutableArray *tempArray = [NSMutableArray arrayWithCapacity:array.count];
for (NSString *str in array) {
[tempArray addObject:[str capitalizedString]];
}
return [tempArray copy]; // convert back to NSArray]
}
You can convert this to a category method on NSArray if you like, and generalize it to use other selectors if you wish.
There's about a gazillion ways to handle this. For small arrays, pick whichever you find easier to understand.
I'd probably use code like this:
- (NSMutableArray *) capitalizedArrayFromArrayOfStrings: (NSArray*) array;
{
NSMutableArray *result = [NSMutableArray arrayWithCapacity: array.count];
for (NSString *string in array)
{
if ([string isKindOfClass: [NSString class]]
[result addObject: [string capitalizedString];
}
}
Creating your array with the correct capacity at the beginning enables the array to allocate enough space for all it's future elements and saves it having to allocate more space later.
Using for..in fast enumeration syntax is more efficient than using array indexing, but for short arrays the difference is small. The code is also simpler to write and simpler to read, so I prefer that syntax where possible.
As Alex says, you could also create a category method on NSArray that would return a capitalized version of your array, or even a category on NSMutableArray that would replace the strings in the array "in place".
Works like charm.
NSString *myString = YOUR_ARRAY.uppercaseString;
[myNSMutableArray addObject:myString];

NSComparator changes behaviour while sorting NSArray when ARC is enabled

I am using NSComparator to sort objects in NSArray. When the code is not ARC enabled then comparator gives different results to when code is ARC enabled.
Following is my Code snippet:
- (NSArray *)sortedToolsInfoArrayWithKey {
NSArray *aReturnVal = nil;
NSArray *aToolsInfoArray = [[NSArray alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"ToolsData" ofType:#"plist"]];
aReturnVal = [aToolsInfoArray sortedArrayUsingComparator:(NSComparator)^(NSDictionary *a, NSDictionary *b) {
[[a valueForKey:#"selectionCount"] compare:[b valueForKey:#"selectionCount"]];
}];
[aToolsInfoArray release];
return aReturnVal;
}
This same method is written in Non-ARC code where I need the same kind of sorted array, Note that, my requirement is I need to sort the same array picked from same pList file in two different files, one of the file is ARC enabled while other file isnt ARC enabled.
But when I am doing that, I am getting exactly opposite sorting order and when I disable the ARC , the problem is solved.
I am not being able to understand the logic behind different behaviour of NSComparator to sort arrays in ARC and non-ARC enabled files.
Thanks..
You don't return a value from your comparator block:
aReturnVal = [aToolsInfoArray sortedArrayUsingComparator:^(id a, id b) {
NSDictionary *adict = (NSDictionary *)a;
NSDictionary *bdict = (NSDictionary *)b;
return [[adict objectForKey:#"selectionCount"] compare:[bdict objectForKey:#"selectionCount"]];
}];
(plus use objectForKey rather than valueForKey, which is the KVC accessor).
EDIT Missed the block syntax you were using is incorrect too (thanks to #Ivan Genchev).
As a follow-on to the answer by "trojanfoe", you have the wrong syntax for the block. You need:
aReturnVal = [aToolsInfoArray sortedArrayUsingComparator:^NSComparisonResult(NSDictionary *a, NSDictionary *b) {
return [[a valueForKey:#"selectionCount"] compare:[b valueForKey:#"selectionCount"]];
}];
Let Xcode's code completion fill all of that in for you. It avoids the kind of mistakes you have.
You can also use NSSortDescriptor to sort your array.
NSArray *testArray = #[#{#"selectionCount":#"3"},
#{#"selectionCount":#"1"},
#{#"selectionCount":#"7"},
#{#"selectionCount":#"2"}];
NSLog(#"testArray: %#", testArray);
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"selectionCount" ascending:YES];
NSArray *sortedTestArray = [testArray sortedArrayUsingDescriptors:#[sortDescriptor]];
NSLog(#"sortedTestArray: %#", sortedTestArray);

NSArray sortedArrayUsingDescriptors: New Copy or Retain?

According to NSArray class reference there are 4 type of methods to sort array:
1- sortedArrayUsingComparator:
2- sortedArrayUsingSelector:
3- sortedArrayUsingFunction:context:
4- sortedArrayUsingDescriptors:
For first three methods it mentioned :
The new array contains references to the receiving array’s elements, not copies of them.
But for the forth method (descriptor) it mentioned:
A copy of the receiving array sorted as specified by sortDescriptors.
But following example shows like the other 3 methods, descriptor also retain original array and do not return a new copy of it:
NSString *last = #"lastName";
NSString *first = #"firstName";
NSMutableArray *array = [NSMutableArray array];
NSDictionary *dict;
NSMutableString *FN1= [NSMutableString stringWithFormat:#"Joe"];
NSMutableString *LN1= [NSMutableString stringWithFormat:#"Smith"];
NSMutableString *FN2= [NSMutableString stringWithFormat:#"Robert"];
NSMutableString *LN2= [NSMutableString stringWithFormat:#"Jones"];
dict = [NSDictionary dictionaryWithObjectsAndKeys: FN1, first, LN1, last, nil];
[array addObject:dict];
dict = [NSDictionary dictionaryWithObjectsAndKeys: FN2, first, LN2, last, nil];
[array addObject:dict];
// array[0].first = "Joe" , array[0].last = "Smith"
// array[1].first = "Robert" , array[1].last = "Jones"
NSSortDescriptor *lastDescriptor =[[NSSortDescriptor alloc] initWithKey:last
ascending:YES
selector:#selector(localizedCaseInsensitiveCompare:)];
NSSortDescriptor *firstDescriptor =[[NSSortDescriptor alloc] initWithKey:first
ascending:YES
selector:#selector(localizedCaseInsensitiveCompare:)];
NSArray *descriptors = [NSArray arrayWithObjects:lastDescriptor, firstDescriptor, nil];
NSArray *sortedArray = [array sortedArrayUsingDescriptors:descriptors];
// array[1] == sortedArray[0] == ("Robert" , "Jones")
// comparing array entries whether they are same or not:
NSLog(#" %p , %p " , [array objectAtIndex:1] , [sortedArray objectAtIndex:0] );
// 0x10010c520 , 0x10010c520
it shows objects in both arrays are same,
"A copy of the receiving array sorted as specified by sortDescriptors" means that the array object is copied not the elements in the array. The reason the documentation uses the word "copy" is to make it clear that the returned array is not the same array instance as the receiver.
Elements in an array are never copied in Cocoa with the exception of initWithArray:copyItems:YES which will copy the first level items in the original array to the new array. Even then, this copy is done by calling copyWithZone: on the elements, so caveats apply depending on what elements are in your array.
Note that Cocoa is reference counted, so the concept of "deep copies" is not inherently built in for a reason. This is also (in part) the reason why array objects in cocoa come in two flavors (NSArray and NSMutableArray) and are usually immutable (NSArray) instead of as in other languages where there is not usually a concept of immutable and mutable arrays.
see this SO answer for how to get a "deep copy" of an NSArray.

Stick NSString[] into NSArray

Why can't I stick NSString[] into NSArray? I get the following error "Implicit conversion of an indirect pointer to an Objective-C pointer to 'id' is disallowed with ARC"
Here's the code:
NSString *s1, *s2;
NSString *cArray[]={s1, s2};
NSMutableArray *dataArray = [[NSMutableArray alloc] init];
[dataArray addObject:cArray];
You cannot do it, because the ownership of cArray cannot be transferred.
If it is a local variable, it would disappear as soon as its scope ends, leaving your mutable array with a dangling reference.
Even if it is a global, there would be a problem, because your NSMutableArray would not know how to release the C array properly.
Objective C wants to protect you from situations like that as much as possible, providing nice classes such NSArray to make your job easier:
NSMutableArray *dataArray = [NSMutableArray array];
NSString *s1 = #"hello", *s2 = #"world";
// You can choose another constructor as you see fit.
NSArray *cArray = #[s1, s2];
[dataArray addObject:cArray];
NSArray holds objects. A C array is not an object, so you can't put it in an NSArray. If you want to create an NSArray out of a C array, you can use the arrayWithObjects:count: constructor.

Resources