JackyJohnson
JackyJohnson

Reputation: 3126

Releasing a C type declared as an Objective-C property

I have declared a C type as a property of my Objective-C object. I would like to make sure that my property is getting garbage collected properly by ARC.

Just iterating a bunch of times doesn't leak memory, but since I haven't found proper documentation on the subject I would like to confirm.

Basically, when I override dealloc to free my C type, I get an error my variable was never allocated:

malloc: *** error for object: pointer being freed was not allocated ***
set a breakpoint in malloc_error_break to debug

@interface MyClass : NSObject
    @property (readonly, assign) xmlDocPtr doc;
@end

@implementation MyClass
    - (void)dealloc {
       if (_doc != NULL) {
          xmlFreeDoc(_doc);  // throwing da runtime error, see above!
       }
       NSLog(@"dealloc'ed!"); // call me please T___T
    }

    // Setting my C-type property here:
    - (instancetype)initWithFileAtPath:(NSString *)path {
       NSData *data = [[NSFileManager defaultManager] contentsAtPath:path];
       [self setDoc:htmlReadMemory([data bytes], (int)[data length], "", NULL, HTML_PARSE_NOWARNING | HTML_PARSE_NOERROR)];

       if (_doc == NULL) return nil;

       NSLog(@"init'ed!");  // gets called!
       return self;
    }

    // Property setter (as suggested by DarkDust):
    - (void)setDoc:(xmlDocPtr)doc {
       if (_doc != NULL) xmlFreeDoc(_doc);
       _doc = doc;
    }
@end

Upvotes: 1

Views: 79

Answers (2)

DarkDust
DarkDust

Reputation: 92316

Now for your runtime error you've cited after updating the question: the error message tells you that someone is trying to free an address that was not the return value of malloc. Most likely, the value assigned to _doc is not valid pointer, or at least not a pointer to the beginning of a block of memory that was returned by malloc. That in turn means the error must be somewhere in your htmlReadMemory function: it doesn't return a valid xmlDocPtr.

Upvotes: 0

DarkDust
DarkDust

Reputation: 92316

Since ARC can only operate on Objective-C objects, it cannot help you with managing C types.

A vital question to correctly managing memory always is "who 'owns' the object?" In Objective-C, that's done using reference counting and clear naming rules to aid that. Since you don't have that with the C types, you need to be careful.

So, who is creating your xmlDoc instance? If it's the same object for which you've written the dealloc method (let's call that class Foo), and you just want to expose the instance via the property, the ownership is clear: Foo is the owner of instance and thus needs to clean it up. You're already doing that in dealloc in this case (though you have a bug there; the variable itself always has an address, so &_doc will never be NULL!). I would make the property read-only to avoid anyone messing with the pointer from the outside (to assign it, you'd then need to access the backing variable _doc directly). It would thus look like this:

@property(readonly, assign) xmlDocPtr doc;

...
_doc = CreateTheXMLDocInstance();
...

- (void)dealloc {
    if (_doc != NULL) xmlFreeDoc(_doc);
}

If you actually want to make the property writable, you need to correctly handle the setter. Again, the question is: who owns the instance? If you want the Foo object to be the sole owner, things are actually pretty easy. You'll just need to write a custom setter to deallocate old instances:

- (void)setDoc:(xmlDocPtr)doc {
    // Do not free if it's the same pointer.
    if (doc == _doc) return;
    // Remove old instance, if we have one.
    if (_doc != NULL) xmlFreeDoc(_doc);
    // Assign new instance.
    _doc = doc;
}

If the Foo object would not be the sole owner it would not be allowed to deallocate _doc in the setter nor in dealloc.

Upvotes: 2

Related Questions