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
Related
There seem to be different methods of instantiating NSArrays (same for NSDictionary and some others).
I know:
[NSArray array]
[NSArray new]
#[]
[[NSArray alloc] init]
For readability reasons I usually stick with [NSArray array], but what is the difference between all those, do they all really do the same?
Results are same for all of them, you get a new empty immutable array. These methods have different memory management implications though. ARC makes no difference in the end, but before ARC you would have to use the right version or send appropriate retain, release or autorelease messages.
[NSArray new], [[NSArray alloc] init] return an array with retain count is 1. Before ARC you would have to release or autorelease that array or you'd leak memory.
[NSArray array], #[] return an already autoreleased array (retain count is 0). If you want it to stick around without ARC you'd have to manually retain it or it would be deallocated when the current autorelease pool gets popped.
[NSArray array] : Create and return an empty array
[NSArray new] : alloc, init and return a NSArray object
#[] : Same as 1.
[[NSArray alloc] init] : Same as 2.
Different between [NSArray array] and [[NSArray alloc] init] is if there are non-ARC:
[NSArray array] is an autorelease object. You have to call retain if you want to keep it. E.g when you return an array.
[[NSArray alloc] init] is an retained object. So you don't have to call retain more if you want keep it.
With ARC, they are same.
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);
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.
If I've to reinitialize a NSArray with others values, is it right to do this?
NSArray *array = [[NSArray alloc] initWithObjects:obj1, obj2, nil];
// ...
// some code
// ...
array = [[NSArray alloc] initWithObjects:obj3, obj4, nil];
thanks
Yes this is absolutely right. The new object is completely different than the previous. The object pointer now points to a new object and the old one will be released, since you are using ARC.
It is not exactly the same as reinitializing because you throw away the object and getting a new, but NSArray is immutable so this is the only way to "reinitialize" it.
Your code does not re-initialze an NSArray. It just assigns a new object to the variable array. That's fine.
I thinks this code may help you. At least, I thing this may be a suitable solution, especially if you are using ARC:
NSObject *obj1 = [NSNull null];
NSObject *obj2 = [NSNull null];
NSMutableArray *arrayObj = [NSMutableArray arrayWithObjects:obj1, obj2, nil];
[arrayObj removeAllObjects];
arrayObj = [NSMutableArray arrayWithObjects:obj1, obj2, nil];
I hope it helps you :)
you can reinitialize NSArray with same name ,you will not get any error or warning but the latest objects gets replaced with previous objects.The previous objects overwrites. For this you have to use ARC otherwise memory problem will occurs.
What is the difference between
NSMutableArray* p = [[NSMutableArray alloc] initWithObjects:...]
and
NSMutableArray* p = [NSMutableArray arrayWithObjects:...]
In the first one, you have the ownership of array object & you have to release them.
NSMutableArray* p = [[NSMutableArray alloc] initWithObjects:...];
[p release];
& last one you dont need to release as you don't have the ownership of array object.
NSMutableArray* p = [NSMutableArray arrayWithObjects:...]; //this is autoreleased
If you call release in this, then it will crash your application.
[NSMutableArray arrayWithObjects:] is the same as [[[NSMutableArray alloc] initWithObjects:] autorelease]
In practice, there is no difference if you're on ARC.
The latter basically is just a shorthand for [[NSMutableArray alloc] initWithObjects: ...], except the returned array is autoreleased (which is important if you're still doing manual reference counting).
What I think the difference is that: initWithObjects is a instance method, and arrayWithObject is a class method.