I have a UITableView where numberOfSectionsInTableView and numberOfRowsInSection are being called twice, with scope issues on the second run. If I mask the problem, I get a scope issue on the first run of cellForRowAtIndexPath.
Most data all comes from an NSDictionary which is configured during viewDidLoad. I also have an NSArray configured at the same time. Once set they are never changed nor released.
When numberOfSectionsInTableView is called the first time, it's fine. Counts the elements as needed etc. It's then immediately called a second time (no idea why). On the second run, it cannot access the NSDictionary or NSArray items. Crash even when trying to NSLog them. For example:
NSLog(#"theMainDictionary %#",theMainDictionary);
usually results in EXC_BAD_ADDRESS but occasionally something like this:
theMainDictionary <_UITableViewSeparatorView: 0x4e73680; frame = (0 307; 320 1); opaque = NO; autoresize = W; layer = <CALayer: 0x4e4bf20>>
Again, this same line runs fine (logging the dictionary as expected) the first run through.
If I mask the problem by returning a fixed NSInteger, numberOfRowsInSection then does the same thing. If I mask numberOfRowsInSection, cellForRowAtIndexPath crashes on the first run. Same issue though - can't access theMainDictionary or the related NSArray.
I can't figure out why they're running twice - there's no reloadData anywhere. Nor do I know why the second call runs any differently. Any assistance greatly appreciated.
You're probably creating your dictionary with [NSDictionary dictionary] or one of the several similar factory methods (which returns an autoreleased instance), and then saving it directly to an ivar without retaining it. It will work fine until your program gets back to the main run loop, at which time the autorelease will resolve and the object gets freed.
There are a few ways to fix it:
Assign to a property declared retain rather than an ivar. This means self.theMainDictionary rather than just theMainDictionary. This will retain it for you, so it will stick around until you release it (or assign a different dictionary or nil to the property).
Use [[NSDictionary alloc] init] (or one of the many other init methods) rather than [NSDictionary dictionary] style. This returns an instance that you own, so it will stick around until you release it.
Explicitly call retain on the dictionary when saving it to the ivar. This takes ownership, so it will stick around until you release it.
In all cases, do remember to release the dictionary in your dealloc method, or the memory will leak.
All of the above probably applies to the array too. See Apple's documentation for a much more detailed explanation of memory management in Cocoa.
Related
I presume this isn't a common problem, which makes it a bit more difficult to answer. Any help is appreciated though.
I need to call this delegate a number of times in my app, and i noticed that after a number of times, the delegate starts to come back as NULL (and hence stops responding). I put an nslog everywhere the delegate gets called, so i know that at this point, it's fine:
UIImage *image = [self.delegate largeThumnailForMediaAtIndex:indexPath.row];
Then the next time this line gets called, the delegate is set to NULL. No lines around it call or set the delegate. I put an NSLog on the setDelegate method too, and that didn't get called before it changed to NULL.
Any code you might need to see, let me know. Any ideas you want me to try out, let me know about that too.
Thanks for your help.
EDIT: Bizarre, but might help to lead to a solution. I put an NSTimer scheduledTimer.. in the class which gets made the delegate, and got it to fire that once a second so I could see if it turned null at any point. The result i got, however, was that this time it didn't turn null. It returned all of the delegate methods. When i took the timer out, it goes back to returning NULL. Obviously having a timer in there is an odd workaround 'solution'. I'm hoping this rings a bell for someone and gives them a clue to where the problem might lie?
EDIT 2: I've solved this problem by, instead of using this code in my AppDelegate:
JCreateViewController *create = [[JCreateViewController alloc] init];
[create.navigationBar addLeftButtonWithTitle:#"Back" type:JButtonTypeArrow];
create.navigationBar.title = #"Entry #17";
[self.window addSubview:create.view];
Declaring it in my header file, then using this:
self.create = [[JCreateViewController alloc] init];
[self.create.navigationBar addLeftButtonWithTitle:#"Back" type:JButtonTypeArrow];
self.create.navigationBar.title = #"Entry #17";
[self.window addSubview:self.create.view];
I don't understand why this makes a difference though. I'd love to know, if anybody does know?
Looks like you're using ARC. Whatever this object is, nothing owns it. Nothing has a strong reference to it, so it gets released, and then at some point it gets deallocated.
JCreateViewController *create = [[JCreateViewController alloc] init];
This is a local variable. When the variable goes out of scope at the end of the method, you can't access that object anymore. Under MRR, this would be a leak. Under ARC, the object is going to die, just like it was in an autorelease pool.
self.create = [[JCreateViewController alloc] init];
By creating a property (presumably strong) and putting the controller into that property, you've given whatever self is an owning reference to the controller. It will now live as long as the property isn't reassigned or set to nil.
The timer fixed things because the timer retains its target (which I believe was the controller (your question is rather unclear)), and the timer itself is retained by the run loop. So the run loop keeps the repeating timer alive and the timer kept your controller alive.
In short, make sure something owns this object and it'll stick around.
This maybe too basic but I couldn't find an exact answer. I'll be happy to delete/close this post if anyone point me to similar posts.
I want to call method "getString" to return a formatted string and set to my label like this:
-(NSString*) getString {
NSString *result = [NSString stringWithFormat:#"blah blah %#", someOtherString];
return result;
}
-(void) viewDidLoad {
someLabel.text = [self getString];
}
This code worked for me, but I am concerned that result is allocated by a convenient method thus may be auto-released before it got retained by the label. Is it true? If so, when exactly would an object from convenient method get released?
Second question, if in case I have to use [NSString alloc] to create my own string object. Where and how should I release it?
Thanks in advance
Leo
It isn't true that the object will be autoreleased before you retain it. The details of when the pool gets drained are unimportant to answer this question, except that it can't happen during your code unless you call drain or release on the pool. If your function calls another function, except in some specific edge cases both the function you call and the function you called from need to exit before that thread can do anything else. The autorelease pool is thread-specific.
To answer your second question, if you alloc an object you should release it when you've finished using it. Simple as that. That includes when you pass it to other code that needs it, because it should be up to that other code to claim ownership if it needs to.
This code worked for me, but I am concerned that result is allocated by a convenient method thus may be auto-released before it got retained by the label.
Yes, it will be autoreleased because it is returned by a method whose name does not contain new, alloc or copy. No, this won't happen before the calling method viewDidLoad returns.
In fact, the autorelease-pool, to which it's added will probably be the one set-up and teared-down by the runloop, so nothing is going to happen to it until the end of the current iteration through the runloop.
I'm new, but I read a lot about memory management, and really tried to find the answer myself.
It sounds so basic and yet I apparently don't get it.
I have a NSMutableArray as property in .h:
#property (nonatomic, retain) NSMutableArray *documentListArray;
I synthesize it and release it in (void) dealloc.
To populate the array, I have a method - (void)updateRecordList and in there:
self.documentListArray=[DocumentDatabase getDocumentListSortedByDate];
EDIT:next line:
[[self recordListTableView] reloadData];
where DocumentDatabase is a different class with the class methods getDocumentListSortedByDate and getDocumentList.
Here is what is happening in getDocumentListSortedByDate:
NSMutableArray *returnArray = [DocumentDatabase getDocumentList];
//sorting...
NSLog("array count:%i",[returnArray count]); //returns correct numbers first and second time
return returnArray;
and in getDocumentList
NSMutableArray *returnArray = [NSMutableArray arrayWithCapacity:files.count];
//add file data to array...
return returnArray;
This works the first time I call updateRecordList, but after adding a file and calling updateRecordList a second time, it crashes with (using NSZombies):
* -[NSCFNumber dealloc]: message sent to deallocated instance 0x7504c90.
With a lot of logging I narrowed the problem down to the line above in updateRecordList and it works if I change it to:
self.documentListArray=[[DocumentDatabase getDocumentListSortedByDate] retain];
My conclusion is that the array down in getDocumentList has been autoreleased before it arrives. So my questions are:
1. Why do I have to retain it there? Shouldn't that happen by itself by declaring the property (retain)?
Or, in other words, why is the array autoreleased too early (assuming this is what is happening)?
2. When I assign a new array to self.documentListArray, is the old array automatically released? If I try to release it myself before getting a new documentList, it crashes too.
Thanks in advance for any reply.
EDIT:
Maybe I'm an idiot: I failed to mention that documentListArray is the data source for an UITableView (see the added line on top). I suspect that I am doing something wrong with populating the table view, and the array gets retained...? It does however crash on assigning the property, not on reloadData.
I go back to study if I use the UITableViewDataSource protocol properly. Thanks to everybody, your answers brought me hopefully on the right track. Will update when solved.
EDIT2:
It works now without retaining, and I think I understand why: I debugged extensively and found that Objects contained in Objects added to the array where nil. Particularly, deep down in encodeWithCoder I did not use "self" when assigning values. When decoding, those values where nil. Since I changed that, it seems to work.
I suspect that not assigning the new array caused the crash, but the TableView which would read the new array-even before I call reloadData. Which would lead back to Davids question of synchroneous access. Thank you all for your help.
The code you've shown it appears correct; it should not be necessary (or correct) to call retain yourself, as long as you are assigning the value to a property with retain semantics before the autorelease pool is drained. Are all the calls (getDocumentListSortedByDate, getDocumentList) happening synchronously, or are you doing any of this in the background? Double-check that you're assigning using the "self." ("self.documentListArray =") instead of just assigning directly to the instance var ("documentListArray ="); if you omit the "self.", the setter is bypassed.
No, don't free the old value before assigning; that's the setter's job.
After reading:
Memory management of a view controller in Objective-c
and
Does UIView's addSubview really retain the view?
I wrote the following code to toggle a subview:
#synthesize switchableView, viewSelector, currentSubview;
//...
if(switchableView.subviews.count != 0)
[[switchableView.subviews objectAtIndex:0] removeFromSuperview]]
self.currentSubview = (veiwSelector.selectedSegmentIndex == 0) ?
[ViewA new] : [ViewB new];
[switchableView addSubview:currentSubview.view];
//[currentSubview release]; //<---crashes if I uncomment this line
It seems to run fine if I comment out that release line, but I can't wrap my head around why. Here's the way I understand what happens and maybe someone can tell me where I go wrong:
So lets consider currentView:
A gets alloc-ed by the 'new' message--retain count=A:1
A gets retained by the setter--retain count=A:2
A's view gets (supposedly) retained--retain count=A:2.1
next time through...
A's subview gets released count=A:2
B gets alloc-ed by the 'new' message--retain count=B:1, A:2
A gets autoreleased by the setter-- B:1, A:1
B gets retained by the setter--B:1, A:1
nothing ever gets rid of A?
So should I change my code, or am I wrong about the way memory management works in this language...or both?-
Ok, step one, ignore the retainCount. It's one of those things Apple should rename to something like lsdjiofsudfoiwjeriowhfiuwhrteiuhweifhsdjkfhsiurwoieuriosfho so people won't guess it's name, and not list it in the documentation. For your purposes, it's entirely useless, so ignore it.
Now that I've said that, let's consider something: addSubview: DOES retain its argument, and removeFromSuperview releases the receiver.
Finally, it's hard to tell what currentSubview is. It has a view property which would lean towards a VC, however, the way you're using it by itself, would indicate its a normal view. Perhaps you can clarify so I can continue my answer.
Your understanding of retain and release is correct, as is your code. That suggests that the problem lies outside of the code you've posted. For example, you would have this problem if your currentSubView property was defined as assign instead of retain.
You code is not structured well, however. This would be much clearer:
self.currentSubView = [[ViewA new] autorelease];
Furthermore, view controllers are meant to be cached, not created and released each time the user toggles a display. Typically, you create your view controllers beforehand, and access their .view property when necessary to display the view. UIViewController will automatically deallocate non-visible views in low memory conditions, and re-allocate their view when the .view property is accessed.
Change the release line to
self.currentSubview = nil;
and I think you'll be fine. You're releasing, but not setting the property to nil. So, when it gets re-assigned next time through, release will be called again on it. But you already released it so... boom.
I ran a few stress tests on my Iphone app. The results are below. I am wondering if I should be concerned and, if so, what I might do about it.
I set up a timer to fire once a second. Whenever the timer fired, the app requested some XML data from the server. When the data arrived, the app then parsed the data and redisplayed the affected table view.On several trials, the app averaged about 500 times through the loop before crashing.
I then removed the parsing and redisplay steps from the above loop. Now it could go about 800 times.
I set up a loop to repeatedly redisplay the table view, without downloading anything. As soon as one redisplay was completed, the next one began. After 2601 loops, the app crashed.
All of the above numbers are larger than what a user is likely to do.
Also, my app never lasts long at all when I try to run it on the device under instruments. So I can't get useful data that way. (But without instruments it lasts quite a while, as detailed above.)
I would say you need to be very concerned. The first rule of programming is that the user will never do what you expect.
Things to consider:
Accessor methods. Use them. Set up
properties for all attributes and
always access them with the
appropriate getter/setter methods:
.
object.property = some_other_object; -OR-
[object setProperty:some_other_object];
and
object = some_other_object.some_property;
object = [some_other_object some_property];
Resist the temptation to do things like:
property = some_other_object;
[property retain];
Do you get output from ObjectAlloc?
There are 4 tools from memory leaks,
performance and object allocations.
Are none of them loading?
What do you get when the app crashes?
EXEC_BAD_ACCESS or some other error?
Balanced retain (either alloc or
copy) and release. It is a good idea
to keep every alloc/copy balanced
with a release/autorelease in the
same method. If you use your
accessors ALL OF THE TIME, the need
for doing manual releases is seldom.
Autorelease will often hide a real
problem. It is possible Autorelease
can mask some tricky allocation
issues. Double check your use of
autorelease.
EDITED (Added based on your fault code)
Based on your above answer of "Program received signal: 0". This indicates that you have run out of memory. I would start by looking for instances that your code does something like:
myObject = [[MyClass alloc] init];
[someMutableArray addObject:myObject];
and you do not have the "release" when you put the new object into the array. If this array then gets released, the object, myObject, will become an orphan but hang around in memory anyway. The easy way to do this is to grep for all of your "alloc"/"copy" messages. Except under exceedingly rare conditions, there should be a paired "release""/autorelease" in the same function. More often than not, the above should be:
myObject = [[[MyClass alloc] init] autorelease];
[someMutableArray addObject:myObject];