3

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:

  1. Releasing self and returning a new object from init is, strictly speaking, against the rules.
  2. Many places on the internet promote this technique, and because of the tandem nature of alloc/init this does work.
  3. The analyser sometimes complains about this, and sometimes doesn't.

So... have we been doing this wrong all along?

Vladimir Gritsenko
  • 1,669
  • 11
  • 25
  • 3
    Can you post a snippet that causes the compiler error? It's a common pattern, as you point out, and it is how most class clusters are implemented. Further, under circumstances when init can fail, the pre-arc method for dealing with failure was to dealloc self and return nil. – gaige Jun 09 '13 at 13:11
  • *Class clusters are based on the Abstract Factory design pattern.* [Concepts in Objective-C Programming: Class Clusters](http://developer.apple.com/library/ios/#documentation/general/conceptual/CocoaEncyclopedia/ClassClusters/ClassClusters.html) – vikingosegundo Jun 10 '13 at 00:51

3 Answers3

2

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

Bryan Chen
  • 45,816
  • 18
  • 112
  • 143
0

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
Morion
  • 10,495
  • 1
  • 24
  • 33
  • 1
    **static** should be renamed as **class**. – Lorenzo B Jun 09 '13 at 14:06
  • If I understand the OP correctly, your code should return `MyClass *`. And its name must begin with `new` or `alloc` so that ARC knows it returns an object with +1 retain count. – Alexei Sholik Jun 09 '13 at 14:32
  • And `MyClass*` would be better ;) – Lorenzo B Jun 09 '13 at 14:54
  • 3
    Class factory methods do not start with new or alloc. They normally start with their class name. For instance, stringWithFormat: in NSString, or numberWithBool: in NSNumber. – e1985 Jun 09 '13 at 14:56
  • yeah, just after several months of using c# instead of objective-c forgot to add * – Morion Jun 09 '13 at 22:57
0

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.

Classic alloc/init

- (instancetype)initWithParameter:(id)parameter {
    if(self = [super init]) {
       _parameter = parameter; 
    }
    return self;
}

Usage

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.

Custom factory method

ARC

+ (instancetype)canIHazInstanceWithParameter:(id)parameter {
    return [[self alloc] initWithParameter:parameter]; // assuming -initWithParameter: defined
}

Pre-ARC

+ (instancetype)canIHazInstanceWithParameter:(id)parameter {
    return [[[self alloc] initWithParameter:parameter] autorelease]; // assuming -initWithParameter: defined
}

Usage

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.

Remarks

  • 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.

Gabriele Petronella
  • 106,943
  • 21
  • 217
  • 235