init method pattern - iphone

I am having problems understanding what/how this works. it seems odd assigning self to return from the init message
- (id)init
{
if((self=[super init])) {
//code here for setting up
}
return self;
}

The init method first assigns the implicit self local variable (self is one of the two hidden arguments passed to methods) to the return value of the superclass's designated initializer. The reason behind this is that initializers can return a different object than the one that received the message, for example, when it is not possible to initialize the receiver correctly or when an existing instance is returned to avoid the need to initialize a new one.
After self is set, the if statement ensures that instance variables are only initialized if self is not nil. If self is nil, accessing the memory for the instance variables may be an error. Very few classes return nil but still it is a valid return value.
This is described in Implementing an Initializer.

Related

Making Xcode complain about a missing parameter

I am designing a new application by modernizing code I wrote in the past. This old code uses the class/delegate model and I am trying to transform them to use blocks as callbacks, not the delegate stuff.
What I do is to create a property like
#property (nonatomic, copy) void (^onTouch)(NSInteger index);
That would pass to the object using that class a block where code can be inserted and in this case executed on touch.
But my problem is this. When you use delegates and you have a method on the delegate protocol, Xcode will warn if you use that class and forget to implement the delegate protocols. Is that a way to do that with blocks? Or in other words: is there a way to make Xcode complain if a callback block is not defined by the caller?
I mean this would be the correct:
MyClass *obj = [[MyClass alloc] init];
obj.onTouch = ^(NSInteger *index){ //call back code to be executed };
This would be OK too
MyClass *obj = [[MyClass alloc] init];
obj.onTouch = nil;
but this would generate a message
MyClass *obj = [[MyClass alloc] init];
// no callback block defined.
Is this possible?
If you want to enforce setting a certain parameter, I would include it in the initializer.
MyClass *obj = [[MyClass alloc] initWithBlock:^(NSInteger *index) { /* code*/ }];
Then, in MyClass:
- (id)init {
// This will result in a runtime error if you use the wrong initializer.
NSAssert(NO, #"Use initWithBlock instead.");
}
- (id)initWithBlock(initWithBlock:^(NSInteger *)block) {
self = [super init];
if (self) {
self.onTouch = block;
}
return self;
}
Also note, attempting to execute a NULL block results in a crash, so make sure to do:
if (self.onTouch) { self.onTouch(); }
Wherever you run the block.
First, I strongly recommend defining types to represent your blocks - makes them a lot easier to work with, especially if you need to refactor the parameters.
You can't write code that distinguishes between "I set this property to nil" or "the runtime initialized this property to nil", at least not without some crazy runtime code to check the stack. Only option I can think of would be to use the null object pattern. Before I elaborate, bear in mind that I haven't actually tried to test this, but it should work. Define a block that means 'has no value' and set your property to point to that block on init. Then you can compare to that NullBlock at runtime to identify if someone explicitly set the property to nil (because it would be nil at that point) or gave it a real non-nil value.
Alternatively, if you don't mind manually writing your set accessors, you could have a BOOL that tracks if someone set the property explicitly. Then when you call the block just check if someone actually set the value or not.
#synthesize onTouchBlock=_onTouchBlock;
MyBlock _onTouchBlock;
BOOL _onTouchBlockWasSet;
- (void)setOnTouchBlock:(MyBlock)block {
_onTouchBlockWasSet = YES;
_onTouchBlock = block;
}
I would not recommend passing the value in the initializer because that makes it tied to the creation of that object type. If you wanted to change the block in code based on some condition, you'd be back to square one. Also, it prevents you from using storyboards which create that object.

Link superclass init method to designated initializer

Since every class inherits the initializer from the superclass this is how I have linked
the default implementation of init to its designated initializer. (It's working.)
Link:
-(id)init {
return [self initWithItemName:#"Default Value"
valueInDollar:0
serialNumber:#""];
}
Initializer:
-(id)initWithItemName:(NSString *)myItemName
valueInDollar:(int)myValueInDollar
serialNumber:(NSString *)mySerialNumber;
My question is, do I ALWAYS have to link my own initializer the way I did it (Link)?
So WITHOUT the code below own initializers would never be called? Am I right?
-(id)init {
return [self myInitMethod......"];
}
If you only initialize your object with initWithItemName, then you don't have to define the init method at all. The initializers are just ordinary methods (no magic involved), so what you call is what will be invoked. But it is good practice to implement the init method so it can be called and the object will be in a consistent state.

Designated Initializer

-(id)initWithStrAndDate: (NSString *)inString date:(NSDate *)inDate
{
if (self = [super init])
{
[self setStr:inString];
[self setDate:inDate];
}
return self;
}
-(id)initWithStr: (NSString *)inString
{
return [self initWithStrAndDate:inString date:[NSDate date]];
}
-(id)init
{
return [self initWithStr:nil];
I am not sure that I know how to use the "designated initializer". First of all isn't
return [self initWithStrAndDate:inString date:[NSDate date]];
this wrong? Shouldn't this be:
return [self initWithStrAndDate:inString date:nil];
And also why do we use 3 different initializers? I mean when do we use "-(id)init" or "-(id)initWithStr: (NSString *)inString" or the first one?
When a class has multiple initializers one, or sometimes more, of them are defined as designated initializers - these initializers must completely initialize the class and invoke a designated initializer of the super-class so that it is completely initialized.
Other initializers are called secondary initializers and must, via calls to self, end up invoking one of the designated initializers. (See Cocoa Fundamentals - Multiple initializers.)
There are two main consequences of this model:
You avoid code duplication and consequential errors; for most classes one initializer does all the work; and
When sub-classing you only need to override the super-classes designated initializers and primitive methods (the non-initializer method equivalent of designated initializers) as all other methods of the super-class end up calling these. You can of course override more methods and must to do if you need to change the behaviour (see Cocoa Fundamentals - Multiple initializers again.)
So your example is correct - 1 designated initializer and 2 secondary initializers which invoke the designated one via a call to self.
There are 3 different initializers so you can use either one of them - neither one of them is wrong, they allow you to init the class with variable data - mostly for convenience as in the end they all do the same thing.
You may init the class with specified string and date (the first and longest initializer),
or you can specify the string only and have the date be set to current time,
or you can have an empty string and current time.
note that calling:
[self init];
is equal to calling
[self initWithStr: nil];
and this in turn is equal to
[self initWithStrAndDate: nil date:[NSDate date]];
So in the end you are calling the same initializer anyway.
You have one designated initializer read the root initializer or the method that does all the work. The other initializer methods only call this root initializer with default values for the missing parameters.
You use [NSDate date] instead of nil, simply because the current date is your default value.
Which initializer you should use depends on the values you want the object to have set. If you have a string and a date, you use the one with string and date. If you don't have a date, you use the one without the date and just the string, and so on.

Obj-C: Passing pointers to initialized classes in other classes

I initialized a class in my singleton called DataModel. Now, from my UIViewController, when I click a button, I have a method that is trying to access that class so that I may add an object to one of its dictionaries. My get/set method passes back the pointer to the class from my singleton, but when I am back in my UIViewController, the class passed back doesn't respond to methods. It's like it's just not there. I think it has something to do with the difference in passing pointers around classes or something. I even tried using the copy method to throw a copy back, but no luck.
UIViewController:
ApplicationSingleton *applicationSingleton = [[ApplicationSingleton alloc] init];
DataModel *dataModel = [applicationSingleton getDataModel];
[dataModel retrieveDataCategory:dataCategory];
Singleton:
ApplicationSingleton *m_instance;
DataModel *m_dataModel;
- (id) init {
NSLog(#"ApplicationSingleton.m initialized.");
self = [super init];
if(self != nil) {
if(m_instance != nil) {
return m_instance;
}
NSLog(#"Initializing the application singleton.");
m_instance = self;
m_dataModel = [[DataModel alloc] init];
}
NSLog(#"ApplicationSingleton init method returning.");
return m_instance;
}
-(DataModel *)getDataModel {
DataModel *dataModel_COPY = [m_dataModel copy];
return dataModel_COPY;
}
For the getDataModel method, I also tried this:
-(DataModel *)getDataModel {
return m_dataModel;
}
In my DataModel retrieveDataCategory method, I couldn't get anything to work. I even just tried putting a NSLog in there but it never would come onto the console.
Any ideas?
Most likely you are sending messages that get ignored, e.g. they're being sent to objects which don't exist/aren't the one you're looking for, and for some reason aren't crashing. This occurs in the case of messaging nil, or possibly other illegitimate values. Although you seem to expect that the m_ variables will be initialized to 0, this is not good form, and furthermore you are not following a very typical objc pattern for your singletons -- m_dataModel should be an ivar of m_instance, and m_instance should probably be declared static, as you probably don't want it accessed from other files directly. In addition, the most likely source of your bug is somehow the -init method, which should never be called on a singleton -- instead do something like this:
+ (ApplicationSingleton *)sharedInstance {
static ApplicationSingleton *instance = nil;
if(!instance) {
instance = [[self alloc] init]; //or whatever custom initializer you would like, furthermore some people just put the initialization code here and leave -init empty
}
return instance;
}
the code you have now leaks because you allocate an object (self) and don't release it before returning a potentially different instance (the shared one if one already exists), such that the newly allocated one is typically lost.

-(id)init using designated initialiser?

In the past I have always used method_1: but I have recently noticed a few instances where folks have instead called the superclasses designated initialiser. Does it matter, just curious if its more about style or substance?
Method_1:
-(id)init {
self = [super init];
if(self) {
//do things ...
}
return self;
}
Method_2:
-(id)init {
[super initWithNibName:nil bundle:nil];
// do things ...
return self;
}
-(id)initWithNibName:(NSString *)nibName bundle:(NSString *)bundle {
return [self init];
}
cheers Gary
Only a few instances?
The Apple documentation on Designated initialisers has this to say on subclassing:
General Principle: The designated initializer in a class must, through a message to super, invoke the designated initializer in a superclass.
Also:
Designated initializers are chained to each other through messages to super, while other initialization methods are chained to designated initializers through messages to self.
Your method 2 would be correct if:
-initWithNibName:bundle: is the designated initialiser of the super class
-init is the designated initialiser of your class (your example is a bit bizarre since you deliberately throw daway the nib name and bundle parameters)
-init was correctly coded not to throw away the return value from [super initWithNibName: bundle: ]
In your class you have to cover all of the super class init methods to ensure that your designated initialiser ultimately gets invoked.
Neither of the above make any sense for the init methed of a class who's super class has the designated initializer
- (id)initWithNibName:(NSString *)nibName bundle:(NSString *)bundle
What are you trying to do?
The idea of a designated initialiser is that all other initialisation functions of a class should call it (this is only enforced by convention). This is nice as it means that someone writing a subclass can add additional steps to the initialisation while only overriding a single initialisor. When this pattern is being used, method 1 should only happen when init is the designated initialiser. One strategy for ensuring this occurs is as follows:
Suppose C inherits from B and let the designated initialisers be d_c and d_b. We override d_b in C to make it simply call d_c on itself. Since d_b is called by all other initialisers of B, this ensures that all initialisers present in the subclass call d_c. We then make all new initialisers call d_c too. d_c calls d_b in its superclass B, which will then pass the calls further up the chain.
Note that this strategy is the opposite of how classes are often initialised. For example, an initialiser with arguments a:1 b:2 could handle the b argument and then call another initialiser that could handle just the a argument. This works well when the function with arguments a and b is simple, by the designated initialiser works better in more complicated situations.