There's a little bit uncommon situation in my app, that is,
I have to reload some retain properties everytime when the view is going to appear,
the code looks like this:
// .h
#property (nonatomic, retain) NSArray *myData;
// .m
#synthesize myData;
- (void)viewWillAppear:(BOOL)animated {
... // get FetchRequest and so on
self.myData = [self.context executeFetchRequest:request error:&error]; // Line 1
[super viewWillAppear:animated];
}
- (void)viewDidUnload {
self.myData = nil;
[super viewDidUnload];
}
- (void)dealloc {
[myData release]; // Line 2
[super dealloc];
}
there are several points:
1st. as you see, the property "myData" is retain, so I think every I set some object for it, it would automatically retain that object?
2nd. I have to reload "myData" everytime the view will appear, just like the code of Line 1 above.
3rd. Since it is a retain property, I have to release it myself correctly.
Now, question is, do I correctly managed the memory without any leaking of "myData" using the codes above?
If the view would appear many times before it is dealloc, (like push in a further view in a UINavigationController and pop out for several times),
then myData would retain some object more than once, but I only release it in the dealloc for 1 once in Line 2, so is that ok?
But if I add this method the to viewController,which I think is more safe for avoiding memory leaks:
- (void)viewWillDisappear:(BOOL)animated {
self.myData = nil;
[myData release];
[super viewWillDisappear:animated];
}
- (void)dealloc {
// [myData release]; // don't release it here.
[super dealloc];
}
my app would crash after one or two times I push in and pop out the view,
So which one is really wrong?
Thanks a lot!
You are not only releasing it in Line 2, it will be also released in Line 1 when replaced as well as in viewDidUnload, so your code on top is just fine. The key is that
self.myData = anything;
is expanded to
[self->myData release];
self->myData = [anything retain];
so by assigning anything (including nil) you are already calling release implicitly. You could in fact replace Line 2 with self.myData = nil; to have never to call release since you don't have any explicit retain.
.h
#property (nonatomic, retain) NSArray *myData;
.m
#synthesize myData;
By including these lines in your code a setter and getter is created for your property myData. The setter generated at run time for objects looks something like this,
- (void)setMyData: (id)newValue
{
if (myData != newValue)
{
[myData release];
myData = newValue;
[myData retain];
}
}
The total effect is that whenever you access the property by appending self in front you are actually calling the setters and getters. So the following two lines are the exact same.
self.myData = nil;
[self setMyData:nil];
So your original code was already correct.
Related
I am new to objective-c and trying to understand better why the following is occurring... in my view controller, I have this in viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
[self createProjectData];
}
And then:
- (void)createProjectData
{
if(!self.projectData) {
self.projectData = [[NSMutableArray alloc] initWithObjects:tempProjectInfo1, tempProjectInfo2, nil];
}
projectData is a public property:
#property (nonatomic, strong) NSMutableArray *projectData;
My problem is that when I navigate to a different View Controller and return to this one, projectData is null even though I had initialized previously with the above values... so I'm really hoping someone can explain how I can retain the property value so when I return it has all of the items that I had added to the mutable array.
Check if somewhere inside viewDidDisappear your code is niling your array.
You may also have code for dealing with memory warnings doing something similar
for my table view I have the following going on (paraphrased)
.h
#interface OptionsView : UIView <UITableViewDelegate, UITableViewDataSource>
#property (nonatomic, retain) NSArray *dataSource;
.m
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
...
self.dataSource = [NSArray arrayWithObjects:options, sections, sponsor, nil];
}
return self;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if(self.dataSource) {
NSArray *ds = [self.dataSource objectAtIndex:indexPath.section];
NSDictionary *d = [ds objectAtIndex:indexPath.row];
ActionBlock a = [d objectForKey:ACTION]; // <-- Thread 1: EXC_BAD_ACCESS (code=2, address=0x802)
if(a != nil) a();
}
}
You can see that in my didSelectRowAtIndexPath I'm getting an EXC_BAD_ACCESS but I'm not sure why. Because I'm using arc it isn't a zombie problem (already checked).
Using breakpoints I see that the self.dataSource exists after it's initialized, but not later when it's needed. It doesn't show up as <nil> either, it's just white space. Also, this works in debug but not in release so what does that mean?
** EDIT adding a screenshot **
You will notice that neither ds or d show up.. odd?
So there's two places I can see that might cause the problem. First, what is ACTION? Is it some sort of NSString? Just want to make sure that you're using a valid key object. Second, (and more likely the problem), it looks like ActionBlock is some kind of code block you're storing in a collection array. Are you copying that block before you store it in the array/dictionary? You must copy any block you intend on keeping around (storing) longer than the scope it was created in. This is easy to do. For example:
void (^NewBlock)(void) = [^{
....code....
} copy];
Alternately:
void (^NewBlock)(void) = ^{
....code....
};
[dictionary setObject:[NewBlock copy] forKey:ACTION]; //Assuming #define ACTION #"action"
This copies it onto the heap so that it can be stored. If you don't copy it, you'll get BAD_EXC_ACCESS anytime you try to reference the block outside the scope it was created in.
It seems you´re using a UIViewController instead of a UITableViewController. If I remember correctly, you have to go to the inspector and drag the delegate and datasource from the tableView to the UIViewController. I don´t remember exactly and I´m not on my computer but I´m sure I did it several times before I began to use a UITableViewController for every tableView I have.
Oh, and i wouldn´t use dataSource as a name for your array. Just to prevent naming conflicts.
Im using a Base view controller for some other view controllers,
Im making it the base as I have 4 to 6 of other view controllers that will be showing the same label and image...
so I tried to make this base page and subClass the other ViewControllers,
my doubt is that if I leave the dealloc for the strings that contain the value for the label and other string, when dealloc is called for that page, then I get an exception
pointer being freed was not allocated
but I dont want just to comment out the deallocs for the labels,
so What im i doing wrong, missing?
here the BasePage
#import "BasePage.h"
#implementation BasePage
#synthesize titleString = _titleString;
#synthesize justifyTitleString = _justifyTitleString;
- (void) dealloc {
[_titleString dealloc];
[_justifyTitleString dealloc];
[super dealloc];
}
- (id)initWithParams:(NSString *)title :(NSString *)justifyTitle
{
self = [super init];
if (self) {
self.titleString = title;
self.justifyTitleString = justifyTitle;
}
return self;
}
my app uses a navigation controller,
so when I call a page i use:
CentresVC *centresVC = [[[CentresVC alloc]initWithParams:[NSString stringWithFormat:#"Centre %d", indexPath.row]:#"center"]autorelease];
[self.navigationController pushViewController:centresVC animated:YES];
and pop the views when navigating back,
So I have the doubt of when using
[_titleString dealloc];
[_justifyTitleString dealloc];
where the pointer for those labels get saved?, and if i just commented out wich doesnt seem nice, would i get then a memory leak that would crash?
how to fix this?
thanks!
As Richard said, you shouldn't be calling other objects' dealloc method. Ever.
If titleString and justifyTitleString are retained/strong properties, then this version below would work. Assuming that no other objects had retained titleString and justifyTitleString, their retain counts would go to 0, and their dealloc methods would be called automatically.
- (void) dealloc {
[_titleString release];
[_justifyTitleString release];
[super dealloc];
}
This next option would work too, since the synthesized setter for a strong/retained property sends release to the old value of the property before assigning the new one. Also, this would be preferred if you had overridden your setter to do additional cleanup of the old value.
- (void) dealloc {
self.titleString = nil;
self.justifyTitleString = nil;
[super dealloc];
}
Finally, if you were using ARC, and you didn't need additional cleanup like in the second case above, you wouldn't need to override dealloc at all. But if you did need to override dealloc, you would omit [super dealloc] since that would be provided automatically by the compiler.
My app is receiving memory warnings because it's asking for lot of memory. I try to release every allocation. However, sometimes I don't know how to do it.
For example: I have two pairs of .h and .m file. One of them makes connections with a server and the other with local SQLite.
Usually, the code which calls to a method from those files are like this:
-(NSMutableArray *) getRecentActivity{
LocalStorageController *local = [[LocalStorageController alloc]init];
return [local getRecentActivity];
}
getRecentActivity returns a NSMutableArray.
Well, in that piece of code we can see that I am allocating memory for the LocalStorageController but I never call to the release method so, I suppose, the more I call that function, the more memory I will be allocating.
If I call autorelease after init, it will crash.
Moreover, usually, I use this other kind of code:
ServerConnection *serv = [[ServerConnection alloc]init];
NSMutableArray list = [serv getMyListOfContacts];
Which uses ASIHTTPRequest and, if I call [serv release]; after the second line, the app crashes with EXC_BAD_ACCESS pointing to a line in ASIHTTPRequest library.
How is suppose to manage this situation?
Thank you very much!
The first case is easy;
-(NSMutableArray *) getRecentActivity{
LocalStorageController *local = [[LocalStorageController alloc]init];
NSMutableArray *tmp = [local getRecentActivity];
[local release];
return tmp;
}
The second case is hard to solve in a general way without seeing more of the actual code.
Using serv as a property would be fixing this retain/release problem.
In your .h:
#property (nonatomic, retain) ServerConnection *server;
In your .m:
#synthesize server;
- (void)dealloc {
[server release];
// The rest of your releases here...
[super dealloc];
}
- (void)yourMethod {
ServerConnection *myServConnection = [[ServerConnection alloc] init];
self.serv = myServConnection;
[myServConnection release];
NSMutableArray list = [self.serv getMyListOfContacts];
}
Just keep on using self.serv in this class from that point on and you won't have a problem with having the object being released.
i'm a little bit confused with memory management in view controllers.
Lets say i have header file like this:
#interface MyController : UIViewController {
NSMutableArray *data;
}
#property (nonatomic, retain) NSMutableArray *data;
#end
and .m file looks like that:
#implementation MyController
#synthesize data;
- (void)dealloc
{
[self.data release];
[super dealloc];
}
- (void)viewDidLoad
{
[super viewDidLoad];
if (self.data == nil)
self.data = [[NSMutableArray alloc] init];
}
- (void)viewDidUnload
{
[super viewDidUnload];
[self.data release];
self.data = nil;
}
Is that ok from the correct memory management point of view? Will that work after dealloc via Memory Warning? How You do that in your apps?
Thanks for your answers ;)
While the alloc-retain calls balance out in viewDidLoad and viewDidUnload and should prove no problem memory-wise, it would be cleaner to take ownership only once and relinquishing it once rather than twice.
- (void)viewDidLoad
{
[super viewDidLoad];
if (self.data == nil)
self.data = [NSMutableArray array];
}
and
- (void)viewDidUnload
{
[super viewDidUnload];
self.data = nil;
}
You are not guaranteed that viewDidUnload will ever get called. Unlike init/dealloc, which get called in pairs, viewDidUnload is undeterministically called. viewDidUnload is only called if there is a low memory situation and your view is not the active view.
Depending on how your model is created and the implications of it remaining in memory, it may make more sense for you not to get rid of it. An example of this may be that recreating that data may involve an expensive web service call. It therefore would be a bad user experience to have to wait for that data to get recreated. If it must absolutely go, a better strategy may be to cache the data to disk so that you can easily reconstruct it.
viewDidUnload should only contain cleaning up your IBOutlets and flushing easily recreatable data.
These lines from -viewDidUnload both release data:
[self.data release];
self.data = nil;
Since you're using the property setter in the second line, and data is a retained property, the setter will release data. This is an over-release, and it'll cause a crash either right away or later, depending on whether other objects also retain that object. To fix, simply delete the first line and rely on the setter to do the right thing.
The -dealloc method, on the other hand, shouldn't use the setter as it does now. You should change:
[self.data release];
to:
[data release];
data = nil; // this line isn't strictly necessary, but often considered good form
The reasoning here is that it's conceivable that this class could be subclassed, and someone might override the property setter in such a way that it has some side effects that could cause problems when the object is being deallocated. You should access the ivar directly -- notice that I left off the "self." so that we're dealing with the ivar and not the property accessor. (-init and -dealloc are the only places where you have to worry about that; use the property accessors everywhere else.)