views:

194

answers:

4
+1  Q: 

When to use self?

I have an iPhone app where one view, View A, updates another view in a tab bar, View B, using:

// This works.
- (void) reloadData
{
    MyDB * db = _GET_DB_CLASS;
    if(data != nil) // data is a property of type NSMutableArray
        [data release];

    NSMutableArray * d = [db getDataQuery];
    data = s; // Don't release since we are not using the accessor. And retain count should be 1.
}

If I do this, it doesn't work (e.g. I update B, then switch to B, crash. I can't see anything useful in the logs either ... ).

    NSMutableArray * d = [db getDataQuery];
    self.data = s; // Doesn't work
    [data release];

I have not used a custom setter. What is going on?

A: 

Have you declared your data property to be "retain", as opposed to "assign"? You should have something like:

@property(retain) NSMutableArray *data;

If not, then when you assign to the data property, it won't increment the reference count. Your subsequent release will dealloc the object and the next reference to the data property will crash.

Take a look at the Apple docs for setter semantics for more information about retain.

Jesse Rusak
Yes I have. It's the typical, @property (nonatomic, retain) NSMutableArray * data;At first I thought that maybe it is sending release to a nil value, but I am sure they took that into account and the generated setters and getters will handle nils.
Megasaur
RE: nil: sending release to nil does nothing, like all messages to nil. I don't think that's the problem.
Jesse Rusak
I don't know, Megasaur. Can you try posting more code, like what getDataQuery does? Or maybe what crash you're getting and where? Are you sure getDataQuery is returning a retained value? Typically, methods named get* return autoreleased, not retained values. (You might want to change the name.)
Jesse Rusak
A: 

Generally, if you are just using a retain property, you should always use self.data and call the generated setter.

It:

  1. Calls release on the old value automatically (checks for nil)
  2. Calls retain on the new value

Then your code would be simply:

self.data = s;
[s release];

Two things to do to debug:

  1. Use static analysis -- I have personally found this tool to be 100% accurate with retain/release issues: http://clang.llvm.org/StaticAnalysisUsage.html

  2. Follow the instructions on my blog post on memory debugging on the iPhone:

http://loufranco.com/blog/files/debugging-memory-iphone.html

Lou Franco
Good point. I didn't even notice he wasn't using the property in the first if block.
Jesse Rusak
+1  A: 

I totally forgot about this. Too much work. So I am not sure what the exact nature of the problem was. But I took a closer look at the retainCounts in the debugger (before trying out what Lou suggested).

It is a retain/release issue. I guess the rule is to be consistent with your usage. Anyway this works:

- (void) reloadFridgeData
{
    MyDB * db = _GET_DB_CLASS;
    if(self.data != nil)
    {
        self.data = nil;
    }

    NSMutableArray * newData = [db getData];
    self.data = newData;
    [newData release];
}
Megasaur
A: 

If data is definitely a property with no underlying value of the same name, then it must be accessed from self as in [[self data] release] or [self.data release]. However as mentioned if the property is set to auto-retain, you can accomplish the same thing by setting the property to a new value, such as nil. (As you've done in the code you noted works.)

dlamblin