In Objective-C under MRC if an object gets de-alloced do objects it created get de-allocated as well? - objective-c

This may look like a dup of this, but I don't think that answers my question as it is about associated objects, not objects that were created by and whose only pointer resides within an object.
Let's say I had this example in MRC mode.
// In h file
#interface MyViewController : UIViewController {
NSObject* myNsObject;
}
// In m file
-(void) viewDidLoad() {
myNsObject = [[NSObject alloc] init]; // I'm never going to release myNsObject
}
I'm smart enough to release myViewController correctly. It's reference count goes to zero and it is de-allocated. But I never released myNsObject, so it had been hanging around with a reference count of 1. So would a release, and therefore de-alloc, automatically get done on myNsObject? Or would myNsObject get leaked in that case?

The proper memory management here is to release myNsObject in the dealloc method of the view controller:
- (void)dealloc {
[myNsObject release];
[super dealloc];
}
If you create something then you are responsible for releasing it (under MRC).
Failure to do this results in memory leaks.

Related

Weird memory issues, with ARC enabled

I am having a very, very strange error, probably related to memory management (even though I'm using ARC).
I have a my AppDelegate, Foo, and SubFoo (which is a subclass of Foo).
Foo.h
#protocol FooDelegate <NSObject>
- (void)didReceiveDownloadRequest:(NSURLRequest *)downloadRequest;
#end
#interface Foo : NSObject {
__weak id <FooDelegate> delegate;
}
- (void)performRequest;
#property (nonatomic, weak) id <FooDelegate> delegate;
#property (nonatomic, retain) NSString *fileIdentifier;
Foo.m
#implementation Foo
#synthesize delegate, fileIdentifier;
- (id)init {
if ((self = [super init])) {
self.delegate = nil; // I tried leaving this line out, same result.
NSLog(#"I am %p.", self);
}
return self;
}
- (void)performRequest {
// Bah.
}
#end
SubFoo.h
#interface SubFoo : Foo {
WebView *aWebView;
}
SubFoo.m
- (void)performRequest {
if (self.fileIdentifier) {
aWebView = [[WebView alloc] init];
[aWebView setFrameLoadDelegate:self];
[[aWebView mainFrame] loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"theURL"]];
}
}
- (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
NSLog(#"Finished loading.");
// ...
NSLog(#"Class Name: %#", NSStringFromClass([self class]));
NSLog(#"Memory Location of delegate: %p", self.delegate);
// ...
}
Sometimes, the class name on webView:didFinishLoadForFrame: returns a completely different class (instead of SubFoo, it returns random classes, like NSSet, NSArray, it even sometimes returns CFXPreferencesSearchListSource), other times it just crashes there with an EXC_BAD_ACCESS, and when it returns a random class on Class Name: it returns that [randomClassName delegate] is an unrecognized selector.
EDIT: When self gets set to another thing, it gets set RIGHT on webView:didFinishLoadForFrame:, and on performRequest it is ALWAYS SubFoo.
Any help here would be appreciated.
First, even though you are using ARC zeroing weak references in your project (#property (weak)), other projects and frameworks may not be (and are probably not) using zeroing weak references.
In other words, assume that all delegates in frameworks are __unsafe_unretained unless:
The delegate property is declared weak in a header
The documentation/header explicitly states otherwise
That said, let's talk about your example. Your object ownership chart looks something like this:
(Note: I'm not entirely sure which class in your project uses SubFoo. Based on common practice, I'm assuming that you have a class with a strong reference to SubFoo, and that class is also set up to be a SubFooDelegate)
Ultimately, your instance of SubFoo is losing its last strong reference and is deallocating. In a perfect ARC-enabled world, the WebView's pointer to SubFoo would nil out at this time. However, it's not a perfect world yet, and WebView's frameLoadDelegate is __unsafe_unretained. Due to run loop interaction, the WebView is outliving SubFoo. The web request completes, and a dead pointer is dereferenced.
To fix this, you need to call [aWebView setFrameLoadDelegate:nil]; in SubFoo's dealloc method. You also need to call it when you reassign aWebView, as you are losing track of the old aWebView:
SubFoo.m
#implementation SubFoo
- (void)dealloc {
[aWebView setFrameLoadDelegate:nil];
// Also nil out any other unsafe-unretained references
}
- (void)performRequest {
if (self.fileIdentifier) {
[aWebView setFrameLoadDelegate:nil]; // Protects us if performRequest is called twice. Is a no-op if aWebView is nil
aWebView = [[WebView alloc] init];
[aWebView setFrameLoadDelegate:self];
[[aWebView mainFrame] loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"theURL"]];
}
}
- (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
// ...
}
Forget the self.delegate error for now, it is a red herring if [self class] is producing the wrong result! Your results suggest you are somehow clobbering self.
Breakpoint on webView:didFinishLoadForFrame: check the self value and step through.
Comment Followup
For self to be wrong on the first statement of an instance method is, let's say, unusual (but not impossible).
It is important when an object is set as another's delegate that you make sure the delegate object's lifetime is at least as long as the one it is acting as a delegate to. Introducing ARC can make previously working code fail as it may release the delegate earlier than the code did under MRC. When this happens the call to the delegate usually fails.
However your error does not fail on the call to the delegate; the call starts - you end up in webView:didFinishLoadForFrame: - and then you find self is invalid. To actually invoke an instance method usually requires a valid value for self as it is used to determine the method implementation to call. Hence it is usual for self to be valid at the start of a method!
But note the "usually"...
So despite you having successfully reach your method, your error might be down to not having a strong reference to your SubFoo instance, you pass it as a delegate to aWebView, and by the time webView:didFinishLoadForFrame: is called your SubFoo has gone.
Make sure you're keeping a strong ref to your SubFoo instance. If you just want to test (this is not a recommended general solution!) if this is your problem you can just assign it to a local static (static SubFoo *holdMe say declared inside performRequest) in performRequest, which will keep a strong reference around at least until the next call to performRequest. If this does prove to be the problem you then need to come up with a good way to maintain the reference that fits your design.
Here's the real problem: You're creating a SubFoo object within the context of a method. So after the method completes, SubFoo is being released (before its WebView has time to load).
To fix this, you'll need to assign the SubFoo object you're creating to something persistent, like a instance variable of the class you're creating it from. That way the object will persist beyond the scope of the method it was created in and all will work as expected.
As CRD mentioned, I would say an incorrect object/bad access returned is a sign of an object being released. Sometimes it's replaced by another object, sometimes it's not so you get the bad access exception. Regarding how this could happen to self, I would imagine that this is a concurrency weird case (object is being freed on another thread).
The best way to confirm this is to run your code in Instrument's NSZombie template, it'll show you as soon as you access a freed object. It also shows when it's been retained/released so you don't have to guess.
Regarding your above comment.
SubFoo *theClass = [[SubFoo alloc] init];
You must store theClass in a
#property (strong) SubFoo *mySubFoo;
If you declare it as such:
{
SubFoo *theClass = [[SubFoo alloc] init];
}
It gets released at the closing bracket. This part of the point of ARC when that variable moves out of scope, it gets released. If you want to let it float in the ether you could use
{
__weak SubFoo *theClass = [[SubFoo alloc] init];
}
and it won't get released, but this will lead to a memory leak unless you carefully manage all the weak references. In the case of it not being released at -performRequest I'm assuming the request looks like this:
{
SubFoo *theClass = [[SubFoo alloc] init];
[theClass performRequest];
}
wheras -webView:didFinishLoadForFrame: is called at some indiscriminate time in the future.

Objective-C associated objects leaking under ARC

I have encountered with a strange objc_setAssociatedObject behavior under ARC. Consider the following code:
static char ASSOC_KEY;
#interface DeallocTester : NSObject
#end
#implementation DeallocTester
- (void) dealloc
{
NSLog(#"DeallocTester deallocated");
//objc_setAssociatedObject(self, &ASSOC_KEY, nil, OBJC_ASSOCIATION_RETAIN);
}
#end
#implementation AppDelegate
- (void) applicationDidFinishLaunching:(UIApplication *)application
{
NSObject *test = [[DeallocTester alloc] init];
objc_setAssociatedObject(test, &ASSOC_KEY, [[DeallocTester alloc] init],
OBJC_ASSOCIATION_RETAIN);
}
I'm creating an instance of DeallocTester, then I set another DeallocTester as an associated object for it, then both of them go out of scope.
I expect the -dealloc of the first object to be called, then the associated object to be deallocated too, but I see the "DeallocTester deallocated" message printed only once. If I uncomment the objc_setAssociatedObject line in -dealloc, the second object gets deallocated too.
The Objective-C reference states that associated objects are deallocated automatically upon object destruction. Is it a compiler/ARC/whatever issue or am I missing something?
Update
This sample code is actually working if you run it from a brand-new project. But I have two ARC-enabled projects where it doesn't. I'll do some investigation and provide a better sample.
Update 2
I've filled a rdar://10636309, Associated objects leaking if NSZombie objects enabled in ARC-enabled project
I've found a source of a problem - I had NSZombie objects enabled in both my projects where this bug appears.
As far as I understand, when zombie objects are enabled, the normal instances are replaced with NSZombie upon deallocation, but all the associated objects are left alive! Beware of that behavior!
I've created a rdar://10636309
Update: There's a workaround by Cédric Luthi, and this issue appears to be fixed in iOS 6.
The code you posted works exactly as advertised under ARC. I rewrote your dealloc implementation to help make things a little more obvious.
- (void)dealloc
{
NSLog(#"deallocating %#", self);
}
Here's the resulting log:
2012-01-03 06:49:39.754 ARC Stuff[47819:10103] deallocating <DeallocTester: 0x6878800>
2012-01-03 06:49:39.756 ARC Stuff[47819:10103] deallocating <DeallocTester: 0x688b630>
Are you sure you're compiling with ARC enabled?

Strange memory only when using copy

I have a class named SomeClass. in its init I have a lot of lines like:
SomeProperty_ = [[SomeObject alloc] initWithSomething];
While the properties are declared as
#property(retain) SomeObject *SomeProperty;
and defined as
#synthesize SomeProperty = SomeProperty_;
When I allocate objects of SomeClass and later release them, everything works fine and there are no memory leaks. However, when I copy an object of SomeClass and later release it, all the lines like
SomeProperty_ = [[SomeObject alloc] initWithSomething];
are marked as a memory leak in Instruments. this is also correct as I get memory warning and later crash if I use this a lot.
However if I make a method named dealloc like:
-(void) dealloc
{
self.SomeProperty = nil;
[super dealloc];
}
Everything is fine with copies as well and no memory warning or leaks.
I think this is because of my copy implementation:
-(id)copy
{
SomeClass *copy = [[SomeClass alloc] init];
copy.SomeProperty.somePOD = self.SomeProperty.somePOD;
return copy;
}
Where is the problem? what can I do to resolve it without the custom dealloc?
The first things I can think of is:
How your somePOD is set as far as #property(???)
And when you say :
"However, when I copy an object of SomeClass and later release it, all the lines like
SomeProperty_ = [[SomeObject alloc] initWithSomething];
are marked as a memory leak in Instruments. this is also correct as I get memory warning and later crash if I use this a lot."
You are referring to call made in your init method?
Because if your not, you are bypassing a setter and the object that was in that variable before this assignment will leak.
You must either use a custom dealloc method or start using Automatic Reference Counting (ARC).
When you call [[SomeObject alloc] init...], you get ownership of the new object, so you must release the object when you are done with it. Setting self.SomeProperty to nil does the release because you declare the property with the retain attribute.
You do this in your dealloc method if you want to own the SomeObject object until your SomeClass object dies. If you use ARC, it will generate the dealloc method for you.

Why the Objective C method still can be invoked when object was released?

Class definition:
#import "MyClass.h"
#implementation MyClass
- (id)init
{
self = [super init];
if (self) {
// Initialization code here.
}
return self;
}
- (void)dealloc
{
[super dealloc];
}
- (void)print
{
NSLog(#"Hello World");
}
#end
And main file:
MyClass * m = [[MyClass alloc]init];
[m print];
[m release];
[m print];
Result:
Hello World
Hello World
Why the 2nd method still invoked when object is released?
Releasing an object simply marks the memory it used as being available to be reused for other purposes. It doesn't overwrite it with zeroes or anything like that. In your example, you haven't created any new objects, so nothing has had a chance to reuse the memory formerly known as "m".
This why it's a common pattern to release an object and assign nil to the pointer, to prevent accidental re-use of an invalid object:
[m release]
m = nil;
That's because the memory for that object still is in place and hasn't been overwritten with garbage yet. Also, the method in question doesn't rely on any other (released) instance variable. In short: it's just pure chance that it worked.
Try setting the environment variable NSZombieEnabled and running that again to see that you really just had "luck".
Accessing freed memory is undefined behavior. When you're invoking undefined behavior, anything can happen. This falls under the heading of "anything," so it's a reasonable thing to have happen — so is accessing the wrong object or just crashing, which are also likely outcomes.

Should I release this property?

I'm a objective c newbie, and i'm having a bit of problems with memory management, I've read the apple's memory management policies, however i need a bit of clarification here, this is pretty simple i guess, but i would like to ask you if I'm right:
Given this property:
#interface Test : NSObject {
NSArray *property1;
}
#property (nonatomic,retain) NSArray* property1;
#end
...
//And its implementation:
#implementation Test
#synthetize property1;
-(id) init {
if (self=[super init]) {
self.property1=[[[NSArray alloc] initWithCapacity:5] autorelease];
}
return self;
}
-(void) dealloc {
[super dealloc];
[property1 release];
}
#end
Is it right to issue an Autorelease message to the allocated object in the init method?, i do this cause in apple's document, says that every allocated object should be released by the developer, then, I think, alloc sets retain count to 1, then the property (nonatomic, retain) adds 1, so retain==2, then autorelease substracts 1, and when the dealloc method is called, property1 is released and retain count==0, am I right?
You have your memory management right, though Apple (and a lot of other people) generally recommend not using accessors in your initialization methods because accessors can have side effects beyond simply setting an instance variable that your class might not be set up to handle yet. And in that case, you wouldn't want to autorelease since you'd want ownership of the object.
one side note: in your dealloc, you need to release the property before calling [super dealloc], because [super dealloc] eventually deallocates the memory of the object, which includes the memory containing the property1 variable, so it is invalid to refer to that variable after you call [super dealloc]. It should be:
-(void) dealloc {
[property1 release];
[super dealloc];
}
One of the nice things about using properties is that you can encapsulate all of your "releasing" behavior regardless of whether your property is set to retain, copy, assign, or whatever by just doing this:
self.property1 = nil;
Personally I've gotten in the habit of setting all properties to nil (using self.property, not just accessing the member variable directly) in dealloc so that even if I change how the memory management works for the member variable it works correctly.