Will An Associated Object Be Released Automatically? - objective-c

Note: This other question seems relevant but it's not: When does an associated object get released?
I'm adding a second description to a UIView instance as follows:
- (void) setSecondDescription:(UIView*)view description2:(NSString*)description2 {
objc_setAssociatedObject (view,&key,description2,OBJC_ASSOCIATION_RETAIN);
}
- (NSString*) secondDescription:(UIView*)view {
return (id)objc_getAssociatedObject(view, &key);
}
If the UIView deallocs, will the associated description 2 get dealloced? Is there any way to get this to happen automatically?

If you want to actually see the description of the entire dealloc timeline, look at WWDC 2011, Session 322, 36:22. However, here's the basic rundown (I wanted to remember it, so this is an actual comment in a piece of my code).
Note, that the associated objects are released at the end of the life cycle.
// General Information
// We take advantage of the documented Deallocation Timeline (WWDC 2011, Session 322, 36:22).
// 1. -release to zero
// * Object is now deallocating and will die.
// * New __weak references are not allowed, and will get nil.
// * [self dealloc] is called
// 2. Subclass -dealloc
// * bottom-most subclass -dealloc is called
// * Non-ARC code manually releases iVars
// * Walk the super-class chain calling -dealloc
// 3. NSObject -dealloc
// * Simply calls the ObjC runtime object_dispose()
// 4. object_dispose()
// * Call destructors for C++ iVars
// * Call -release for ARC iVars
// * Erase associated references
// * Erase __weak references
// * Call free()

Yes. When an object is dealloc'd, any associated objects (that use the RETAIN or COPY association types) are automatically released.

In short, yes - when the owning object is released then retained associated objects are released. See the first section of Apple's documentation

Section 4 in Jody Hagins' answer says "Erase associated references", which doesn't explicitly imply that the references are released. So I used the following piece of code (note WITHOUT ARC) to test this.
#interface AssociatedObjectHelper : NSObject
#end
#implementation AssociatedObjectHelper
- (void) dealloc
{
NSLog(#"In %s", __FUNCTION__);
[super dealloc];
}
#end
#implementation AppDelegate
...
- (void) testReleaseAssociatedObject
{
static const NSString *key = #"testKey123";
NSObject *ob = [NSObject new];
AssociatedObjectHelper *assocOb = [AssociatedObjectHelper new];
objc_setAssociatedObject(ob, key, assocOb, OBJC_ASSOCIATION_RETAIN);
[assocOb release];
[ob release];
}
Invoking above code does indeed end up calling -[AssociatedObjectHelper dealloc], with the following stack-trace:
#0 0x000000010000528f in -[AssociatedObjectHelper dealloc]
#1 0x00007fff8a0bb89c in objc_object::sidetable_release(bool) ()
#2 0x00007fff8a0a537f in _object_remove_assocations ()
#3 0x00007fff8a0a1644 in objc_destructInstance ()
#4 0x00007fff8a0a1595 in object_dispose ()
#5 0x00007fff8a0bb89c in objc_object::sidetable_release(bool) ()
#6 0x000000010000e9b6 in -[AppDelegate testReleaseAssociatedObject]
Tested on Xcode 7.0.1

Related

Calling getter for dispatch_queue_t property causes crash

I have a private serial queue declared as a property and I am running into a very strange situation.
If I dispatch_async the property, it will crash (EXC_BAD_ACCESS (code=EXC_i386_GPFLT)). After some debugging, I found out that it's because the getter is called. If getter is not called, the crash won't happen. Also, it always crashes the second time self.queue is called. See second example below.
It's as though the first synthesized getter call has somehow caused the ivar to be over-released.
This is targeting iOS 9 and above so I am not checking OS_OBJECT_USE_OBJC.
Example 1) This doesn't work:
#interface Test ()
#property (nonatomic, strong) dispatch_queue_t initQueue;
#end
- (instancetype)init {
self = [super init];
if (self) {
_initQueue = dispatch_queue_create("com.test.initQueue", DISPATCH_QUEUE_SERIAL);
}
return self;
}
- (void)onCompletion:(void (^)())completion {
// Crashes here - EXC_BAD_ACCESS (code=EXC_i386_GPFLT)
// the second time self.queue is accessed - either by subsequent call into
// this method, or by adding NSLog(#"%#", self.queue) before this line.
dispatch_async(self.initQueue, ^{
...
});
}
Example 2) This also doesn't work:
#interface Test ()
#property (nonatomic, strong) dispatch_queue_t initQueue;
#end
- (instancetype)init {
self = [super init];
if (self) {
_initQueue = dispatch_queue_create("com.test.initQueue", DISPATCH_QUEUE_SERIAL);
}
return self;
}
- (void)onCompletion:(void (^)())completion {
NSLog(#"%#", self.initQueue);
// Crashes below - EXC_BAD_INSTRUCTION (code=EXC_i386_INVOP, subcode=0x0)
NSLog(#"%#", self.initQueue);
}
Example 3) It works if I stay away from using the getter:
#interface Test ()
#property (nonatomic, strong) dispatch_queue_t initQueue;
#end
- (instancetype)init {
self = [super init];
if (self) {
_initQueue = dispatch_queue_create("com.test.initQueue", DISPATCH_QUEUE_SERIAL);
}
return self;
}
- (void)onCompletion:(void (^)())completion {
// Works fine
dispatch_async(_initQueue, ^{
...
});
}
Example 4) It also works if I supply the getter:
#interface Test ()
#property (nonatomic, strong) dispatch_queue_t initQueue;
#end
- (instancetype)init {
self = [super init];
if (self) {
_initQueue = dispatch_queue_create("com.test.initQueue", DISPATCH_QUEUE_SERIAL);
}
return self;
}
- (dispatch_queue_t)initQueue {
return _initQueue;
}
- (void)onCompletion:(void (^)())completion {
// Works fine
dispatch_async(self.initQueue, ^{
...
});
}
Example 5) It will also work if I use ivar for queue instead of property or self.initQueue is assigned the main queue instead.
What's the reason for this behavior?
Other open source library is using property for dispatch_queue_t along with the getter and they have no issue at all. Example: https://github.com/rs/SDWebImage/blob/7e0964f8d90dcd80d535c52dd9f6d5fa7432052b/SDWebImage/SDImageCache.m#L57
Per your comments you originally named the property initQueue, this in turn created a method called initQueue which fell afoul of the ARC Method family rules. Those rules indicate that ARC will automatically annotate any method beginning with new or init as NS_RETURNS_RETAINED.
Methods in the init family implicitly consume their self parameter and return a retained object. Neither of these properties can be altered through attributes.
This in turn means that callers of the method are supposed to be safe to assume that they are taking ownership of the returned value and do not need to increment the retain value. As a result when you attempted to use the property ARC did not increase the reference count as was expected but ARC still left a release call at the end of the method. This resulted in your property value being released prior to when your class was dealloced.
It is possible to override this behavior using attributes in some cases. However I would suggest just being aware of method families, as they can have a nice performance impact on your application particularly for factory methods.
Other pitfalls to be aware of:
Methods in the alloc, copy, mutableCopy, and new families — that is, methods in all the currently-defined families except init — implicitly return a retained object as if they were annotated with the ns_returns_retained attribute. This can be overridden by annotating the method with either of the ns_returns_autoreleased or ns_returns_not_retained attributes.
A side note on this as well:
It is undefined behavior for a program to cause two or more calls to init methods on the same object, except that each init method invocation may perform at most one delegate init call.
Sadly the compiler doesn't seem to warn about that one.

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

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.

