understanding mutable copy - objective-c

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.

Related

Objective-C method returns NSDictionary instead of NSMutableDictionary. Why?

I am using the included method to return a pointer to a NSMutableDictionary, that is contained in an NSArray. However, the NSMutableArray (theOne) is being created as a non-mutuable NSDictionary. This is a problem because I want to modify the dictionary after retrieving it with this method.
- (NSMutableDictionary*)getMatFromBoutKey:(NSString*) boutKey
{
/*
* Returns the mat object with the provided boutKey.
* Returns nil if no mat has that boutKey.
*/
NSUInteger idx = [[event objectForKey:#"mats"] indexOfObjectPassingTest:
^ BOOL (NSMutableDictionary* obj, NSUInteger idx, BOOL *stop)
{
return [[obj objectForKey:#"boutKey"] isEqualToString:boutKey];
}];
if (idx == NSNotFound)
return nil;
else {
NSMutableDictionary* theOne = [[event objectForKey:#"mats"] objectAtIndex: idx];
return theOne;
}
}
Here's an image of the debugger stopped on a breakpoint right after the theOne is first referenced.
Why isn't theOne mutable? How can I return a pointer to the NSMutableDictionary so that I can modify it after I get the value returned to me?
Thanks!
I'm going to assume that you have a dictionary of arrays. Then that array contains a bunch of regular dictionaries. So when you pull it out of the array it is still a regular dictionary regardless of what you assign it to.
For instance, take the following code for example
NSDictionary *dict = [[NSDictionary alloc] init];
NSMutableDictionary *mutDict = dict;
mutDict will contain a regular dictionary because it has not properly been casted to a mutable dictionary.
either make sure when you create the array that is at [event objectForKey:#"mats"] that you put NSMutable dictionaries inside of it OR
use
NSMutableDictionary* theOne = [[[event objectForKey:#"mats"] objectAtIndex: idx] mutableCopy];
When taking the data out
In general, I think it's better practice to work with immutable objects when mutability isn't strictly necessary. Mutable objects use more memory, and of course have the potential of being accidentally changed. Maybe in the block are being changed to the enumerator(I'm not sure, but it is possible. For faster indexing). If you want to change a mutable object is via mutableCopy. or using a other method.
Is it inserted as mutable elsewhere in the code? If so it should return as mutable, if not you can send it the mutableCopy message to get a mutable copy (that has a reference count of 1, so be sure to release it when necessary).

Copying array elements to other array with pointers

Say I have NSMutableArray *array1 with 10 objects. I want to create an *array2 and add 5 objects from array1 to array2, and I want it so that when I change these object properties from array2, they also change the 5 specific objects from array1 as well. How would I do this?
Edit: Ok I think I asked the wrong question. It's more about passing by reference and pointers, which I confuse too much:
NSMutableArray *mainArray;
NSMutableArray *secondaryArray;
NSMutableDictionary *dic1;
[mainArray addObject:dic1];
[self changeValues:[mainArray lastObject]];
-(void)changeValues:(NSMutableDictionary*)someDic
{
[secondaryArray addObject:someDic];
NSMutableDictionary *aDic=[secondaryArray lastObject];
...//some code to change values of aDic
//by changing aDic, I want to also change the same dic from mainArray
//so [mainArray lastObject] should be the same exact thing as [secondaryArray lastObject]
}
How would I change the above code so the changes reflect in both arrays?
NSMutableArray *array2 = [NSMutableArray array];
for (int i=0; i<5; ++i){
[array2 addObject: [array1 objectAtIndex:i] ]
}
In this example you have the set of objects pointed by items of array1 as well as by items
of array2, since NSMutableArray contains pointers to objects, not objects theirselves.
Therefore, changing the object thru pointer in one array you may observe that change thru
pointer from other array.
Edit
#mohabitar, you already receive an answers. dic1, someDic and aDic - all these values are same. Just change aDic(or someDic) and see result.
This sounds like a good case for some KVC (Key-Value Coding).
With KVC you can create indexed properties and have the KVC engine create an array proxy for the indexed property, which will then allow you to operate on the indexed property as if it were an array.
Below is a quick proof-of-concept piece of code, tested on both OS X and iOS.
Interface:
#property (strong) NSMutableArray *mainArray;
Implementation:
#synthesize mainArray = _mainArray;
- (id)init
{
self = [super init];
if (self) {
// For simplicity, use strings as the example
_mainArray = [NSMutableArray arrayWithObjects:
#"1st element",
#"2nd element",
#"3rd element",
#"4th element",
#"5th element",
#"6th element",
#"7th element",
#"8th element",
#"9th element",
#"10th element",
nil];
}
return self;
}
// KVC for a synthetic array, accessible as property #"secondaryArray"
- (NSUInteger) countOfSecondaryArray
{
return 5;
}
- (id) objectInSecondaryArrayAtIndex: (NSUInteger) index
{
// In practice you would need your mapping code here. For now
// we just map through a plain C array:
static NSUInteger mainToSecondaryMap[5] = {1,4,5,7,8};
return [self.mainArray objectAtIndex:mainToSecondaryMap[index]];
}
- (void) watchItWork
{
NSArray *secondaryArray = [self valueForKey:#"secondaryArray"];
// See how the sub array contains the elements from the main array:
NSLog(#"%#", secondaryArray);
// Now change the main array and watch the change reflect in the sub array:
[self.mainArray replaceObjectAtIndex:4 withObject:#"New String"];
NSLog(#"%#", secondaryArray);
}
There is more information in the docs, specifically the part on Indexed Accessor Pattern.

NSMutableDictionary error

I want to use NSMutableDictionary to cache some data i will use later. My custom object is following:
#interface MyData : NSObject {
NSRange range;
NSMutableArray *values;
}
#property (nonatomic, retain) NSMutableArray *values;
and implement:
- (id)init {
if (self = [super init]) {
values = [[NSMutableArray alloc] init];
}
return self;
}
and when i wanna cache it, i use it like this:
NSMutableDictionary *cache = [[NSMutableDictionary alloc] init];
NSString *key = #"KEY";
MyData *data = [[MyData alloc] init];
// save some data into data
[data.values addObject:"DATA1"];
[data.values addObject:"DATA2"];
//... ...
[cache setObject:data forKey:key];
My questions is the count of cache.values is zero when i retrieve this object later as follow:
[cache objectForKey:#"KEY"];
i can retrieve "data" and the object's memory address is the same as the address when i put it into cache.
what's wrong? i need some kind guys help, any info is helpful. thanks
As Carl Norum pointed out, you're passing C strings to addObject:. addObject:, as its name suggests, requires a pointer to a Cocoa object; a C string is a pointer to characters. You need to pass NSString objects there; for literal strings, this simply requires prefixing them with #: "Fred" is a constant C string, whereas #"Fred" is a constant NSString object.
Is cache an instance variable? It looks like it's not; it appears to be a local variable, which means you're creating a new dictionary object every time. That's why there's nothing you've added previously (to previous dictionaries) in the new one. It also means you're leaking those previous dictionaries, since you're not releasing them (not in the code you showed, anyway).
Make cache an instance variable and only create the dictionary when you don't already have one (i.e., when cache == nil). Creating the dictionary in your init method is one good way. And make sure you manage its lifetime appropriately, so you don't leak and/or crash.
First of all your objects your adding don't look right it should have an # before the string. Like #"DATA1"
Second when you add an object to a dictionary or an array it does not make an actual copy of it. It just creates a pointer to it so if those objects are destroyed or moved somewhere also they are also gone out of your dictionary. A better way to make a cache of your values would be to copy the objects like so:
MyData* cache = [[MyData alloc] init];
for (int i = 0; i < [data.values count]; i ++){{
[cache.values addObject:[NSString stringWithString:[data.values objectAtIndex:i]]];
}
Don't use a dictionary in this situation.

Terminology question regarding looping thru an NSArray in Objective-C

When you have an NSArray and you want to evaluate and change the elements, you can't change the array from inside the loop. So, you create a mutable copy that can be changed.
code example:
NSMutableArray *bin = [NSMutableArray arrayWithObjects:#"0", #"1", #"2", #"3", #"4", #"5", #"6", #"7", nil];
NSMutableArray *list = [NSMutableArray arrayWithObjects:#"a1", #"b2", #"c3", #"e4", nil];
NSMutableArray *listHolder = list; // can't mutate 'list' within loop so create a holder
for (int i = 0; i < [list count]; i++) {
[listHolder replaceObjectAtIndex:i withObject:[bin objectAtIndex:i]];
}
What is that second array listHolder called? I mean, what term is used to refer to an array in this context.
This is perfectly valid:
NSMutableArray *bin = [NSMutableArray arrayWithObjects:#"0", #"1", …, #"7", nil];
NSMutableArray *list = [NSMutableArray arrayWithObjects:#"a1", …, #"e4", nil];
// NSInteger should be used instead of int
for (NSInteger i = 0; i < [list count]; i++) {
[list replaceObjectAtIndex:i withObject:[bin objectAtIndex:i]];
}
You're not allowed to change the array inside a for … in or NSEnumerate loop, but using an index is perfectly valid.
What troubles me is your misunderstanding of pointers.
If it were a loop in which you weren't allowed to mutate the array this wouldn't copy the array but only the pointer to the array, effectively modifying the array you're not allowed to. (I'm not even sure if this works.)
Instead of just copying the pointer
// can't mutate 'list' within loop so create a holder
NSMutableArray *listHolder = list;
make a true copy:
NSMutableArray *copy = [[list mutableCopy] autorelease];
In case I really have to make a copy I try to name it according to its content. For example:
NSMutableArray *views;
NSMutableArray *reorderedViews = [views mutableCopy];
// reorder reorderedViews
Sometimes it's hard to find a good enough name, then I usually just use nameCopy.
In this context listHolder would be called a copy.
Your code has a bug though. This line is not actually making a copy, it is only letting listHolder and list both reference the same array object:
NSMutableArray *listHolder = list;
This would be an actual copy:
NSMutableArray *listHolder = [list mutableCopy];
Make sure that you use mutableCopy and not just copy if you want the copy to be mutable. The copy method will return immutable variants on all mutable classes such as NSMutableSet, NSMutableDictionary, and so forth.
Also as others have noted it is only inside the for (item in collection) loop that the enumerated collection can not be mutated. In a normal for (;;) mutation is perfectly ok, but can lead to strange result if the number of items in the collection changes.
There is not specific stylistic or common name for this that is universally used, it is your code afterall, and if there appropriate terms for them use them.
Having said that generally if you don't have specific names in this sort of situation then people refer to the original list as the "source" (src) and the final list as "destination" (dst), just like in a memory blitting style operation.
A temporary mutable copy of the original NSArray would be how I would refer to it.

return a copy or a mutable object

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];
}