Crash when trying to scroll


#1

My completed code (sans challenge) for the chapter works as advertised. But when I try to scroll the screen up or down on either iPod or simulator, the app crashes. Is this expected behavior at this stage?

Thanks,
Byrne


#2

No. I bumped up the count of generated Possession objects and could still scroll with no issue.

Can you show the code for -tableView:cellForRowAtIndexPath:?


#3

Thank you for the confirmation. I performed yet another recheck of my code and it all looks fine. (We’ve all heard that before!) But now that I know something’s amiss, I think I’ll substitute code from the solution project, one file at a time, and do a process of elimination.

[size=85]edit:[/size]
OK, I figured out where the problem originated but I’m not sure why. The issue had to do with setting ivars in Possession.m. I’ve completed Programming in Objective-C (Kochan) in which the author shows assignment of parameters to instance variables within a method (of the same class) as such:

  possessionName = name;

Not only did this work on all the examples in his book, it also worked when creating the Possession class in chapter 2, BNRG (I took liberties with the code in the text on this point because I thought I had it down). And, it works until I try scrolling in chapter 10. But to eliminate the scrolling crash, I needed to use:

[self setPossessionName: name];

or the shorthand:
self.possessionName = name;

I’m still puzzling this one out. Maybe I’ll take up basket weaving. . .

Byrne


#4

There’s a subtle difference between assigning a value to an instance variable and using the setter method (as generated by the property synthesis) and that’s the retain count of the object you’re setting to the instance variable.

Using [self setPossessionName: name] increments the retain count on name. More-or-less like this:

- (void)setPossessionName:(NSString *)name {
     [name retain];
     [possessionName release];
     possessionName = name;
}

My guess is that the NSString you’re using is autoreleased, so when the table view gets around to needing to re-use cells (because of scrolling), that NSString has already been deallocated.


#5

Yes; it’s all about the retain counts. As @82times said, a simple assignment like possessionName = name doesn’t affect the retain count at all. Plus, by the rules of memory management, it says the Possession instance is NOT INTERESTED IN OWNING the reference now in possessionName. I recommend going back through chapter 3 on memory management. It’s hugely important.

Remember:

  1. If you obtain a reference to an object via new, alloc, or copy, then you own it and must release it later.

  2. If you obtain a reference by any other means, then you don’t own it. If you want to own it then you must retain it (and of course release it later).

#2 applies to straight assignment from a parameter as well. (That’s also one reason why dot notation is so misleading; it looks like straight assignment but it’s not…unless it’s a C struct so it’s not really dot notation…but you can’t tell just by looking at the assignment.)

Burn these into your brain (and skip the dot notation for now) and you won’t go wrong.


#6

Thank you! Indeed, when I add retains the scrolling works fine using possessionName = name;

But it also works fine, without the retain, using self.possessionName = name; so the issue appears to not so much involve use of the property synthesis shorthand setter method as it does use of self. So you seem to be spot-on about the issue but I’m unclear about the retain count rules here and why they seem to change when self is used.

Thanks again,
Byrne

[edit: This was posted in response to 82times, before I was aware of gc3182’s post.]


#7

Assuming @property attributes like (nonatomic, retain), it IS using retain, just not in an obvious way. You wrote earlier:

That’s true. The synthesized setter, invoked by both of these statements, uses retain. So whether you retain explicitly or use the setter doesn’t matter as far as memory management goes, though it’s best to always use the setter.


#8

The properties declarations in this code do not specify retain. (@property (nonatomic, copy) NSString *possessionName;). When equivalent code to the copy specification is shown on page 73, it shows that it is the parameter which is copied; maybe this has the same effect as a retain. (I can’t say I completely understand what happens, memory wise, to str and its copy t in that code snippet for setPossessionName: on p.73)

But the issue is still unresolved as to why the parameters are retained when self is used but not otherwise.

Thanks,
Byrne

edit: I’ve logged the retain count for the name parameter, from within the init method, and it appears equal to one in both the crash code and in the good stuff (using self).
I appreciate the help with this, especially since I discovered this using the non-BNR-recommended coding style.


#9

Ah, okay; copy ensures you’re the owner as well.

(The reason for using copy instead of retain is in case the original was, say, an NSMutableString. If you just used ‘retain’ then the value could change underneath you because you were pointing to the same object. With copy, you’re guaranteeing that the value you see right now will remain unchanged. You could just as easily use @property (nonatomic,retain) in this app because we know we’re not passing in an NSMutableString, but specifying copy is a good habit.)

Because the setter is invoked with self, and the other is straight assignment.

[self setPossessionName: name] sends the setPossessionName: message to self, which says to invoke the -setPossessionName: method, which was generated by the ‘@synthesize possessionName’ compiler command. That generated method, which you coded manually in chapter 3 (and built up to step-by-step on pages 67-69), is essentially what the one generated by @synthesize does (except for using -retain instead of -copy). (If it’s too confusing, you don’t have to use @property or @synthesize. They’re there for convenience because (1) they cut down on the amount of visible code, and (2) they ensure that the getters and setters (the latter especially) are coded correctly.)

self.possessionName = name is hugely misleading because it LOOKS like an assignment, but it’s COMPLETELY DIFFERENT from possessionName = name. self.possessionName (on the left side of an ‘=’) invokes the setter just like the [ ] syntax above. Like you wrote earlier, it’s essentially just a shortcut for the same method. So the old reference (if there is one) is released, the new reference is saved, AND the reference count is bumped up.

With possessionName = name, the address in name is just stuffed into possessionName, with NO change in the reference count, and whatever was in possessionName before, if anything, is now lost. So if the old possessionName had a reference that was retained, it’s now leaking. Worse, if name gets released (whether explicitly or because it’s in the autorelease pool and that pool gets drained), then there’s now NOTHING referencing that address so the memory is freed up…so when you try to reference that now-invalid address later, kaboom.

Bottom line: “self.possessionName = x” has a completely different behaviour from “possessionName = x”…and now you know why the book avoids the use of dot notation. Don’t use it. It obscures what’s really happening.

EDIT: noted that @property and @synthesize are for convenience but are optional


#10

So unless I use self, I’m not taking advantage of the copy attribute specified in the synthesized accessor. Got it!

Thank you for taking all that time, now I can move on.

Byrne


#11

That’s it exactly. Without self, you’re not taking advantage of the synthesized accessor at all.