Register FAQ / Rules Forum Spy Search Today's Posts Mark Forums Read
Go Back   MacRumors Forums > Apple Systems and Services > Programming > Mac Programming

Reply
 
Thread Tools Search this Thread Display Modes
Old Jun 23, 2006, 01:57 AM   #1
mduser63
macrumors 68040
 
mduser63's Avatar
 
Join Date: Nov 2004
Location: Salt Lake City, UT
Send a message via AIM to mduser63
Question about object alloc/init related crash

I'm seeing something weird that I can't figure out. I've just done the challenge at the end of Chapter 15 in Aaron Hillegass' Cocoa Programming for Mac OS X. You're supposed to write a document-based app that will allow the user to draw ovals. Anyway, I've got it working, but I'm seeing something that I don't understand. In it the initWithFrame: method for the view implementation, really the only thing that gets done is allocation and initialization of the NSMutableArray instance variable (ovals) used to hold the NSBezierPath's for the ovals. The code below causes no problems:

Code:
- (id)initWithFrame:(NSRect)frameRect
{
	if ((self = [super initWithFrame:frameRect]) != nil) {
		// Add initialization code here
		ovals = [[NSMutableArray alloc] initWithCapacity: 1];
	}
	return self;
}
However, this code crashes immediately upon launching the application. If I run it from XCode, the debugger comes up immediately. (Either I don't know how to use the debugger, or you have to be some kind computer brain to use it, because the information it gives is really rather useless to me.)

Code:
- (id)initWithFrame:(NSRect)frameRect
{
	if ((self = [super initWithFrame:frameRect]) != nil) {
		// Add initialization code here
		ovals = [NSMutableArray arrayWithCapacity: 1];
	}
	return self;
}
Anyway, does anyone know why using an explicit alloc then an initWithCapacity: method as opposed to the arrayWithCapacity: makes a difference? I'm still having a pretty hard time getting my head around all the reference counting, retain/release and autorelease pool stuff in Objective-C.

I've uploaded the project here in zip format if you want to download it. The code in that project right now is in "crash" state. If it matters, I'm doing all this on an Intel Mac.

(I realize that I may not have done everything else in my code the best way or the way it should be done, but right now I'm really only concerned with figuring out the problem described here.)
mduser63 is offline   0 Reply With Quote
Old Jun 23, 2006, 02:16 AM   #2
HexMonkey
Administrator
 
HexMonkey's Avatar
 
Join Date: Feb 2004
Location: New Zealand
The arrayWithCapacity method is just a convenience method which is basically equivalent to [[[NSMutableArray alloc] initWithCapacity: 1] autorelease]. So in your second code fragment, the ovals object is added to the autorelease call and is released shortly after the OvalView is created (resulting in a retain count of 0). Then when it's used, it's already been deallocated, hence the crash.
HexMonkey is offline   0 Reply With Quote
Old Jun 23, 2006, 02:23 AM   #3
mduser63
Thread Starter
macrumors 68040
 
mduser63's Avatar
 
Join Date: Nov 2004
Location: Salt Lake City, UT
Send a message via AIM to mduser63
Wow you're fast HexMonkey. I figured I'd get an answer in the morning (it's after 1:00 AM here).

Am I correct that the solution (other than using the first code snippet) is to send a retain message to ovals then explicitly send it a release method in my (overridden) dealloc method? I don't quite understand why it gets released, as it seems like I haven't done anything different in other code that works fine. The retain count is 1 right after it is initialized. When does it get a release message causing the retain count to go down to 0 (causing it to be released)?
mduser63 is offline   0 Reply With Quote
Old Jun 23, 2006, 02:47 AM   #4
HexMonkey
Administrator
 
HexMonkey's Avatar
 
Join Date: Feb 2004
Location: New Zealand
Quote:
Originally Posted by mduser63
Wow you're fast HexMonkey. I figured I'd get an answer in the morning (it's after 1:00 AM here).

Am I correct that the solution (other than using the first code snippet) is to send a retain message to ovals then explicitly send it a release method in my (overridden) dealloc method? I don't quite understand why it gets released, as it seems like I haven't done anything different in other code that works fine. The retain count is 1 right after it is initialized. When does it get a release message causing the retain count to go down to 0 (causing it to be released)?
Adding a retain message would solve the problem, but it's not as elegant a way to do it as in the first code fragment since you're effectively allocating, retaining then releasing the object rather than just allocating it.

It gets released because arrayWithCapacity is a convenience constructor, so by convention it returns an autoreleased object. The object is then released when leaving the scope of the current autorelease pool, typically done in the event loop (which is handled by the OS).

You might want to read relevant sections of the following pages to make a bit more sense of this:
Object Ownership and Disposal
Autorelease Pools
HexMonkey is offline   0 Reply With Quote
Old Jun 23, 2006, 03:15 AM   #5
HiRez
macrumors 601
 
HiRez's Avatar
 
Join Date: Jan 2004
Location: Western US
Quote:
Originally Posted by mduser63
Am I correct that the solution (other than using the first code snippet) is to send a retain message to ovals then explicitly send it a release method in my (overridden) dealloc method? I don't quite understand why it gets released, as it seems like I haven't done anything different in other code that works fine. The retain count is 1 right after it is initialized. When does it get a release message causing the retain count to go down to 0 (causing it to be released)?
You are correct in your assumptions. The reason the second code fragment fails is that you're using a convenience initializer, which is a convention in Cocoa (usually of the form xxxOfxxx, such as stringWithCString, arrayWithCapacity, etc. ) that, as Hex mentions, is already autoreleased. Autoreleased meaning that the runtime system is going to send the object a release message the next time it completes an event loop. This means it'll be done "behind your back", so if you want to hold on to that object, you'd better send it a retain message.

It's tricky because you just have to know which methods these convenience initializers are (usually any initializer that does not start with "init"). The documentation generally does not tell you on a method-by-method basis. So using convenience initializers, you don't need to explicitly retain the object. Usingy either initialization method, you still must send the object a release message in your dealloc method in any case, unless it's just a temporary object that's only being used within the scope of a local method, in which case you'd either send it a release message as soon as you're done with it in the same method (if you used alloc-init), or just let the runtime deallocate it in the autorelease pool (if you used a convenience initializer or used alloc-init with an explicit autorelease message). One other note, only use alloc with init, never use alloc with a convenience initializer (it's implicitly doing an alloc).

To summarize:

1. If you create a subobject that you want to keep around in for the life of your main object (the one you're creating, in your initXxx or sometimes awakeFromNib methods), use either alloc-init, or use a convenience contructor with retain. Always release such objects in your dealloc method in any case.

2. If you only need an object briefly, do the initialization and the deallocation in the same scope. Make sure that when your program changes scope (for example, completes a for loop), you've taken care of releasing that object as appropriate.

Using alloc-init:
Code:
int i;
for (i = 0; i < 10; i++) {
	NSNumber *n = [[NSNumber alloc] initWithInt:i];
	NSLog(@"The number is %d", [n intValue]);
	[n release]; /* done with n,release it or you leak this memory */
} /* it's too late to do it after here because you created it inside the loop */
Using a convenience initializer:
Code:
int i;
for (i = 0; i < 10; i++) {
	NSNumber *n = [NSNumber numberWithInt:i]; /* convenience initializer */
	NSLog(@"The number is %d", [n intValue]);
} /* no need to release it; you created it with a convenience initializer, it's autoreleased and will be deallocated at some time in the future (you should not care about when in most cases) */
__________________
Go outside, the graphics are amazing!
HiRez is offline   0 Reply With Quote
Old Jun 23, 2006, 12:17 PM   #6
mduser63
Thread Starter
macrumors 68040
 
mduser63's Avatar
 
Join Date: Nov 2004
Location: Salt Lake City, UT
Send a message via AIM to mduser63
Thanks for the replies! I read both of the documents that HexMonkey linked to, and that helped a lot. My information about reference counting and autorelease pools came from Programming in Objective-C, and while the information in that book is fine within the scope of the programs in the book (all command-line, non-AppKit), it doesn't give an accurate enough description for the move to Cocoa. For example, I didn't understand the scope of autorelease pools at all. Anyway, thanks for the help!
mduser63 is offline   0 Reply With Quote

Reply
MacRumors Forums > Apple Systems and Services > Programming > Mac Programming

Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Similar Threads
thread Thread Starter Forum Replies Last Post
Resolved: How do I convert an NSData object to an NSDictionary object, and vice versa? moonman239 iPhone/iPad Programming 6 May 2, 2014 01:17 PM
Crash related to UISearchDisplayController on iOS 7 namanhams iPhone/iPad Programming 0 Sep 23, 2013 03:30 AM
Resolved: ARC, @property, alloc/init larswik iPhone/iPad Programming 5 Aug 25, 2013 05:36 PM
When to use [[alloc]init] Branda22 iPhone/iPad Programming 20 Jun 26, 2013 01:45 PM
Silly question about alloc/init for UILabel animefx iPhone/iPad Programming 1 Jul 14, 2012 12:53 AM

Forum Jump

All times are GMT -5. The time now is 04:59 AM.

Mac Rumors | Mac | iPhone | iPhone Game Reviews | iPhone Apps

Mobile Version | Fixed | Fluid | Fluid HD
Copyright 2002-2013, MacRumors.com, LLC