I'm writing a project where a very straightforward assignment is sporadically failing. Fascinated with this result and curious to see what y'all make of it.
I've got a project with a large data set, and I'm creating and display a modal window to present some details about a new class instance. So I've got a window with a custom window controller class, with the following code:
MyWindowController.h:
#import <DataModel.h>
#interface MyWindowController : NSWindowController
#property (nonatomic, weak) FooClass *fooInstance;
#end
MyWindowController.m:
#implementation MyWindowController
#synthesize fooInstance = _fooInstance;
-(void) init {
self = [super init];
if (self) {
self.fooInstance = [FooClass new];
}
return self;
}
#end
Totally cookie-cutter, right? But when I first added the form and ran it a bunch of times, the assignment failed over and over again. self.fooInstance kept coming back as nil. I even confirmed that the FooClass initializer was running (stepped right through it) and returning a non-null pointer to a valid FooClass instance. And yet, after the assignment line, self.fooInstance remained null.
I ran it a bunch of times, watching this same result over and over again. Then, I replaced just the assignment statement with this:
FooClass *foo = [FooClass new];
self.fooInstance = foo;
...and the assignment suddenly started working, and it's run consistently ever since. Even when I reverted the code back to self.fooInstance = [FooClass new], it's worked perfectly.
I couldn't believe it... until I saw it happen AGAIN, in the same project, while banging out the same type of window for a different class.
I don't understand what's happening. The self.fooInstance accessors are totally #synthesized; there is no code running in the background that might be futzing with the class (it's a single-threaded modal window); and there's nothing bound to the class. It just... doesn't work. It's like the code for the window classes doesn't run right until it's run a few times to break it in.
What in the world is happening? Does anyone even hazard a guess that might explain this behavior?
I recommend taking a read through Mike Ash's explanation of weak pointers. This section is the relevant bit:
Weak References
First, what is a weak reference? Simply put, a weak
reference is a reference (pointer, in Objective-C land) to an object
which does not participate in keeping that object alive. For example,
using memory management, this setter creates a weak reference to the
new object:
- (void)setFoo: (id)newFoo
{
_foo = newFoo;
}
Because the setter does not use retain, the reference does not keep the new object alive. It will stay alive as long as it's retained
by other references, of course. But once those go away, the object
will be deallocated even if _foo still points to it.
OK, that explains the first part, but why does the second part work?
Well, when you write an instance variable like:
FooClass *foo = //assignment
The compiler says to itself "it's a really good idea to keep this thing around, (at least til the function goes out of scope under ARC), and it turns it into this:
__strong FooClass *foo = //assignment
That means whatever I assigned is retained, and therefore, because at least one object owns it, I can assign it to my weak instance variable.
Related
I am not a complete n00b here, but I feel like I must be missing something essential. I'm passing a variable into a new class instance via the class's init method:
#synthesize side;
- (id)initWithSide:(NSString *)aSide frame:(NSRect)frameRect
{
if (self = [super initWithFrame:(NSRect)frameRect]) {
self.side = aSide;
}
NSLog(#"Side is %#", self.side);
return self;
}
The output from NSLog() does show the correct value here. Now, the drawRect: method that is then triggered to draw the view, has a similar call:
- (void)drawRect:(NSRect)rect
{
NSLog(#"Side is %#", self.side);
And this time the NSLog() reports Side as nil! Stepping through it with the debugger, I see the value for side gets set, then it goes into the registers (all those hex codes that I don't understand), then when it jumps out again into the drawRect: method, the value is simply nil.
If anyone can shed light on this I'd appreciate it!
Cheers,
Aaron
This is usually the way to write a property for NSString*.
assign is used for primitive types (it doesn't increment the retain count for an object), and either retain or copy for Objects.
#property (readwrite, copy) NSString * side
I'm going to go ahead and guess the most common cause for this kind of bug: You're dealing with two different objects. You're initializing the view in your code and you also have an instance in a nib. The two instances are completely separate objects, so when the view that is visible performs its drawRect:, it doesn't see the other one's instance variable.
The problem with your property that other people are pointing out is also definitely true, but I suspect this bug is actually hiding that one.
In my .h file I have a NSMutableArray *locationsArray defined with a property as follows
#property (nonatomic, retain) NSMutableArray *locationsArray
In my .m file I was displaying a table view, this loaded fine until I tried to roll-up at which point I it crashed with bad access. This was due to the locationsArray not being retained.
This line of code fixed my problem in the .m
locationsArray = [[Locations loadLocations] retain] (#1)
This line of code also fixed the same problem
self.locationsArray = Locations.loadLocations (#2)
I have a couple of questions I need clarification on
is this the correct way to set this value, should I be doing an alloc init, alloc initwithArray?
Comming from a java world I understand self is this, or at least I thought I did...What is different in objective C that the locationsArray without the self is not being retained without me adding the retain.
Obviously I got it working but it took as while and am still a little confused as to why. any help would be appreciated.
Yes, if you set the instance variable (locationsArray) directly, you need to retain the value to keep it. You can either do that as you've done, or alloc/init it directly, which also means that you have a retain ownership of it.
self is indeed equivalent to this in other OO languages. But there's a semantic difference in Obj-C between referring to an ivar "naked" like in your first example, and referring to it as self.locationsArray in the second. The former behaves how you'd imagine, but the latter is actually a syntactic shortcut for [self setLocationsArray: ... ] which does the retain automatically for you, because you've marked the property as being retain.
This last point is really subtle, and (to my view) not expected or obvious behavior. If you're still fuzzy on it, go back to the Apple docs. Really important to get this.
In the book I'm studying from for iPhone dev, they utilize IBOutlet instances using the Interface Builder. An example would be a UIButton. So they add a thing in the struct like this:
IBOutlet UIButton *whateverButton;
Then they add a #property for each of these in the .h, and a #synthesize in the .m.
Then they include a release in the dealloc of the .m. Two questions:
Is the release necessary? Aren't all properties already handled automatically?
How can I check the ref count to see what's happening, for debug purposes...?
Is the release necessary? Aren't all
properties already handled
automatically?
If the property is retained, the release is necessary. When you declare a #property and #synthesize it, all you get is the accessors, there is no special automatic behaviour in dealloc.
Also, there is nothing magical about IBOutlet – it’s just a marker for Interface Builder to see which properties you would like to appear in IB. It’s simply an empty macro, Cmd-click the IBOutlet keyword to see its definition:
#ifndef IBOutlet
#define IBOutlet
#endif
Same thing goes for IBAction which expands to void.
How can I check the ref count to see
what's happening, for debug
purposes...?
When I need to debug memory management, I usually simply set up a breakpoint in the dealloc method or log a string there. It is also helpful to log the retainCount of an object around the calls that might do something fishy with it.
It might also help to see how the #synthesize directive creates the accessors. When you declare a retained #property and ask the compiler to #synthesize them, you get something like this:
#property(retain) NSString *foo;
#synthesize foo;
- (void) foo {
return foo;
}
- (void) setFoo: (NSString*) newFoo {
// Try to think what would happen if this condition wasn’t
// here and somebody called [anObject setFoo:anObject.foo].
if (newFoo == foo)
return;
[foo release];
foo = [newFoo retain];
}
This isn’t exactly the thing, but it’s close enough. Now it should be more clear why you should release in dealloc.
Properties are not "handled automatically." The closest that comes to being true is that synthesized accessors handle their memory management responsibilities properly. But that is just those accessors. Properties are just a way of declaring accessible "things" on your class. They don't get much special treatment beyond that. It doesn't turn on some sort of garbage collection. So yes, release is necessary.
And you should use the debugging tools like Instruments if you want to inspect a running app for leaks or memory that doesn't get released. I would not look at the ref count directly, because it's almost dangerously useless — there's no guarantee that the ref count will be what you expect at any point, and that doesn't necessarily indicate a problem.
You should read Apple's memory management rules for Cocoa. It's pretty simple once you've absorbed that. I wouldn't necessarily recommend reading other guides first, because subtle misstatements can lead you down the wrong path (for instance, the idea that properties will be released for you probably came from hearing somebody misstate how they work).
Is the release necessary? Aren't all
properties already handled
automatically?
It depends on how the property is implemented. If it is auto-implemented (#synthesize'd), the property will retain its value in the setter and release it if set to another value. If you just got into Obj-C and Cocoa, you should read about the conventions for memory management. I have put up a post on my blog about them, there are plenty of resources elsewhere too.
How can I check the ref count to see
what's happening, for debug
purposes...?
You can check the NSObject retainCount property. Information on that is here. For advanced debugging purposes, there is the NSZombieEnabled environment flag that will cause all release message to not decrement the reference count but log an error when an object that would have normally been released is accessed.
I have a controller with a delegate.
#interface MyConversionController : NSObject {
id <ConversionDelegate> _delegate;
}
#property (assign) id delegate;
#end
#implementation
#synthesize delegate = _delegate;
#end
I'm getting Unrecognized selector sent to instance 0x36c4a0 errors. I've set a breakpoint on the -(void)setDelegate(id)delegate method so I can observe objects that are passed into my MyConversionController class. My setDelegate method is called twice, the first time is an object at the address 0x36c4a0 that I know conforms to the <ConversionDelegate> protocol. The second time this method is called another object is passed in that also conforms to the protocol. When the time comes to start calling methods on the delegate the method calls are sent to the first object (0x36c4a0) which is now some other kind of object (usually a CFString or __NSFastEnumerationEnumerator if that makes a difference).
Does anyone know why this could be happening?
After running malloc_history I see that the first address, the one that's giving me trouble, is allocated and freed a number of times before I get to it. The second object is just allocated once. Under what conditions would the pointers be reused like this?
You might want to use malloc_history to find the callstack of the object at that address. Do the following in the terminal while your process is running:
malloc_history <pid> 0x36c4a0 # insert the address in question for the 2nd arg
You'll also need to enable MallocStackLogging (thanks to Kubi's comment below on this).
This may help you understand where the object at that address is being allocated.
Also, you've marked the delegate as assign, not retain, however, I think this is appropriate for delegates. That said, if it was autoreleased somewhere else, that memory may be being reused.
Are you possibly autoreleasing the delegate and assigning it? Something like:
delegate = [[[ConversionDelegateClass alloc] init] autorelease];
controller.delegate = delegate
If so, the delegate will be released in the next autopool release since nothing is retaining it and that memory location will be available for re-use.
The problem is that the the delegate was being prematurely deallocated. The reason this was so difficult to debug was that the deallocation occured in code I had written a long time ago, and the program would quit before the issues would occur. Writing the new sliver of code kept the program open for just long enough for other classes to start sending messages to the deallocated object.
Solution: I ran the code with the the Zombie module in Instruments. Wish I had done this a few days ago, I fixed the code within 30 seconds of looking at the output from Instruments.
I am really puzzled by this. I believe I am managing memory the correct way but executing the code suggests that I am double releasing the object. Here is the code and then I'll explain what is happening.
#protocol SomeDelegate <NSObject>
#required
- (id)initWithCols:(NSUInteger)Cols Rows:(NSUInteger)Rows;
#end
#interface SomeObject : NSObject <SomeDelegate> {
}
- (id)initWithCols:(NSUInteger)Cols Rows:(NSUInteger)Rows;
#end
#interface Layout : UIView {
id<SomeDelegate> someDelegate;
}
#property(retain) id<SomeDelegate> someDelegate;
- (id)initWithFrame:(CGRect)aRect Cols:(NSUInteger)Cols Rows:(NSUInteger)Rows;
#end
#implementation Layout
#synthesize someDelegate;
- (id)initWithFrame:(CGRect)aRect Cols:(NSUInteger)Cols Rows:(NSUInteger)Rows {
if(self = [super initWithFrame:aRect]) {
cols = Cols;
rows = Rows;
id<SomeDelegate> delegate = [[SomeObject alloc] initWithCols:cols Rows:rows];
[self setSomeDelegate:delegate];
//[delegate release];
}
return self;
}
-(void)dealloc {
[someDelegate release];
[super dealloc];
}
#end
Now when I uncomment out the "//[delegate release];" line in the constructor of the Layout class, then I get a "EXC_BAD_ACCESS" error and the application crashes when it attempts to dealloc. I have traced the crash to the release of the someDelegate object in the dealloc method of Layout class. If I leave it commented then the application works fine.
Can someone please explain why this is happening as it appears to be going against everything I have read about memory management in Objective-C.
Just a note that the code example actually works, however my code doesn't which follows the example. Could there be something inside of my actual SomeObject that is causing an autorelease?
Thanks in advance.
First, go back and reread the memory management rules just to make sure you are not missing anything obvious in your use of delegate elsewhere.
Next, turn on NSZombieEnabled (in your executable settings, Arguments panel, add an environment variable NSZombieEnabled set to YES).
Then add a dealloc method to your delagate if it does not have one already (make sure you call [super dealloc]!) and put a break point on there - that will tell you when your delagate is deallocated which will tell you when it is being released.
Alternatively, add trivial release/autorelease methods to your delegate class which do nothing but call through, and then breakpoint them and that will tell you exactly when it is being released.
Three final comments: in the standard naming convention for Objective C/Cocoa, you should have lowercase parameter fields, ie it should be:
- (id)initWithFrame:(CGRect)aRect cols:(NSUInteger)Cols rows:(NSUInteger)Rows;
When your ivar and property are named identically, it is very easy to accidently use the wrong one, so I recommend using a different ivar name and property name to avoid confusion, either use an _ prefix like Apple, or some other prefix to avoid confusion with Apple as well:
id<SomeDelegate> _someDelegate;
#synthesize someDelegate = _someDelegate;
And Apple recomends against using setters/getters in init/dealloc, so your init code should be:
_someDelegate = [[SomeObject alloc] initWithCols:cols Rows:rows];
As alluded to in the comments, the problem does not appear to be in the code posted.
I could ask for more information, but I'm firmly in the teach a man to fish camp....
A crash in -release will often be misleading in that various optimizations -- tail call optimizations, generally -- will make it look like the crash happened a frame or two above the actual call that crashed. When the crash happens, there isn't enough info on the stack to really identify the culprit.
Whenever you suspect you have any kind of a crash in -release or -dealloc, immediately turn on Zombies. This can be done through Instruments or via an environment variable or by calling a function in the Foundation very early in your program's execution.
Search for "Zombies" or "NSZombie" in the documentation included with the development environment (that'd be more of the "teach a man to fish" thing).
The problem was a MutableArray deep in a subclass that was created through a factory (autoreleased) but I was also releasing too. Unfortunately the crash wouldn't indicate which inherited dealloc was causing the crash and just stop on the first overridden dealloc.
The Zombie thing helped a little in that it told me an array was the culprit but not much else. I think there is more to NSZombie and requires more experience to take full advantage of it.