I've been using Objective-C for a few years, but I'm still unsure how to initialize an object. Our problem is this:
-(id)init {
self = [super init];
if (self) {
self.backgroundColor = [UIColor blueColor];
self.textColor = [UIColor greenColor];
}
return self;
}
-(id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor yellowColor];
}
return self;
}
Getting it to be initialized the same in both methods means either copy and paste coding (huge no-no, causes endless problems due to human error, such as values being different, like in the example above) or a new method:
-(id)init {
self = [super init];
if (self) {
[self initialSetup];
}
return self;
}
-(id)initialSetup {
self.backgroundColor = [UIColor blueColor];
self.textColor = [UIColor greenColor];
}
This then has the problem that if a subclass has an initialSetup
method, the superclass calling [self initialSetup]
will call the subclass's initialSetup
, and thus ignore all of the superclass's setup. You can get around this by appending the class name each time, such as initialSetupTextField
.
This seems rather messy though. This is all just to initialize an object. How does everyone else do this?
Instantiation: The new keyword is a Java operator that creates the object. Initialization: The new operator is followed by a call to a constructor, which initializes the new object.
There are two ways to initialize a class object: Using a parenthesized expression list. The compiler calls the constructor of the class using this list as the constructor's argument list. Using a single initialization value and the = operator.
A constructor is a special method that has the same name as the class and is used to initialize attributes of a new object.
Every class should have a designated initializer; the one true init...
method variant that must always be called to properly initialize the class (as Matt states).
However, that doesn't fully solve the problem in that a class may also support instantiation through unarchival, which runs into the same "where do I put the common initialization goop" problem in your question.
Typically, I create a method like:
- (void)commonInit
{
... common initialization goop goes here ...
}
And then call it first from the designated initializers and/or initWithCoder:
.
Yes, a bit messy, but not much you can do about it.
A general strategy to reduce the mess:
• provide very very few initializers; maybe init
+ one initializer with all the possible arguments for other configurations.
• Designate the super
's initializer as your DI and make any additional initializers call through it. When subclassing where you have a more specific DI (see UIView
's initWithFrame:
), override the super's DI to call [self initFancy:...]
which then calls [super initThroughSupersDI]
.
You're missing the concept of a Designated Initializer. You write one init method that does the heavy lifting. All other initializers just call the designated initializer. Your example would look like:
-(id)init {
return [self initWithFrame:CGRectZero];
}
-(id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor yellowColor];
}
return self;
}
…appending the class name each time, such as
-initialSetupTextField
Yes, exactly that, or you can create a C function for your private instance initialization routine -- when the designated initializer is insufficient. I use a different naming scheme from yours, but as long as you use a convention which avoids collision, then you're good. I'm mentioning that because the method in your example could be confused for a getter.
Another thing I do is return a value from this routine which indicates whether there was an error (i.e. nil
should be returned).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With