-(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.
Related
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.
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.
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.
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.
I'd like to add additional behavior to a class derives from NSManagedObject and there are 4 distinct (for now) groups of behaviors. I don't need my decorator class to be persisted with CoreData -- it's purely for adding run-time behavior.
However, if I try to apply the standard Decorator pattern, I can't call '[super init]', which makes sense because you need to insert the new object into the ManageObjectContext. But I thought you'd want to invoke [super init] within WindowClassScrollDecorator's init and likewise, later 'dealloc' so everything gets initialized & cleaned up correctly.
I'm inheriting from 'MyWindowClass' class because I don't want my client classes to know the subtype but depending on the decorator used, the behavior will be different.
So what's a good way to approach this?
#interface MyWindowClass : NSManagedObject
{
}
#end
#interface WindowClassScrollDecorator: MyWindowClass
{
MyWindowClass *decoratedClass;
}
- (id)initWithMyWindowClass:(MyWindowClass *)aWindowClass;
#end
#implementation WindowClassScrollDecorator
- (id)initWithMyWindowClass:(MyWindowClass *)aWindowClass
{
// Calling [super init] elicits the following error:
// Failed to call designated initializer on NSManagedObject class 'ModelClassScrollDecorator'
if (self = [super init])
{
// do some initialization work here
self.decoratedClass = aWindowClass;
}
}
#end
The lifecycle of NSManagedObjects is a bit different from that of other objects; specifically, the object may turn into a fault (essentially a shell object without any of its properties set) without being deallocated. You should be sure to be aware of these events, so you may want to look at the NSManagedObject Class Reference - Subclassing Notes document. Specifically, you may want to look into awakeFromInsert:, awakeFromFetch:, and (will|did)TurnIntoFault.
To address your immediate issue, an NSManagedObject cannot be created without an NSManagedObjectContext to live in. Thus, to initialize a managed object, you must call its designated initializer:
initWithEntity:insertIntoManagedObjectContext:
Your init method needs to call that method on the superclass or else your NSManagedObject won't work.
The question you have here seems to not be CoreData specific but OO design.
You shouldn't be inheriting NSManagedObject if it is not a NSManagedObject.
You should make MyWindowClass either be a protocol, or a class which has a NSManagedObject.