I have the following method which is spawned by a call for a new thread (using NSThread):
- (void) updateFMLs {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSArray *temp = [[NSArray alloc] initWithArray:someArrayFromAnotherProcess];
[self performSelectorOnMainThread:#selector(doneLoading:) withObject:temp waitUntilDone:NO];
[pool release];
}
My doneLoading: method looks like this:
- (void) doneLoading:(NSArray *)obj {
myArray = [[NSArray alloc] initWithArray:obj copyItems:NO];
}
The contents of myArray become invalid. How can I preserve the contents of myArray so I can use them later in my app?
P.S. myArray is defined in the class header file.
If your background thread does some work and needs to 'pass' an NSArray to your main thread, then all doneLoading needs to do is:
-(void)doneLoading:(NSArray *)obj
{
[myArray release]; // release the previous array and its objects
myArray = [obj retain];
// now use myArray, refresh tables, etc.
}
There's (likely) no need to make another copy of the array, and that might be the underlying issue. You should also call [temp release] after your performSelector call, since arguments to that are retained already.
If the contents of myArray are becoming valid somehow, then they are being doubly released somewhere. myArray will retain any objects that are added to it. You mentioned that myArray itself is becoming invalid, so try rewriting your background thread and your doneLoading method with this pattern.
Finally you should use [pool drain] in place of [pool release].
The code you posted looks fine, apart from the memory leak in updateFMLs. You're probably over-releasing the objects somewhere else. I'm guessing it would be wherever someArrayFromAnotherProcess is made.
An alternative to the options above would be to declare myArray as an atomic property in the header
#property (atomic,retain) NSArray *myArray;
Then in updateFMLs you should be able to simply call the setter from the secondary thread. Obviously this only works if you are willing to pay the performance penalty for an atomic property.
- (void) updateFMLs {
NSAutoreleasePool *pool - [[NSAutoreleasePool alloc] init];
NSArray *temp = [[NSArray alloc] initWithArray:someArrayFromAnotherProcess];
[self setMyArray:temp];
[temp release];
[pool drain];
}
Related
I'm totally stumped on this one. I have an NSMutableArray which is declared in my header and set as a property, synthesized etc. I then call a function that allocates and initializes the array, and I add custom objects to it. I do a for each loop after the objects are added to ensure that they are actually contained within the array and they are. Once the program goes outside of this function scope, though, suddenly the array is empty.
header file:
#interface ScheduleViewController : UITableViewController {
NSString *login_id;
NSMutableArray *events;
}
- (id)initWithID:(NSString*)l_id;
- (void)grabURLInBackground; // ASIHTTP example method
#property (nonatomic, retain) NSString *login_id;
#property (nonatomic, retain) NSMutableArray *events;
#end
implementation:
#synthesize events;
- (void)requestFinished:(ASIHTTPRequest *)request
{
// Use when fetching text data
NSString *response = [request responseString];
SBJsonParser *parser = [[SBJsonParser alloc] init];
NSArray *eventDics = [parser objectWithString:response error:nil]; // an array of dictionaries of events
NSDateFormatter *dateForm = [[NSDateFormatter alloc] init];
// Allocate empty event object and initialize the mutable array
Event* event = [[Event alloc] init];
self.events = [[NSMutableArray alloc] initWithCapacity:[eventDics count]];
// loop through the array of dictionaries
for (int i = 0; i < [eventDics count]; i++)
{
NSDictionary *dict = [eventDics objectAtIndex:i];
for(NSString *key in dict) {
// for the sake of readability i wont include the code
// but the event is populated here
}
[self.events addObject:event];
[event release];
}
NSLog(#"Array Count: %i", [self.events count]);
for (Event *e in events) {
NSLog(#"eventid: %i, type: %#, price: %f, name: %#", e.event_id, e.type, e.price, e.name);
}
[parser release];
[dateForm release];
}
So the above code works fine and prints out the variables from the Event objects that are stored in the events mutable array.
What I want to do is use the events array in another function now, and when I try to, the count is 0 and also no objects are stored in the array when I look at it.
In viewDidUnload I set self.events = nil; and in dealloc I do [self.events release]
You are doing your alloc/init for the Event *event object outside of your for loop. This means you are adding the same object every time you add it to the array. You should move this line:
Event* event = [[Event alloc] init];
To the inside of your
for (int i=0 ... loop.
Not sure that would explain the symptoms you are seeing, but it could, since the following statement:
[event release]
is also releasing that one allocated object once for every time through the loop - so you are releasing the object multiple times. If you move the Event alloc to the inside of the loop then the release will be ok. (adding the object to the array will retain it so its ok to release it, but you need to allocate a new Event each time through the loop).
Basically your code should look like this: (note I've also added an autorelease to your array alloc).
// Allocate empty event object and initialize the mutable array
self.events = [[[NSMutableArray alloc] initWithCapacity:[eventDics count]] autorelease]; // assigning to the retain property will retain it, so autorelease it or it will be retained twice. Could also have used the arrayWithCapacity convenience method here instead and then wouldn't need to autorelease.
// loop through the array of dictionaries
for (int i = 0; i < [eventDics count]; i++)
{
Event* event = [[Event alloc] init]; // Allocate a new Event each time through the loop so you are adding a unique object to the array each time.
NSDictionary *dict = [eventDics objectAtIndex:i];
for(NSString *key in dict) {
// for the sake of readability i wont include the code
// but the event is populated here
}
[self.events addObject:event];
[event release];
}
I see many problems with this code. These include the fact that you are releasing objects at inappropriate times, and that you are getting confused about the scope of different objects. It seems to me that one of the biggest problems that you are having is re-allocating your events array every time the requestFinished: method is called. In your init method, you should do something like this:
- (id)init {
if ((self = [super init])) {
// Since it is a retain property, we should autorelease it when
// assigning to it, thus preventing an extra retain.
self.events = [[[NSMutableArray alloc] initWithCapacity:[eventDics count]] autorelease];
}
}
With that being said, here is how I would rewrite your requestFinished: method, as well as your dealloc method:
- (void)requestFinished:(ASIHTTPRequest *)request {
NSString *response = [request responseString];
SBJsonParser *parser = [[SBJsonParser alloc] init];
NSArray *eventDics = [parser objectWithString:response error:nil]; // an array of dictionaries of events
NSDateFormatter *dateForm = [[NSDateFormatter alloc] init];
// Clear the already allocated events array
[self.events removeAllObjects];
for (int i = 0; i < [eventDics count]; i++) {
// note how I assign event in here
Event *event = [[Event alloc] init];
NSDictionary *dict = [eventDics objectAtIndex:i];
for (NSString *key in dict) {
// Do whatever it is you do here
}
[self.events addObject:event];
[event release];
}
NSLog(#"Array Count: %i", [self.events count]);
for (Event *e in events) {
NSLog(#"eventid: %i, type: %#, price: %f, name: %#", e.event_id, e.type, e.price, e.name);
}
[parser release];
[dateForm release];
}
Finally, you can simply set the events property to nil in the dealloc method:
- (void)dealloc {
self.events = nil;
[super dealloc];
}
The only reason that I can think of for the array being empty is that a) it's contents are being deallocated, or b) it itself is being deallocated and set to nil. The pieces of your code that I fixed could possibly cause both of these. Try the changes that I have made, and see if they make a difference.
So I figured out the problem and it's due to an error on my part. After stepping through the function calls more closely, it turns out that the table view delegate method
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
was being called before populating the array with requestHasFinished. I was calling requestHasFinished indirectly through viewDidLoad but I think that the table view delegate method was being called when the view controller is initialized. Init was being called before viewDidLoad because the view controller is actually handled within a tab view controller which initializes all of the view controllers for each tab at the time when itself is initialized. That's another matter to get into.. I'm not sure if I like everything being initialized and setup before the views are even displayed but.. something to research.
Anyways thanks again for the help.
maybe someone can help me finding why his code is leaking ..
im calling the getNotes function, wich is returning a autorelease NSMutableArray
notesArray = [[noteManager getNotes:id] retain];
notesArray is a property declared in my header file
#property (nonatomic, retain) NSMutableArray* notesArray;
this is the stripped version of the getNotes function
- (NSMutableArray*) getNotes:(NSString *)id {
NSMutableArray* rArr = [[NSMutableArray alloc] init];
for (NSString* sNote in noteArray) {
myNote* note = (myNote*)[NSKeyedUnarchiver unarchiveObjectWithFile:sFile];
[rArr addObject:note];
}
return [rArr autorelease];
}
the [rArr addObject:note]; is 100% leaking ..
why? they are all autoreleased?
the myNote class just a class with some properties, nothing special ...
It may be that you already have notes stored in notesArray and they are not getting released before setting it again.
Try changing this
notesArray = [[noteManager getNotes:id] retain];
//to
self.notesArray = [noteManager getNotes:id];
Does notesArray get released at some point? Instead of manually retaining you can use the dot syntax:
self.notesArray = [noteManager getNotes:id];
instead of:
notesArray = [[noteManager getNotes:id] retain];
for (NSString* sNote in noteArray) {
myNote* note = (myNote*)[NSKeyedUnarchiver unarchiveObjectWithFile:sFile];
[rArr addObject:note];
}
Here your notes are autoreleased and shoud not produce memory leak, but autorelease does not happen immediately. This can become a problem in a long loops. You should use your own autorelease pool to avoid such situations. Like this
for (NSString* sNote in noteArray) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
myNote* note = (myNote*)[NSKeyedUnarchiver unarchiveObjectWithFile:sFile];
[rArr addObject:note];
[pool drain];
}
I just ran my app through the Leaks in Instruments and I am being told that the following code causes leaks, but I don't see how.
I allocate some NSMutableArrays in my viewDidLoad with this code:
- (void)viewDidLoad {
[super viewDidLoad];
self.currentCars = [[NSMutableArray alloc] init];
self.expiredCars = [[NSMutableArray alloc] init];
}
Then I populate these arrays inside of my viewWillAppear method with the following:
[self.currentCars removeAllObjects];
[self.expiredCars removeAllObjects];
for (Car *car in [self.dealership cars]) {
if ([car isCurrent])
[self.currentCars addObject:car];
if ([car isExpired])
[self.expiredCars addObject:car];
}
And later in the code I release these arrays here:
- (void) viewWillDisappear:(BOOL)animated {
if (currentCars != nil) {
[currentCars release], currentCars = nil;
}
if (expiredCars != nil) {
[expiredCars release], expiredCars = nil;
}
[super viewWillDisappear:animated];
}
Any ideas? Thanks!
Your leak is here:
self.currentCars = [[NSMutableArray alloc] init];
self.expiredCars = [[NSMutableArray alloc] init];
Assuming that you declared property accessores like this:
#property(nonatomic, retain) NSMutableArray *currentCars;
#property(nonatomic, retain) NSMutableArray *expiredCars;
In my opinion, the best way to find leaks (other than using Instruments) is to keep track of the retain count manually.
If you were to do that with for example currentCars, you would find your leak easily. Here is what happens:
self.currentCars = [[NSMutableArray alloc] init];
// The 'init' makes the retain count 1.
// 'self.currentCars = ..' translates to the setCurrentCars: method.
// You probably did not implement that method yourself,
// but by synthesizing your property it is automatically implemented like this:
- (void)setCurrentCars:(NSMutableArray *)array {
[array retain]; // Makes the retain count 2
[currentCars release];
currentCars = array;
}
// In your viewWillDisappear: method
[currentCars release], currentCars = nil; // Makes the retain count 1 so the object is leaked.
The solution is simple. Use this:
NSMutableArray *tempMutableArray = [[NSMutableArray alloc] init];
self.currentCars = tempMutableArray;
[tempMutableArray release];
A little sidenote. You shouldn't release your objects in viewWillDisappear:. The recommended place to do that is dealloc. So your code would be:
- (void)dealloc {
[currentCars release], currentCars = nil;
[expiredCars release], expiredCars = nil;
[super dealloc];
}
The problem is (probably) that you are using the property accessors for the initial setting of the arrays in -viewDidLoad. Since well-implemented property accessors will retain the object, you are getting 1 retain from the +alloc and another retain from assigning it. To fix this, you should release your arrays after assigning them or use [NSMutableArray array] to get an autoreleased one to use for your initial assignments.
Unless you're doing something very odd in currentCars, expiredCars, dealership or cars, no, there's no leak there.
Instruments' pointer to the location of a leak isn't necessarily where the object is actually leaked, per se. If I were to guess, I'd say you're probably neglecting to release either currentCars or expiredCars in your dealloc method.
I have noted several other threads on this topic and have tried wrapping my threaded code with:
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
[pool release];
but the errors still come.
I am using a static method to instantiate a dictionary of words.
Here is some code:
-(id)init
[NSThread detachNewThreadSelector:#selector(loadDictionary:) toTarget:[IntroScreen class] withObject:nil];
[NSThread setThreadPriority:1.0];
return self;
}
+(void)loadDictionary:(id)param
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
[[SimpleAudioEngine sharedEngine] preloadEffect:#"click.wav"];
[[SimpleAudioEngine sharedEngine] preloadEffect:#"pop.wav"];
[[SimpleAudioEngine sharedEngine] preloadEffect:#"dink.wav"];
[[SimpleAudioEngine sharedEngine] playBackgroundMusic:#"musicloop.wav"];
[WordDictionary configDictionary];
[pool release];
}
+(void)configDictionary
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
Serializer * mySerializer = [[Serializer alloc] init];
[WordDictionary setDictionary:[mySerializer readApplicationPlist:#"x"]];
NSString * string;
NSString *filePath = [[[NSBundle mainBundle] resourcePath]
stringByAppendingPathComponent:#"x.txt"];
NSString *info = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
NSArray *arrayOfLines = [info componentsSeparatedByString:#"\r\n"];
[WordDictionary setDictionary:[[NSMutableDictionary alloc] init]];
[NSMutableDictionary dictionaryWithContentsOfFile:filePath];
int len = [arrayOfLines count];
for(int i = 0; i < len; i++)
{
string = [arrayOfLines objectAtIndex:i];
NSString * blankString = [NSString stringWithString:#""];
[[WordDictionary dictionary] setObject:blankString forKey:string];
double calc = ((double)i / (double)len) * 100.0;
[WordDictionary setProgress:(int)calc];
}
[mySerializer writeApplicationPlist:[WordDictionary dictionary] toFile:#"s"];
[WordDictionary setProgress:100];
[pool release];
}
Is there something I should know about using static class methods with new selector threads?
Thank you for your help
First, there are no static methods in Objective-C. There are class methods.
Secondly, your code shows both methods wrapped in autorelease pools. The warning must be coming from somewhere else.
Finally, your code leaks like a sieve. You aren't following the memory management rules. And there are some nonsense statements in there.
Specifically:
[WordDictionary setDictionary:[[NSMutableDictionary alloc] init]];
Unless +setDictionary: is breaking the memory management rules, the above leaks.
This statement [NSMutableDictionary dictionaryWithContentsOfFile:filePath]; effectively does nothing unless you do something with the return value.
Also, mySerializer is leaking.
Try running the analyzer over your code and fixing the problem. You should also read this and this.
Ah the [NSMutableDictionary dictionaryWithContentsOfFile:filePath]; was part of an experiment I was attempting to make the dictionary access faster. I should have removed it from this example.
I have just read the memory management rules, and understand that
[WordDictionary setDictionary:[[NSMutableDictionary alloc] init]]; appears to be poorly planned instantiation because I have no way to release it from within configDictionary as the reference is lost. But actually I don't ever want to release it, it lives for the entire lifetime of my application. Probably bad practice just the same.
mySerializer should definitely be released at the bottom.
I was just wondering if class methods had any special rules regarding autorelease pools and memory.
I will look over the documents you sent me and try to figure out the Analyzer, thank you for your help.
With an instance variable myArray:
#interface AppController : NSObject
{
NSArray *myArray;
}
Sometimes I see myArray initialized like this:
- (void)init
{
[super init];
self.myArray = [[NSArray alloc] init];
return self;
}
and sometimes I see it with a more complicated method:
- (void)init
{
[super init];
NSArray *myTempArray = [[NSArray alloc] init];
self.myArray = myTempArray
[myTempArray release];
return self;
}
I know that there's no difference in the end result, but why do people bother to do the longer version?
My feeling is that the longer version is better if the instance variable is set up with a #property and #synthesize (possibly because the variable has already been alloced). Is this part of the reason?
Thanks.
If myArray is a property and it's set to retain or copy (as it should be for a property like this), then you'll end up double-retaining the variable when you do this:
self.myArray = [[NSArray alloc] init];
The alloc call sets the reference count to 1, and the property assignment will retain or copy it. (For an immutable object, a copy is most often just a call to retain; there's no need to copy an object that can't change its value) So after the assignment, the object has retain count 2, even though you're only holding one reference to it. This will leak memory.
I would expect to see either a direct assignment to the instance variable
myArray = [[NSArray alloc] init];
Or proper handling of the retain count:
NSArray *newArray = [[NSArray alloc] init];
self.myArray = newArray;
[newArray release];
Or the use of autoreleased objects:
self.myArray = [[[NSArray alloc] init] autorelease]; // Will be released once later
self.myArray = [NSArray array]; // Convenience constructors return autoreleased objects
This is an idiom used in mutators (sometimes called "setters"), but I think you typed it slightly wrong. Usually it looks like this:
-(void)setMyName:(NSString *)newName
{
[newName retain];
[myName release];
myName = newName;
}
The new name is retained, since this instance will need to keep it around; the old name is released; and finally the instance variable is assigned to point to the new name.
I have a feeling you mean this:
NSArray* a = [NSArray arrayWithObjects:#"foo", #"bar", nil];
and this
NSArray* a = [[NSArray alloc] initWithObjects:#"foo", #"bar", nil];
//...
[a release];
With the first style, the static method performs an alloc/init/autorelease on it for you so you don't have to. With the second style, you have more control over when the memory is released instead of automatically releasing when you exit the current block.
That code will crash your application. The second version only copies the pointer then releases the instance. You need to call [object retain] before releasing the reference.