Does dispatching to a queue that's owned by an object retain that object? [duplicate]

This question already has an answer here:
Is self retained within this Objective-C block?
(1 answer)
Closed 9 years ago.
Suppose I have a simple call dispatch_async(self.queue, ^{ /* Empty */ }), where self.queue is a queue created previously.
Does self get retained by the Block in this case, given that there is no self reference inside the block, but only as a parameter for dispatch_async()?
Ok, so apple docs states that
The queue is retained by the system until the block has run to
completion.
So the queue will be retained by the system until the block completes the execution, but the self won't be retained in this case.
Thanks to #Wain for pointing out my mistake in the previous version of this answer.
No, self will not get retained if the block has neither (a) any explicit references to self; nor (b) any implicit references to self that are generated by referencing any instance variables of self. The presence of self.queue in the invocation of dispatch_async will not cause it to be retained. It's what inside the block that matters.
This is quite easy to demonstrate. Imagine a view controller whose implementation looks like:
#interface SecondViewController ()
#property (nonatomic, strong) dispatch_queue_t queue;
#end
#implementation SecondViewController
- (void)dealloc
{
NSLog(#"%s", __FUNCTION__);
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.queue = dispatch_queue_create("com.stackoverflow.17306228", 0);
void (^block)(void) = ^(void){
sleep(10);
NSLog(#"%s", __FUNCTION__);
};
dispatch_async(self.queue, block);
}
#end
If you push to this view controller's scene, and then immediately press the "back" button to pop it off, you'll see the view controller immediately be deallocated, and the block will continue to execute. But if you add a class instance variable or property to the block, you'll see the view controller retained until after the block completes.

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.

Class Instance Release Order?

This is probably a pretty basic question, but just something that I wanted to make sure I had right in my head. When I release the class instance "newPlanet_001" what is the order of disposal, Am I right in assuming that if the retain count of the object is 1 prior to the release that the instances dealloc method is called first (to release the instance variable "planetName") before continuing to release the class instance as a whole?
(i.e.)
// CLASS
#interface PlanetClass : NSObject {
NSString *planetName;
}
- (NSString *)planetName;
- (void)setPlanetName:(NSString *)newPlanetName;
#end
// MAIN
int main (int argc, const char *argv[]) {
PlanetClass *newPlanet_001;
newPlanet_001 = [[PlanetClass alloc] init];
[newPlanet release];
}
// DEALLOC
- (void)dealloc {
[planetName release];
[super dealloc];
}
#end
cheers -gary-
-[NSObject release] calls -dealloc if the retain count is zero. This allows the object to cleanup any objects it owns before calling [super dealloc] to do the actual deallocation.
If implemented properly, an object will release any objects it owns before calling the super (this them to get deallocated if their retain count is also zero).
An object owns another if it calls alloc, copy or retain on it.
Your assumption is correct. The planetName object is released BEFORE the newPlanet_001 instance.