return a copy or a mutable object - objective-c

Which variant is more correctly?
//first variant
- (NSArray*) someArray
{
NSMutableArray* mutArr = [[NSMutableArray alloc] init];
//...some operations with mutArr
NSArray* retArray = [mutArr copy];
[mutArr release]; //remove mutArr
return [retArray autorelease];
}
//second variant
- (NSArray*) someArray
{
NSMutableArray* mutArr = [[NSMutableArray alloc] init];
//...some operations with mutArr
return (NSArray*)[mutArr autorelease];
}

The answer is, how much of a problem will there be if the array is changed after you return it?
If you are creating a mutable array inside your method then returning it, never to use it again, I think it is fine to return the mutable version. The fact that your method declares a return type of NSArray only means you won't guarantee the array will be mutable. You don't have to guarantee that it is immutable.
On the other hand, if you are returning an array that your class uses internally, it is much safer to return an immutable copy. In your example above, that does not appear to be the case.
The consumer of the array, should they want to keep a reference, should use copy instead of retain; if the array is mutable, they will get an immutable copy. If it is already immutable, only the reference count will be increased. So there's no penalty for copying an immutable object.
In other words, your second variant is fine. (Although the cast to (NSArray *) is totally unnecessary.)

The first one is better, in my opinion. It ensures immutability.

I'm assuming that you mean for mutArr and names to be the same array. If that's the case, then the first is more correct, since you don't really need to make a copy of it.
You can just return mutArray if you want; since NSMutableArray is a subclass of NSArray, returning one will work. If you want to return a regular, non-mutable NSArray, I would recommend the following:
(NSArray*)someArray {
NSMutableArray* mutArr = [[[NSMutableArray alloc] init ] autorelease];
// your operations here
return [NSArray arrayWithArray:mutArr];
}

Related

NSArray of NSDictionary in Objective C

