19

I'm trying to add a convenience constructor to my custom object. Similar to [NSArray arrayWithArray:]

I know it involves a class method that returns an auto released object. I've been googling around but all I can seem to find is the definition of a convenience constructor but not how to write one.

Christian Schlensker
  • 21,708
  • 19
  • 73
  • 121

2 Answers2

32

Let's say you have the following:

@class PotatoPeeler : NSObject
- (instancetype)initWithWidget: (Widget *)w;
@end

Then to add a factory method, you'd change it to this:

@class PotatoPeeler : NSObject
+ (instancetype)potatoPeelerWithWidget: (Widget *)w;
- (instancetype)initWithWidget: (Widget *)w;
@end

And your implementation would simply be:

+ (instancetype)potatoPeelerWithWidget: (Widget *)w {
    return [[[self alloc] initWithWidget: w] autorelease];
}

Edit: replaced id with instancetype. They are functionally identical, but the latter provides better hints to the compiler about the method's return type.

Jonathan Grynspan
  • 43,286
  • 8
  • 74
  • 104
  • 12
    +1 note the use `self` instead of the hard-coded class name to alloc-init the instance in order to **handle subclassing properly** (`self` here refers to the class object itself). Also note the return type `id`: ["The return type of convenience constructors is id for the same reason it is id for initializer methods"](http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocAllocInit.html#//apple_ref/doc/uid/TP30001163-CH22-SW12) – albertamg Aug 12 '11 at 20:53
  • 2
    @albertamg Can you elaborate on why `[self alloc]` is used over `[PotatoPeeler alloc]`? Not sure what you mean by handling subclassing properly...thx – raffian Jun 23 '12 at 00:56
  • 3
    Let's say you have a subclass, `SweetPotatoPeeler`. If you use `+[self alloc]`, then the convenience method will properly return an instance of `SweetPotatoPeeler` instead of `PotatoPeeler`. If you use `+[PotatoPeeler alloc]`, the method will always give you an instance of your base class, and subclasses will not be able to take advantage of it. – Jonathan Grynspan Jun 23 '12 at 02:54
4

Generally my approach is the following: first I create a normal initializer method (instance method), then I create a class method that calls the normal initializer. It seems to me Apple uses the same approach most of the time. An example:

@implementation SomeObject

@synthesize string = _string; // assuming there's an 'string' property in the header

- (id)initWithString:(NSString *)string 
{
   self = [super init];
   if (self)
   {
      self.string = string;
   }
   return self;
}

+ (SomeObject *)someObjectWithString:(NSString *)string
{
   return [[[SomeObject alloc] initWithString:string] autorelease];
}

- (void)dealloc
{
   self.string = nil;

   [super dealloc];
}

@end
Wolfgang Schreurs
  • 11,779
  • 7
  • 51
  • 92