NSarray release - cocoa

If i declare an NSArray with alloc & retain in single sentence then should i release the NSArray object twice (i.e. [arrayObject release] 2 times) ?

If you are creating an NSArray with an alloc and a retain on the same line then you are probably doing something wrong.
Objects are alloced with a retain count of +1, so there is no need to call retain on it as well.
To answer your question directly; yes, you do have to release it twice. Once because you created the object and once because you retained it. But I would question why you need to retain it an extra time in the first place.

You don't need to retain it. You already retain--or take ownership of--an object when you alloc/init. Revisit the Memory Management Programming Guide for Cocoa.

No, you have to release the object for each alloc and each retain. (And you can't alloc an object more than 1 time anyway.)

If you do
NSArray* arrayObject;
arrayObject = [[NSArray alloc] init];
arrayObject = [[NSArray alloc] init];
...
then it just wrong code. The latter assignment will cover the old one, which causes a leak. Either use 2 objects, and release each of them once:
NSArray* arrayObject1, arrayObject2;
arrayObject1 = [[NSArray alloc] init];
arrayObject2 = [[NSArray alloc] init];
...
[arrayObject1 release];
[arrayObject2 release];
or release the object before another init.
NSArray* arrayObject;
arrayObject = [[NSArray alloc] init];
...
[arrayObject release];
arrayObject = [[NSArray alloc] init];
...
[arrayObject release];

Related

Ownership of members inside NSArray? without ARC

Is there a memory leak withOUT ARC?
NSMutableArray *array = [[NSMutableArray alloc] init];
NSNumber *numberForTest = [[NSNumber alloc] initWithInt:123456];
[array addObject: numberForTest];
[numberForTest release];
NSLog(#"number = %#", numberForTest); //safe to access "numberForTest" after calling release???
[array release];
My concern is: Did
[array addObject: numberForTest];
make "numberForTest" not qualified to be deallocated, before
[array release];
???
After using Static Analyzer, no leak is reported. And the output is consistently correct. But I don't feel comfortable.
NSArray (and NSMutableArray) retain objects.
When you release your array, all the objects in that array get released as well.

Building up an array an element at a time

Currently I'm populating an array in one line, e.g.
self.monthMonths = [[NSArray alloc] initWithObjects:#"January", #"February", #"March", #"April", #"May", #"June",#"July",#"August",#"September",#"October",#"November",#"December", nil];
What is the syntax to add these elements one at a time as I want to pull the data from a database. I'm using the months of the year as an example.
while([results next]) {
NSString *months = [results stringForColumn:#"month"];
self.month = [[NSArray alloc] initWithObjects:#"month",nil];
//[NSArray
NSLog(#"Month: %#",month);
}
Create an NSMutableArray and add the objects to it one by one with addObject
You need to use NSMutableArray instead, and call -addObject:

Sort a NSMutuableArray

I have a NSMutableArray that is loaded with a inforamtion from a dictionary...
[self.data removeAllObjects];
NSMutableDictionary *rows = [[NSMutableDictionary alloc] initWithDictionary:[acacheDB.myDataset getRowsForTable:#"sites"]];
self.data = [[NSMutableArray alloc] initWithArray:[rows allValues]];
There are two key value pairs in the rows dictionary.
I need to sort the self.data NSMutableArray in alphabetical order.
How is this accomplished??
thanks
tony
If the values are plain strings you can use the following to create a sorted array:
NSArray *sorted = [values sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)];
This should do:
[self.data removeAllObjects];
NSArray *values = [[acacheDB.myDataset getRowsForTable:#"sites"] allValues];
NSSortDescriptor *alphaDescriptor = [[NSSortDescriptor alloc] initWithKey:#"DCFProgramName" ascending:YES selector:#selector(localizedCaseInsensitiveCompare:)];
NSArray *sortedValues = [values sortedArrayUsingDescriptors:[NSMutableArray arrayWithObjects:alphaDescriptor, nil]];
[alphaDesc release];
[self.data addObjectsFromArray:sortedValues];
There's no need to clear an NSMutableArray if you're replacing it shortly afterwards.
There's no need to create an additional NSMutableDictionary, if you're not modifying anything in it.
There's no need to create an additional NSMutableArray, if you could just as well just add the values to the existing one.
Also: There are some serious memory leaks in your code. (2x alloc + 0x release = 2x leak)
Edit: updated code snippet to reflect OP's update on data structure.

Is there a more memory efficient way to search through a Core Data database?

I need to see if an object that I have obtained from a CSV file with a unique identifier exists in my Core Data Database, and this is the code I deemed suitable for this task:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity;
entity =
[NSEntityDescription entityForName:#"ICD9"
inManagedObjectContext:passedContext];
[fetchRequest setEntity:entity];
NSPredicate *pred = [NSPredicate predicateWithFormat:#"uniqueID like %#", uniqueIdentifier];
[fetchRequest setPredicate:pred];
NSError *err;
NSArray* icd9s = [passedContext executeFetchRequest:fetchRequest error:&err];
[fetchRequest release];
if ([icd9s count] > 0) {
for (int i = 0; i < [icd9s count]; i++) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
NSString *name = [[icd9s objectAtIndex:i] valueForKey:#"uniqueID"];
if ([name caseInsensitiveCompare:uniqueIdentifier] == NSOrderedSame && name != nil)
{
[pool release];
return [icd9s objectAtIndex:i];
}
[pool release];
}
}
return nil;
After more thorough testing it appears that this code is responsible for a huge amount of leaking in the app I'm writing (it crashes on a 3GS before making it 20 percent through the 1459 items). I feel like this isn't the most efficient way to do this, any suggestions for a more memory efficient way? Thanks in advance!
Don't use the like operator in your request predicate. Use =. That should be much faster.
You can specify the case insensitivity of the search via the predicate, using the [c] modifier.
It's not necessary to create and destroy an NSAutoreleasePool on each iteration of your loop. In fact, it's probably not needed at all.
You don't need to do any of the checking inside the for() loop. You're duplicating the work of your predicate.
So I would change your code to be:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:...];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"uniqueID =[c] %#", uniqueIdentifier]];
NSError *err = nil;
NSArray *icd9s = [passedContext executeFetchRequest:fetchRequest error:&err];
[fetchRequest release];
if (error == nil && [icd9s count] > 0) {
return [icd9s objectAtIndex:0]; //we know the uniqueID matches, because of the predicate
}
return nil;
Use the Leaks template in Instruments to hunt down the leak(s). Your current code may be just fine once you fix them. The leak(s) may even be somewhere other than code.
Other problems:
Using fast enumeration will make the loop over the array (1) faster and (2) much easier to read.
Don't send release to an autorelease pool. If you ever port the code to garbage-collected Cocoa, the pool will not do anything. Instead, send it drain; in retain-release Cocoa and in Cocoa Touch, this works the same as release, and in garbage-collected Cocoa, it pokes the garbage collector, which is the closest equivalent in GC-land to draining the pool.
Don't repeat yourself. You currently have two [pool release]; lines for one pool, which gets every experienced Cocoa and Cocoa Touch programmer really worried. Store the result of your tests upon the name in a Boolean variable, then drain the pool before the condition, then conditionally return the object.
Be careful with variable types. -[NSArray count] returns and -[NSArray objectAtIndex:] takes an NSUInteger, not an int. Try to keep all your types matching at all times. (Switching to fast enumeration will, of course, solve this instance of this problem in a different way.)
Don't hide releases. I almost accused you of leaking the fetch request, then noticed that you'd buried it in the middle of the code. Make your releases prominent so that you're less likely to accidentally add redundant (i.e., crash-inducing) ones.

Cocoa Memory Management NSArray with Objects

I'm having troubles releasing objects.. To explain it better I have included my code below.
NSTask *task = [NSTask new];
NSTask *grep = [NSTask new];
NSPipe *pipe = [NSPipe new];
[task setStandardError: pipe];
[grep setStandardInput: pipe];
[pipe release];
pipe = [NSPipe new];
[grep setStandardOutput: pipe];
[task launch];
[grep launch];
NSString *string = [[[[[[NSString alloc] initWithData: [[[grep standardOutput] fileHandleForReading] readDataToEndOfFile] encoding: NSASCIIStringEncoding] autorelease] componentsSeparatedByString: #" "] objectAtIndex: 3] substringToIndex: 8];
NSMutableDictionary *dict = [NSMutableDictionary new];
[dict setObject: string forKey: #"myKey"];
[records addObject: dict];
[dict release];
[task release];
[grep release];
[pipe release];
How would I release the string and are there any other leaks? Also, if I remove everything from the array records with removeAllObjects, is everything released okay then too? The array should never be released and be available at all time, I'm just worrying about its objects.
Edit: The only leak pointed out had to do with the NSPipe and should be fixed in the code.
Thanks for any help!
Memory management in Objective-C has one fundamental rule:
You take ownership of an object if you create it using a method whose name begins with “alloc” or “new” or contains “copy” (for example, alloc, newObject, or mutableCopy), or if you send it a retain message. You are responsible for relinquishing ownership of objects you own using release or autorelease. Any other time you receive an object, you must not release it.
Thus every call to new in your code sample should be balanced with a call to release or autorelease. The NSArray, along with most other objects in the code, isn't created with either, so it doesn't need to be released. The [NSString alloc] is autoreleased, so it's taken care of. Collections manage their own items, retaining and releasing them as necessary: when an item is inserted, it's retained; when it's removed, it's released. Dictionary keys are copied rather than retained.
Where you've got an unbalanced new (and hence leak) is the first NSPipe you created. Release it before creating the pipe for grep's standard output. Perhaps you simply left it out of the sample, but you're also not setting any arguments for the grep task.
substringToIndex: returns an autoreleased string, so there's no need to release it.
The only memory leak I see is where you set up your 'pipe' var a second time (for the task's standard output) without first releasing its current value (the NSPipe instance used for standard error & input).
Mutable collections like NSMutableArray will retain the objects they contain (as do all mutable/nonmutable collections) then release them when they're removed (or when the collection itself is deallocated).

Resources