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.
Related
when I doing animation with the next code:
NSArray *images=[NSArray arrayWithObjects:[UIImage imageNamed:#"shavit_1.png"],[UIImage imageNamed:#"shavit_1.png"],[UIImage imageNamed:#"shavit_2.png"],[UIImage imageNamed:#"shavit_3.png"],[UIImage imageNamed:#"shavit_4.png"],[UIImage imageNamed:#"shavit_5.png"],[UIImage imageNamed:#"shavit_6.png"],[UIImage imageNamed:#"shavit_7.png"],[UIImage imageNamed:#"shavit_8.png"],[UIImage imageNamed:#"shavit_9.png"],[UIImage imageNamed:#"shavit_10.png"],[UIImage imageNamed:#"shavit_11.png"],[UIImage imageNamed:#"shavit_12.png"],[UIImage imageNamed:#"shavit_13.png"],[UIImage imageNamed:#"shavit_14.png"],[UIImage imageNamed:#"shavit_15.png"],[UIImage imageNamed:#"shavit_16.png"],[UIImage imageNamed:#"shavit_17.png"],[UIImage imageNamed:#"shavit_18.png"],[UIImage imageNamed:#"shavit_19.png"],[UIImage imageNamed:#"shavit_20.png"],[UIImage imageNamed:#"shavit_21.png"],[UIImage imageNamed:#"shavit_22.png"],[UIImage imageNamed:#"shavit_23.png"],[UIImage imageNamed:#"shavit_24.png"],[UIImage imageNamed:#"shavit_25.png"],nil];
shavitimage.animationImages=images;
shavitimage.animationDuration=2.0;
shavitimage.animationRepeatCount=1;
[shavitimage startAnimating];
[images release]; //release the used array
[self performSelector:#selector(animation_ends) withObject:nil afterDelay:2.0];//after 2 seconds put astatic image
timer=[NSTimer scheduledTimerWithTimeInterval:10.0 target:self selector:#selector(timerFireMethod:) userInfo:nil repeats:NO];
}
-(void)animation_ends{
[shavitimage stopAnimating];//the UIImageView return to static mode
shavitimage.image=[UIImage imageNamed:#"shavit_25.png"];
self.view.userInteractionEnabled=YES;
}
the animation is working well,and the application working well after the end of the animation.
but when I doing animation with the next code:
NSMutableArray *images=[NSMutableArray array]; //insrt the images dynamiclly
for(int i=1;i<=25;i++)
{
NSString *file=#"shavit_";
file=[file stringByAppendingFormat:#"%d",i];
NSString *filePath = [[NSBundle mainBundle] pathForResource:file ofType:#"png"];
[images addObject:[UIImage imageWithContentsOfFile:filePath]];
[file release];
[filePath release];
}
shavitimage.animationImages=images;
shavitimage.animationDuration=2.0;
shavitimage.animationRepeatCount=1;
[shavitimage startAnimating];
[images release]; //release the used array
[self performSelector:#selector(animation_ends) withObject:nil afterDelay:2.0];//after 2 seconds put astatic image
timer=[NSTimer scheduledTimerWithTimeInterval:10.0 target:self selector:#selector(timerFireMethod:) userInfo:nil repeats:NO];
}
-(void)animation_ends{
[shavitimage stopAnimating];//the UIImageView return to static mode
shavitimage.image=[UIImage imageNamed:#"shavit_25.png"];
self.view.userInteractionEnabled=YES;
}
the animation is working well,but it stack after.why does it happen?
Do not release an autoreleased object.
That's the gist of it.
Evaluation of chain of events :–
You create an autoreleased array. Retain Count = 1 (will diminish by 1 in near future)
UIImageView object retains the array when you set animationImages. Retain Count = 2
You release the array. Retain Count = 1
Autorelease kicks in. Retain Count = 0 (object is deallocated)
Now the UIImageView still thinks that it has ownership of the array. When it tries to access it then it should result in an error. If it isn't animating and then is later deallocated, it will try to send a release message to the deallocated instance which should also crash the application. All of this is fair assumption as we don't know if the framework is adding its own memory management calls in any way.
Now both the pieces of code have the same problem and are both unreliable.
Be a good memory citizen and remove the release call.
#synthesize myImage;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSMutableArray *a = [NSMutableArray array];
//[a addObject: [UIViewController ]];
[a addObject:[UIImage imageNamed:#"1.gif"]];
[a addObject:[UIImage imageNamed:#"2.gif"]];
[a addObject:[UIImage imageNamed:#"3.gif"]];
[a addObject:[UIImage imageNamed:#"4.gif"]];
[a addObject:[UIImage imageNamed:#"5.gif"]];
[a addObject:[UIImage imageNamed:#"6.gif"]];
[a addObject:[UIImage imageNamed:#"7.gif"]];
//[myImage initWithFrame:CGRectMake(0, 0, 131, 125)];
myImage.animationImages = a;
myImage.animationDuration=5;
myImage.animationRepeatCount=0;
myImage.startAnimating;
[self.view addSubview:myImage];
}
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.
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.
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).
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];