Reputation: 23
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:
"..., 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
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
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
orcopy
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
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