Rob
Rob

Reputation: 23

Need to release twice?

I can't seem to find the answer anywhere. I'm using Manual Memory Management in Objective-C developing for iOS.

I wrote a convenience function for getting UIColor from a hex string. In it, it returns

[[UIColor alloc] initWithRed:... alpha:alpha]

Apparently on certain platforms (we have a few devices, ranging iOS 8-9) the object would be destroyed on exiting the function, so that its returned UIColor* cannot be used. So now, we changed it to

[[[UIColor alloc] initWithRed:... alpha:alpha] retain]

My question is when I'm done using this object, do I have to release it twice? Once for the alloc, once for the retain? It seems very strange to me, and I can't find this online anywhere.

If I don't retain, it gets dealloc'd on exiting the function (on some platforms) making the function useless. If I do retain, I need to release twice when done?

EDIT:

https://developer.apple.com/library/ios/documentation/General/Conceptual/DevPedia-CocoaCore/MemoryManagement.html

"..., it is normally guaranteed to remain valid within the method or function it was received in. If you want it to remain valid beyond that scope, you should retain or copy it. "

So I'm not doing anything out of the ordinary. The docs say I "should retain it" if "I want it to remain valid beyond" the scope of a function. I will try what @FreeNickname suggested. That makes the most sense.

Upvotes: 0

Views: 594

Answers (3)

Amin Negm-Awad
Amin Negm-Awad

Reputation: 16650

You "misunderstood" Apple's documentation, because it is simply wrong for this topic. You really should read clang's documentation about ARC instead of Apple's, because clang's ARC documentation explains MRC correctly to interact with it.

Let's have a closer look on it:

You own any object you create by allocating memory for it or copying it.

Related methods: alloc, allocWithZone:, copy, copyWithZone:, mutableCopy, mutableCopyWithZone:

Conversely, if you are not the creator of an object and have not expressed an ownership interest, you must not release it.

If you receive an object from elsewhere in your program, it is normally guaranteed to remain valid within the method or function it was received in.

Taking this documentation for serious, you are not an owner of the object:

[[UIColor alloc] initWithRed:... alpha:alpha]

This is, because you do not receive the object reference from +alloc et al., but from -init…. Following Apple's documentation you are not an owner and have to retain it. (So it is "elsewhere".)

In clang's documentation it is described differently and correctly:

Methods in the init family implicitly consume their self parameter and return a retained object. (5.2.1)

Therefore there is a special method family for -init… along with the others mentioned in Apple's documentation as correctly described in clang's documentation:

The families and their added restrictions are:

  • alloc methods must return a retainable object pointer type. [Apple: alloc, allocWithZone:)

  • copy methods must return a retainable object pointer type. [Apple: copy, copyWithZone:)

  • mutableCopy methods must return a retainable object pointer type.(Apple: mutableCopy, mutableCopyWithZone:)

  • new methods must return a retainable object pointer type. (Apple: Ooops, I forgot something)

  • init methods must be instance methods and must return an Objective-C pointer type. … (Apple: Oooops, I forgot something)

(5.)

So, what you get from -init is already retained, you have the ownership and there is definitely no reason to retain it.

According to Rob's answer there might be a reason to autorelease it.

Upvotes: 0

Rob
Rob

Reputation: 437392

You said:

I wrote a convenience function for getting UIColor from a hex string. In it, it returns

[[UIColor alloc] initWithRed:... alpha:alpha]

According to the Basic Memory Management Rules, the proper memory management is dictated by the name of your method:

  • if your method name does not start with “alloc”, “new”, “copy”, or “mutableCopy”, then you should return an autorelease object:

    - (UIColor *)colorWithHexString:(NSString *)hexString {
        ...
        return [[[UIColor alloc] initWithRed:... alpha:alpha] autorelease];
    }
    
  • if your method name does start with “alloc”, “new”, “copy”, or “mutableCopy”, then you can return an object like you have above:

    - (UIColor *)newColorWithHexString:(NSString *)hexString {
        ...
        return [[UIColor alloc] initWithRed:... alpha:alpha];
    }
    

    Note, this pattern is less common than the above convention of colorWithHexString.

(Note, this memory management dictated by the method name prefix was historically merely best practice, but now, for interoperability with ARC code, it's critical. Always follow the above rules in manual reference counting code.)

Now, if the code that is calling your convenience initializer is allowing the object to be deallocated, the problem rests with that code, not your convenience initializer. Do not start adding extra retain statements to your initializer because something that calls it doesn't manage its memory properly.

Instead, make sure that the calling code does the proper retain (and eventual release) of the result of colorWithHexString, itself.

By the way, Xcode's static analyzer (shift+command+B) is remarkably good at analyzing manual reference counting code and identifying the issues.


In an edit to your question, you quoted the documentation:

If you receive an object from elsewhere in your program, it is normally guaranteed to remain valid within the method or function it was received in. If you want it to remain valid beyond that scope, you should retain or copy it. If you try to release an object that has already been deallocated, your program crashes.

This is not saying that your convenience initializer should issue a retain or copy. It is saying that the code that calls colorWithHexString is responsible for establishing its own claim of ownership of the UIColor object that was returned via retain or copy, as discussed above.

Upvotes: 2

DAXaholic
DAXaholic

Reputation: 35338

I think you are looking for the concept of autorelease which is used in situations like yours. It is essentially a way to send a deferred release message to the newly created object so the caller has the chance to retain it if necessary, otherwise it is destroyed when the autoreleasepool is processed.

Upvotes: 0

Related Questions