Reputation: 1683
I implemented the old init-as-a-factory pattern, but in one particular case (but not others!) I get a warning from the analyser regarding memory leaks. And indeed, looking at the Cocoa Memory Management Policy rules, it is alloc
, not init
, which can return +1-retain-count objects.
So it appears that:
self
and returning a new object from init
is, strictly speaking, against the rules.So... have we been doing this wrong all along?
Upvotes: 3
Views: 900
Reputation: 108169
Without knowing what is the code that is causing the analyzer's behavior it's hard to tell, but as a general rule, here's a couple of compiler-friendly ways to define init/factory methods.
- (instancetype)initWithParameter:(id)parameter {
if(self = [super init]) {
_parameter = parameter;
}
return self;
}
MyCustomClass * myInstance = [[MyCustomClass alloc] initWithParameter:foo];
This will produce an instance with a +1 retain count. Under ARC this will be automatically managed properly since it follows the NARC rule (New, Alloc, Retain, Copy). For the same reason, in pre-ARC environments it has to be explicitly released by the client.
+ (instancetype)canIHazInstanceWithParameter:(id)parameter {
return [[self alloc] initWithParameter:parameter]; // assuming -initWithParameter: defined
}
+ (instancetype)canIHazInstanceWithParameter:(id)parameter {
return [[[self alloc] initWithParameter:parameter] autorelease]; // assuming -initWithParameter: defined
}
MyCustomClass * myInstance = [MyCustomClass canIHazInstanceWithParameter:foo];
Both in ARC and pre-ARC the method returns an autoreleased instance (this is clearly more explicit in the pre-ARC implementation), which doesn't have to be managed by the client.
You may have noticed the instancetype
keyword. That's a handy language extension introduced by Clang, that turns the compiler into a dear friend when implementing your own constructors/factory methods. I wrote an article on the subject, which may be relevant to you.
Whether factory methods are preferable to init
methods is debatable. From a client perspective it does not make much difference under ARC, provided that you carefully follow the naming conventions, even though I personally tend to expose factory methods in the interface, while implementing custom init
methods only internally (as I did in the examples above). It's more a matter of style than an actual practical concern.
Upvotes: 0
Reputation: 46608
you can implemented init
like this, which should release self
to balance the retain count from alloc
call.
- (id)initWithSomething:(id)something
{
[self release]; // don't need this line for ARC
self = nil;
return [[PrivateSubClass alloc] initWithSomething:something];
}
and it if very often to implement init
as a factory method. e.g. NSArray
, NSDictionary
, NSString
Upvotes: 2
Reputation: 10860
As gaige said, it will be much more clearer if you post a piece of code rather than explanations.
Anyway, you can move your factory to the class method, so you will have no such problem at all. I mean something like this:
MyClass* instance = [MyClass instanceWithParameters:params];
@interface MyClass
+ (MyClass*) instanceWithParameters:(ParamType)params;
@end
Upvotes: 0