How does Object copying work in objective c - objective-c

I'm a bit confused on how copying objects works in Objective C. Here's what i know: When you copy an object, you get a distinct object in memory that contains all the same elements from the object that you have just copied and increments the retain count for each element. Also, copying each element in the array object from the original to a new location meant just copying the reference from one element of the array to another. So, the old and the new are pointing to the same element.
look at the below code: why is it that when you remove an object it only affects one object and when you change the element, it affects both the original and the copy object? Shouldn't the remove affects both objects?
NSMutableArray *dataArray = [NSMutableArray arrayWithObjects:
[NSMutableString stringWithString:#"one"],
[NSMutableString stringWithString:#"two"],
[NSMutableString stringWithString:#"three"], nil];
NSMutableArray *dataArray2;
NSMutableString *mStr;
NSLog(#"1-dataArray: ");
for( NSString *elem in dataArray)
NSLog(#" %#", elem);
dataArray2 = [dataArray mutableCopy];
[dataArray2 removeObjectAtIndex:0];
NSLog(#"2-dataArray2: ");
for( NSString *elem in dataArray2)
NSLog(#" %#", elem);
mStr =[dataArray objectAtIndex:1];
[mStr appendString:#"ONE"];
NSLog(#"3-dataArray: ");
for( NSString *elem in dataArray)
NSLog(#" %#", elem);
NSLog(#"4-dataArray2: ");
for( NSString *elem in dataArray2)
NSLog(#" %#", elem);
[dataArray2 release];

mutableCopy performs a 'shallow copy' of the NSArray's contents. eg. it is copying the pointers (and presumably retaining them) from the origin array. It is not copying the data those pointers are pointing to.
If we were to do this explicitly it's essentially doing:
-(NSMutableArray*)mutableCopy
{
NSMutableArray *newArray = [[NSMutableArray alloc] init];
for (id elem in originalArray)
[newArray addObject: elem];
return newArray;
}
Though presumably it's doing it more efficiently by using its access to the internal data structures.

Related

Trying to build polygon from NSString

So, I'm trying to build an array of CGPoints by breaking an NSString, typically look like this:
31.241854,34.788867;31.241716,34.788744;31.242547,34.787585;31.242661,34.787719
Using this code:
- (NSMutableArray *)buildPolygon:(NSString *)polygon
{
NSMutableArray *stringArray = [[NSMutableArray alloc] init];
[stringArray addObject:[polygon componentsSeparatedByString:#";"]];
NSMutableArray *polygonArray = [[NSMutableArray alloc] init];
for (int i=0; i < polygonArray.count; i++)
{
NSArray *polygonStringArray = [[NSArray alloc] init];
polygonStringArray = [[stringArray objectAtIndex:i] componentsSeparatedByString:#","];
CGFloat xCord = [[polygonStringArray objectAtIndex:0] floatValue];
CGFloat yCord = [[polygonStringArray objectAtIndex:1] floatValue];
CGPoint point = CGPointMake(xCord, yCord);
[polygonArray addObject:[NSValue valueWithCGPoint:point]];
}
NSLog(#"return polygonArray: %#", polygonArray);
return polygonArray;
}
But eventually I get an empty array.
What I'm doing wrong?
You're defining polygonArray as an empty array just before the start of your for loop. You should define polygonArray like:
NSArray *polygonArray = [polygon componentsSeparatedByString:#";"];
And you don't even need to bother with that stringArray variable.
You have confusion over alloc & init, and one simple typo...
The confusions first:
NSMutableArray *stringArray = [[NSMutableArray alloc] init];
This creates a new NSMutableArray and stores a reference to it in stringArray. All good so far.
[stringArray addObject:[polygon componentsSeparatedByString:#";"]];
And this obtains a reference to an NSArray ([polygon componentsSeparatedByString:#";"]) and adds it as a single element to the mutable array referenced by stringArray. There is nothing wrong per se with this, but it is not what you want in this case - you just want the array returned by componentsSeparatedByString:. You do this with:
NSArray *stringArray = [polygon componentsSeparatedByString:#";"];
Which takes the reference returned by componentsSeparatedByString: and stores it in the variable stringArray - no alloc or init required as you are not creating the array yourself. You don't even own this array, so if you are using MRC there is no need to release it later.
NSArray *polygonStringArray = [[NSArray alloc] init];
Now this allocates an immutable empty array and stores a reference to it in polygonStringArray. This is not a very useful array, as it contains nothing and cannot be modified! But you don't keep it around long...
polygonStringArray = [[stringArray objectAtIndex:i] componentsSeparatedByString:#","];
This obtains a reference to an array from componentsSeparatedByString: and stores it in polygonStringArray. If you are using MRC this will cause a leak - your pointless zero-length array created above will leak, and a new zero-length array will be created and leaked every time around the loop.
You are confused over allocation - you only need to allocate things you are creating; when you receive a reference to an already allocated object you only need to store that reference. (If using MRC you may also need to retain/release/autorelease it as well - but let's stick with ARC.) So all you needed here was:
NSArray *polygonStringArray = [[stringArray objectAtIndex:i] componentsSeparatedByString:#","];
Now your code is almost correct, just one typo:
for (int i=0; i < polygonArray.count; i++)
Well you are filling polygonArray in this loop and it starts off as empty, what you need is stringArray.count.
HTH

Searching NSArray using suffixes

I have a word list stored in an NSArray, I want to find all the words in it with the ending 'ing'.
Could someone please provide me with some sample/pseudo code.
Use NSPredicate to filter NSArrays.
NSArray *array = #[#"test", #"testing", #"check", #"checking"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF ENDSWITH 'ing'"];
NSArray *filteredArray = [array filteredArrayUsingPredicate:predicate];
Let's say you have an array defined:
NSArray *wordList = // you have the contents defined properly
Then you can enumerate the array using a block
// This array will hold the results.
NSMutableArray *resultArray = [NSMutableArray new];
// Enumerate the wordlist with a block
[wordlist enumerateObjectsUsingBlock:(id obj, NSUInteger idx, BOOL *stop) {
if ([obj hasSuffix:#"ing"]) {
// Add the word to the result list
[result addObject:obj];
}
}];
// resultArray now has the words ending in "ing"
(I am using ARC in this code block)
I am giving an example using blocks because its gives you more options should you need them, and it's a more modern approach to enumerating collections. You could also do this with a concurrent enumeration and get some performance benefits as well.
Just loop through it and check the suffixes like that:
for (NSString *myString in myArray) {
if ([myString hasSuffix:#"ing"]){
// do something with myString which ends with "ing"
}
}
NSMutableArray *results = [[NSMutableArray alloc] init];
// assuming your array of words is called array:
for (int i = 0; i < [array count]; i++)
{
NSString *word = [array objectAtIndex: i];
if ([word hasSuffix: #"ing"])
[results addObject: word];
}
// do some processing
[results release]; // if you're not using ARC yet.
Typed from scratch, should work :)

obj-c fetching strings from array

i'm new to obj-c (this is my first day class eheh) and i'm trying to change a label with a random string from a multidimensional array. plus, every time the button is hitten you switch the array. i know it's a bit odd eheh… this is the IBAction:
UIButton *button = (UIButton *)sender;
NSMutableArray *firstArray = [NSMutableArray array];
[firstArray addObject:#"foo"];
NSMutableArray *secondArray = [NSMutableArray array];
[secondArray addObject:#"bar"];
NSMutableArray *frasi = [NSMutableArray array];
[frasi addObject:firstArray];
[frasi addObject:secondArray];
NSMutableArray *array = [NSMutableArray arrayWithObjects:[frasi objectAtIndex:[button isSelected]], nil];
NSString *q = [array objectAtIndex: (arc4random()% [array count] )];
NSString *lab = [NSString stringWithFormat:#"%#", q];
self.label.text = lab;
all works, but the new label is
( "foo" )
instead of just foo (without quotes)... probably i mess in the last block of code...
ty
So, you create 2 mutable arrays, then add them to a new mutable array frasi. Then you get one of those two arrays and use it as the single element (because you use arrayWithObjects: instead of arrayWithArray:) of a new array array.
So array is an array that contains a single array element (instead of an array of strings as you may believe).
When you get an object from array, it's always the same single object that was used to initialize it: either firstArray or secondArray.
So you get an array of strings where you expect a string. When using stringWithFormat:, the specifier %# is replaced with the string description of that object.
A string returns itself as its own description. But the description of an array is the list of all its elements separated with commas and surrounded by parenthesis, which is why you get ( "foo" ).
So instead or creating unneeded arrays, you may just replace all the 8th last lines with this:
NSArray *array = [button isSelected] ? secondArray : firstArray;
self.label.text = [array objectAtIndex:arc4_uniform([array count])];
Actually u have array within array
Replace this line with yours:
NSString *q = [[array objectAtIndex: (arc4random()% [array count] )] objectAtIndex:0];

Getting an NSString out of an NSArray

I am trying to save and read back some application settings stored as NSStrings in an iPhone app and have been having some trouble.
The code to save looks like:
NSMutableArray *array = [[NSMutableArray alloc] init];
[array addObject:accountID];
...
[array writeToFile:[self dataFilePath] atomically:YES];
[array release];
And the code to read looks like (accountID is an NSString*):
NSArray *array = [[NSArray alloc] initWithContentsOfFile:filePath];
accountID = [array objectAtIndex:0];
...
[array release];
NSLog(#"Loading settings for: %#", accountID);
The read code throws an exception because after the array is released the accountID variable also appears to have been released (moving the NSLog call before releasing the array works fine). So I'm guessing that I'm creating a reference to the array instead of pulling out the actual string contained in the array. I tried several things to create new strings using the array contents but haven't had any luck.
You guess is on the right lines although you have a reference to the 0th element of the array not the array. The array consists of pointers to NSString objects. The Strings will get get released when yhe array is released.
You need to retain the element you are using e/g/
NSArray *array = [[NSArray alloc] initWithContentsOfFile:filePath];
NSString* accountID = [[array objectAtIndex:0]retain];
...
[array release];
NSLog(#"Loading settings for: %#", accountID);
When you release the array the reference to the accountID will also be released. You need to retain it.
accountID = [[array objectAtIndex:0] retain];
Then obviously at some point you need to release it.
try [accountID retain] before you release the array

Return mutable or copy

If you have a method in objective c that builds an array or dictionary using a mutable object, should you then copy the object, or return the mutable version? This is probably a case of opinion but I have never been able to make up my mind. Here are two examples to show what I am talking about:
- (NSArray *)myMeth
{
NSMutableArray *mutableArray = [NSMutableArray array];
for (int i=0; i<10; i++) {
[mutableArray addObject:[NSNumber numberWithInt:i]];
}
return mutableArray;//in order for calling code to modify this without warnings, it would have to cast it
}
- (NSArray *)myMeth
{
NSMutableArray *mutableArray = [[NSMutableArray alloc] init];
for (int i=0; i<10; i++) {
[mutableArray addObject:[NSNumber numberWithInt:i]];
}
NSArray *array = [[mutableArray copy] autorelease];
[mutableArray release];
return array;//there is no way to modify this
}
It depends what the method will be used for, or what the intented use for the returned array is.
By convention it is considered normal to copy and autorelease the mutable array before returning it, thereby complying to the object ownership conventions and protecting the data from being changed once it's returned.