I am trying to develop a function to add Dictionaries into Arrays.
This function looks this way but doesn't work:
in the implementation of the CBLModel I have the property emailList. The property I want to be an NSArray of NSDictionary's.
-(void) addEmail: (NSDictionary*) c{
NSMutableArray* mutArrayEmail= [NSMutableArray arrayWithObject: self.emailList];
[mutArrayEmail addObject:c];
self.emailList = [NSArray arrayWithObject:mutArrayEmail];
}
And the implementation would be something like:
[persInf addEmail: email1];
[persInf addEmail: email2];
[persInf addEmail: email3];
Any help?
EDIT
Thanks to everyone for your reply.
I have inserted in the main code the following (in order to not test everything easier):
NSDictionary *inventory = #{
#"Mercedes-Benz SLK250" : [NSNumber numberWithInt:13],
#"Mercedes-Benz E350" : [NSNumber numberWithInt:22],
#"BMW M3 Coupe" : [NSNumber numberWithInt:19],
#"BMW X6" : [NSNumber numberWithInt:16],
};
NSMutableArray *nsmut= [[NSMutableArray alloc] init];
[nsmut addObject: inventory];
[nsmut addObject: inventory];
[nsmut addObject: inventory];
NSArray *nsarray= [NSArray arrayWithArray:nsmut];
[persInf setEmailList:nsarray];
After this I get an error. I post this code sample as may be easier to see where the error can be
You only want to create the array if it doesn't already exist:
- (void)addEmail:(NSDictionary *) c{
if (!self.emailList) {
self.emailList = [[NSMutableArray alloc] init];
}
[self.emailList addObject:c];
}
This is lazy initialization as the array is created on first use. This is fine, as long as you realise that the array can be nil at times in the object's lifetime (which is less of a problem in Objective-C than in other languages).
A more conventional approach is to override an init method and allocate the array there.
Declare your NSMutableArray outside of your addEmail: method. You can use addObject: as-is.
Since emailList is a property you should override the getter to lazily instantiate it when its needed, like so.
- (NSMutableArray *)emailList {
if(!_emailList) {
_emailList = [NSMutableArray array];
}
return _emailList
}
To be thorough you could write that code using the ternary and make it a bit shorter:
- (NSMutableArray *)emailList {
return _emailList = _emailList ?: [NSMutableArray array];
}
Finally once you've overrode the getter your addEmail method becomes:
- (void)addEmail: (NSDictionary*)c {
[self.emailList addObject:c];
}
If you have to store emailList as an NSArray, I guess you need to set it with
self.emailList = [NSArray arrayWithArray:mutArrayEmail];
I'm far from an expert, though, so there may be problems with this approach. I'd welcome comments to that effect!
You can declare additional property of NSMutableArray (it can be in your class extension):
#property(nonatomic, copy)NSMutableArray *tempArray;
Initialize it in the init
_tempArray = [[NSMutableArray alloc] init];
And change your setter method to:
-(void) addEmail: (NSDictionary*) c{
[self.tempArray addObject:c];
}
And change your getter to:
- (NSArray *)emailList {
return [NSArray arrayWithArray:[self.tempArray copy]];
}
I see you are new to SO, so a little (friendly!) advice: you need to provide more detail in your questions of exactly what you've tried and what has gone wrong; e.g. "After this I get an error." does not help others help you.
Now reading your question and comments two things pop out: assertions about CBLModel, and how to add items to an array.
A. First, CBLModel. You've stated a few times you need an NSArray as:
I can't declare it as Mutable array or it won't work in the CBLModel
What do you mean by this? If you are saying that the methods in CBLModel take NSArray type arguments then you can pass NSMutableArray's as an NSMutableArray is an NSArray (i.e. it is a subclass) - this is one of the central pillars of inheritance-based languages like Objective-C.
If instead you mean that CBLModel does not function correctly if you pass it an NSMutableArray, say because you yourself mutate the array after passing it and that effects CBLModel in an invalid way[*], then you can simply create an immutable copy ([NSArray arrayWithArray:someMutableArray]) or just another mutable copy ([someMutableArray copy]) which you pass only to CBLModel.
B. Adding items to an array. Let's look at your code, line by line, so you can see what it is doing:
NSMutableArray* mutArrayEmail= [NSMutableArray arrayWithObject: self.emailList];
This creates a new NSMutableArray initialised with a single element obtained from self.emailList, and stores a reference to it in mutArrayEmail. E.g. if before execution self.emailList references an array [1, 2, 3], then after execution mutArrayEmail references a (mutable) array [ [1, 2, 3] ] - a single element array whose element is itself an array.
[mutArrayEmail addObject:c];
You add the object referenced by c, so using the same example you now have [ [1, 2, 3], c ] - an array with two elements.
self.emailList = [NSArray arrayWithObject:mutArrayEmail];
And finally you create a new array with the single object referenced by mutArrayEmail. Keeping the same example you now have [ [ [1, 2, 3], c ] ]. That is probably not what you wanted, or what CBLModel accepts.
The class method arrayWithArray: creates an array with the same elements as another array. If you used that method the above example would produce [1, 2, 3, c], which is probably what you want.
HTH
[*] Any classes which accept objects of types with mutable subtypes [arrays, strings, etc.] would normally be written such that a mutation after the object is passed does not effect the behaviour in undesirable ways, i.e. the classes would copy objects if needed. This it is recommended that properties of such type are defined with the copy attribute.
It's a bad idea, generally, to expose a mutable array to the outside world if it's part of an object's state. It means the object's state can be amended without the object knowing about it.
Here's how I would do this:
Have a readonly property that lets people get the array.
Have a method to add objects to the array.
#interface MyClass : NSObject
#property (nonatomic, readonly, strong) NSArray* emailList;
-(void) addEmail: (NSDictionary*) newEmail;
#end
In the implementation, have an mutable instance variable to back the property, initialise it in your designated initialiser. Implement your add method.
// All code assumed to be using ARC
#implementation MyClass
{
NSMutableArray* _emailList;
}
#synthesize emailList = _emailList;
-(id) init
{
self = [super init];
if (self != nil)
{
_emailList = [[NSMutableArray alloc] init];
}
return self;
}
-(void) addEmail: (NSDictionary*) newEmail
{
[_emailList addObject: newEmail];
}
#end
Although, the actual object returned by the property is an NSMutableArray the rule is to program to the interface, which means that when an interface says it returns an immutable object, even if you know it's really a mutable subclass, you are not allowed to send it mutating methods.
If, however, you are paranoid, implement the getter for the property like this:
-(NSArray*) emailList
{
return [_emailList copy];
}

understanding mutable copy

