0

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.

Rob
  • 23
  • 3
  • 1
    Does it crash if you release it twice on those sertain platforms? I would try to use `[[[[UIColor alloc] initWithRed:... alpha:alpha] retain] autorelease]`, and treat the `UIColor` returned from the function as "already retained" (as it should be). But in this case you'd have to release it elsewhere, and it would be released twice. A strange situation really. – FreeNickname Jun 04 '16 at 15:31
  • 6
    "I'm using Manual Memory Management in Objective-C developing for iOS." I have to ask: why? There is no need, and you clearly have no idea what you're doing. Use ARC! The compiler understands memory management a whole better than you do. – matt Jun 04 '16 at 15:38
  • 3
    "Apparently on certain platforms ... the object would be destroyed on exiting the function, so that its returned `UIColor` cannot be used." No, in manual reference counting, the object instantiated with `alloc` will not be deallocated until paired with a corresponding `release` or `autorelease`. Don't randomly insert `retain` statements. If you have question about why you think you needed to add a `retain`, then let's focus on that problem rather than trying to manage what you must do if you insert random `retain` statements. – Rob Jun 04 '16 at 15:43
  • 4
    You really need to better explain under what specific circumstances the expected behavior isn't behaving as expected. An object created through `alloc/init` isn't just mysteriously deallocated for no reason only on certain platforms. There's more to this than what you've provided so far. Focus on the actual problem instead of adding more problems to it. – rmaddy Jun 04 '16 at 15:59
  • 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 seeing a unexpected behavior due to developer error - the docs corroborate what I see. And I get how to use autorelease but in this case I'm not trying to prevent a leak, I'm trying to get the opposite. – Rob Jun 04 '16 at 16:18
  • @matt. Every time I ask a question, I get told this. No I've been a C++ developer for a decade writing mission critical code. What is confusing is the black-box nature of iOS & objective C & lack of clear documentation. – Rob Jun 04 '16 at 16:24
  • @rmaddy - please see my edit. Apple docs also states an object may be destroyed upon exiting a function, and that a retain may be needed. My Q was - in this case I called both alloc and retain, so do I release twice later? – Rob Jun 04 '16 at 16:37
  • 1
    No, you do not need to retain it at all. The `alloc/init` has already set the retain count to 1. It can't be deallocated until the retain count goes to 0. It doesn't magically go to 0 simply be leaving the method assuming you are really using MRC. And your edit makes no attempt to do what I stated in my previous comment. – rmaddy Jun 04 '16 at 16:43
  • Offtopic - How do I reply to the proper "Rob"? – rmaddy Jun 04 '16 at 16:45
  • If your convenience function is short then adding it to the question should help others help you. Have you run Xcode's Analyze on the function? – CRD Jun 04 '16 at 17:22
  • 1
    OP: "Apparently on certain platforms (we have a few devices, ranging iOS 8-9) the object would be destroyed on exiting the function" Are you _actually seeing_ this behavior? That would be quite extraordinary. If so, please document it in the question. Otherwise, this does seem to be a misunderstanding of the relevance of `alloc` to memory management, and Rob's answer should set you straight. – jscs Jun 04 '16 at 18:43

3 Answers3

2

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.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
0

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.

DAXaholic
  • 33,312
  • 6
  • 76
  • 74
0

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.

Community
  • 1
  • 1
Amin Negm-Awad
  • 16,582
  • 3
  • 35
  • 50