Reputation: 4731
Normally, you define init or initWith... methods and call them inside convenient constructors like this:
@implementation MyClass1 : NSObject
@synthesize n, s;
-(id)init
{
self = [super init];
if (self) {
self.n = 1;
}
return self;
}
-(id)initWithString:(NSString *)s
{
self = [self init];
if (self) {
self.s = s;
}
return self;
}
+(id)myClass
{
return [[self alloc] init];
}
+(id)myClassWithString:(NSString *)s
{
return [[self alloc] initWithString:s];
}
@end
But I think it is possible to define convenient constructors without defining init or initWith... methods like this:
@implementation MyClass2
@synthesize n, s;
+(id)myClass
{
MyClass2 *obj = [[self alloc] init];
obj.n = 1;
return obj;
}
+(id)myClassWithString:(NSString *)s
{
MyClass2 *obj = [self myClass];
obj.s = s;
return obj;
}
@end
Is it bad practice to define convenient constructors without defining init method?
If it is bad practice, could you tell me the disadvantage or problems?
Upvotes: 2
Views: 176
Reputation: 2590
Doing it like you did with MyClass1
makes it easier to define a designated initializer. Apple recommends these; they help reducing code repetition.
Upvotes: 0
Reputation: 301
//I think it is BAD. First of all, you confused CLASS and OBJECT, you get an object like this
// [[CLASSNAME alloc] init];
//not like this:
// [[OBJECT alloc] init];
//so, what you want to do shall be like this:
// +(id)myClass
// {
// MyClass2 *obj = [[[self class] alloc] init];
// obj.n = 1;
// return obj;
// }
PLEASE ignore above all, thanks to @W'rkncacnter.
however, here you are returning an object owned by yourself without autorelease, it's not recommended.
Maybe what you really want is something like factory method?
+(id)myObjectWithString:(NSString *)string
{
MyClass2 *obj = [[[MyClass2 alloc] init] autorelease];
obj.s = string;
return obj;
}
Upvotes: 0
Reputation: 11834
I'm not sure if it's actually a bad practice. Generally, when I write convenience constructors they look like this:
+ (FSClub *)clubWithXMLElement:(SMXMLElement *)element;
{
FSClub *club = [[FSClub alloc] init];
if (club)
{
club.identifier = [element integerValueWithPath:@"id"];
club.name = [element valueWithPath:@"naam"];
club.referer = [element URLWithPath:@"referer"];
}
return club;
}
The code still takes into account possible memory issues (initialisation failure) like in a 'normal' init constructor. Values will only be set if initialisation is successful.
The interface file is defined as such:
@interface FSClub : NSObject
@property (nonatomic, assign, readonly) NSInteger identifier;
@property (nonatomic, copy, readonly) NSURL *referer;
@property (nonatomic, copy, readonly) NSString *name;
+ (FSClub *)clubWithXMLElement:(SMXMLElement *)element;
@end
Please note the properties are readonly. I prefer creating immutable objects, since they're easier to deal with in e.g. threaded code. The only way to have the properties set in this situation is by using the convenience constructor.
When I create the convenience constructors, these are generally the only methods I use to instantiate the objects. That is to say, the -init
method will not be used most of the time. Also, writing lots of initialisers even if you don't use them takes lots of developer time. I wouldn't create methods that I don't use.
When you create code as part of a framework -code that you share with lots of people that you don't know- in such situations you might want to write both the convenience constructors as well as all the normal constructors, because you can't be sure how the code will be used in people's own project. For example creating lots of objects using convenience constructors in tight loops might be bad for performance, since the objects are added to the autorelease pool. I think this is also true in an ARC scenario. In such situations one might have the option to use 'normal' constructors to create objects.
Upvotes: 1