Due to an example from Apress,for a search criteria,it has a soultion:it will firstly get a mutable copy and search the string and then will remove objects that are not in range of that string.
in the Mutable DeepCopy it has function as follow:
#implementation NSDictionary(MutableDeepCopy)
-(NSMutableDictionary *)mutableDeepCopy{
NSMutableDictionary *returnDict=[[NSMutableDictionary alloc]initWithCapacity:[self count]];
NSArray *keys=[self allKeys];
for(id key in keys)
{
id oneValue=[self valueForKey:key];
id oneCopy=nil;
if([oneValue respondsToSelector:#selector(mutableDeepCopy)])
oneCopy=[oneValue mutableDeepCopy];
else if([oneValue respondsToSelector:#selector(mutableCopy)])
oneCopy=[oneValue mutableCopy];
if(oneCopy==nil)
oneCopy=[oneValue copy];
[returnDict setValue:oneCopy forKey:key];
}
return returnDict;
}
#end
when I don't understand any code I will debug it,so I debugged it and in the beginnings when it wants to show the whole array,in the if statement it will go to oneCopy=[oneValue mutableCopy]; I want to know that why it will choose this selector and not the MutableDeep selector? I can't understand this function totally.what is the main purpose of this function?
for searching it has this function
-(void)handleSearchForTerm:(NSString *)searchTerm
{
NSMutableArray * sectionsToRemove=[[NSMutableArray alloc]init];
[self resetSearch];
for(NSString * key in self.keys)
{
NSMutableArray * array=[names valueForKey:key];
NSMutableArray *toRemove=[[NSMutableArray alloc]init];
for(NSString * name in array)
{
if([name rangeOfString:searchTerm options:NSCaseInsensitiveSearch].location==NSNotFound)
[toRemove addObject:name];
}
if([array count]==[toRemove count])
[sectionsToRemove addObject:key];
[array removeObjectsInArray:toRemove];
}
[self.keys removeObjectsInArray:sectionsToRemove];
[table reloadData];
}
and for reset the search and recovering whole it has this function below.
my problem is that in self.keys we removed objects with help of handleSearchForTerm but when we go to resetsearch it will return again those whole keys without any removal maybe my main problem is that I can't understand Mutablecopy and DeepMutableCopy.
-(void)resetSearch{
NSMutableDictionary *allNamesCopy=[self.allNames mutableDeepCopy];
self.names=allNamesCopy;
NSMutableArray *keyArray=[[NSMutableArray alloc]init];
[keyArray addObjectsFromArray:[[self.allNames allKeys]sortedArrayUsingSelector:#selector(compare:)]];
self.keys=keyArray;
}
They both have some compare & contrast features...
First comparing: Both are mutable, you can alter the object itself.
Second contrasting/differences:
MutableCopy: Similar to Call-by-Reference.
MutableDeepCopy: Similar to Call-by-Value.
A copy of a container (e.g. NSArray) copies only the pointers to objects and sends them retain.
A mutableCopy does the same thing, but the resulting container can be modified, i.e. Pointers can be added or removed.
A deep copy would also make copies of the individual elements.
Take for example an NSArray of NSMutableString instances. You cannot modify the array itself, but you can alter each mutable string. Now if you copy the array and modify one of the strings then the copied array also has the changes because it's pointers point to the same instances as the first array.
If you deep copy the array and alter a mutable string, then the new array elements are not modified. In fact copy always makes an immutable copy.

correct way to allocate the NSMutableArray

I wanted to know which is the right way to allocate an NSMutableArray.
NSMutableArray *a;
Where a is a class level variable.
First method is:
self.a = [NSMutableArray alloc] init];
Second method is:
a = [NSMutableArray alloc] init];
Which method is better? Can anyone please help me out in this?
If a is a class variable, then correct way to allocate NSMutableArray will be creating a temporary array and assigning it to class variable, followed by releasing the temporary variable.
You can do this way:
NSMutableArray *temp = [[NSMutableArray alloc]init];
self.a = temp;
[temp release];
It depends on the property type. (Though it's in most cases a retain)
You should either use a temp value or create it in one string and send an autorelease message:
self.a = [[NSMutableArray alloc] init] autorelease];
You must send an autorelease becuase a property increases retain count by one. This is the same as doing:
[self setA:array];//where array - is newly created array
Where:
- (void)setA:(NSMutableArray *)array {
if (array != a) {
[a release];
a = [array retain];//here you increased a retain count by 1
}
}
You can also use an autorelease method of creation:
self.a = [NSMutableArray array];
There are several ways. But below way is good enough per me whether you are working with ARC/Non-ARC. Just make sure you have created property.
self.a = [NSMutableArray array];//It will return autorelease object.
The difference between the methods:
1) When you use self.a ,
You use the setter & getter methods created in the #synthesize.
2) When you use just a,
You bypass the accessor methods and directly modify the instance variable. (a in here).
There are two ways to look at it.
Many programmers say that you should never call the accessors from within the implementation of the object as this adds unnecessary overhead.
Some others say that you should always use the accessors, and never access the instance variable directly.
It is generally safe to use an object directly, if you are reading its value only. If you are modifying the object, you should use the accessors in order to make sure that any other objects observing that property are properly notified.
The latest objective C syntax allows you to create mutable and non-mutable arrays very quickly.
The following two examples demonstrate this:
NSArray *objectsToAdd = [#"Ted", #"Ned" , #"Sed"];
NSMutableArray *objectsToAdd = [ #[#"Ted", #"Ned" , #"Sed"] mutableCopy ];
NSMutableArray *a = [[NSMutableArray alloc] init];

mutable copy copies by reference, not value?

Apparently mutableCopy copies by reference, not value. Ie if I do this:
NSMutableArray arrayA = [arrayB mutableCopy];
then change values of arrayB, then arrayA's values will also be changed.
I think Java has a clone() method to copy by value.. is there an equivalent in objective c?
The mutableCopy method performs “shallow” copy. Each element of arrayA is a reference to an object that is also in arrayB. If you add elements to arrayA (or remove elements), arrayB will be unchanged, and vice versa. But since the elements of arrayA and arrayB reference the same objects, a change to one of those objects “shows up” in both arrays.
If you want a one-level deep copy of arrayB, you can do this:
NSMutableArray *arrayA = [[NSMutableArray alloc] initWithArray:arrayB copyItems:YES];
That will have this effect:
NSMutableArray *arrayA = [[NSMutableArray alloc] init];
for (id element in arrayB) {
[arrayA addObject:[element copy]]; //copies immutable objects to new array
}
To deep copy an array you need to use:
NSMutableArray *newArray = [[NSMutableArray alloc] initWithArray: oldArray copyItems:YES];
This performs a copyWithZone: on each object in the array
The regular [NSMutableArray copy] method will, as per Apple's documentation, return a "functionally independent object with values identical to the original at the time the copy was made." You should probably just use that.
To be totally sure that it is mutable, use [[NSMutableArray alloc] initWithArray:[otherArray copy]].

Why Do I Have to Create An Object and Assign It to A Property in Objective C?

So I had this code, and it did not work:
for (NSDictionary *item in data){
[self.resultsArray addObject:item];
}
self.resultsArray is nil. But then I changed it to this:
NSMutableArray *myDataArray = [[NSMutableArray alloc] init];
for (NSDictionary *item in data){
[myDataArray addObject:item];
}
self.resultsArray = myDataArray;
[myDataArray release];
and now it worked. self.resultsArray is now populated
So I'm a beginner in Objective C and I was wondering why can I not just directly use it in the property's addObject. Why did I have to create another mutable array, populate it, assign it to the resultsArray property and release the mutable array I made?
Thanks in advance!
EDIT: Also, in a lot of books I've been working on, this is done a lot.
simple answer
You didn't initialize self.resultArray before adding objects to it. It is just a pointer to the value which is nil until you alloc it.
self.resultArray = [[NSMutableArray alloc] init]; before adding objects to it will solve the issue.
However, this way of alloc'ing will create a memory leak, therefore it is not shown in books and examples. Memory leak can happen if the self.resultArray property is marked as retain and by calling alloc it will be retained 2 times.
If self.resultsArray is nil, then [self.resultsArray addObject:item] will NOT add an object to the array, it will just do nothing (because the array will be nil by default, and sending messages to nil is a no-op in Objective-C). When you create a mutable array as a local variable, you can add things to it — then if you assign it to the property, well, everything works as you expect and self.resultsArray will no longer be nil.
Typically when you have properties like this, you'd set them up in your init method:
- (id)init {
// ...
self.resultsArray = [NSMutableArray array];
// or access the ivar directly:
// _resultsArray = [[NSMutableArray alloc] init];
// ...
}
Then as soon as your object is initialized you'll be able to add things to the array. Again, if you don't do this, it will be nil by default, and [self.resultsArray addObject:item] will have no effect.
Chances are you are not initializing the array (I'm going to assume myDataArray is an NSMutableArray).
In your init method, call myDataArray = [NSMutableArray array]; and it'll work
The important thing to note is that you're not creating another mutable array as you didn't have an array to start with. Merely declaring a property or variable does not create an object to go along with it. That's why self.resultsArray starts out as nil.
The working code you have is designed to allow you to explicitly release the array as you are retaining it twice: once when you alloc it and once when you assign it to your property. You only want one of those retains, so you release once.
You could just do:
self.resultsArray = [[NSMutableArray alloc] init];
[self.resultsArray release];
for (NSDictionary *item in data){
[self.resultsArray addObject:item];
}
This is less code, but it's not as clear. Clarity is important.