Set args in -(id)init - objective-c

is possible pass an args on an init method? I'm working with UITableViewController classes and UINavigatorController, when I push a new view whit:
[self.navigationController pushViewController:[[Class alloc] init] animated:YES];
I would also pass a string to that controller, is it possible?

It is not only possible, but it is normal and very common. In fact, it is quite common to have multiple initializers in classes where each initializer has a different combination of args, allowing you to initialize a class in different ways.
As you find yourself creating more than one initializer for a class, you should be sure to follow the best practices for making one initializer the "designated initializer". Here is a link to an article that demonstrates the principle of designated initializers.

Sure, just define your own initializer for your view controller subclass:
- (id)initWithStyle:(UITableViewStyle)style andSomeParameter:(NSString *)param
{
if (self = [super initWithStyle:style]) {
myInstanceVariable = [param retain];
}
return self;
}
(This is for a subclass of UITableViewController.)

Related

Objective-C subclass setup

I have 3 Objective-C classes:
Animal - a subclass of NSObject
Feline - a subclass of Animal
Cat - subclass of Feline
Each of these three classes implement (what I thought was) its own private method (-private_Setup), for setup:
e.g. in Animal:
-(instancetype)init
{
self = [super init];
if (self) {
[self private_Setup];
}
return self;
}
Same thing in the Feline and Cat classes.
A list of required classes is provided at runtime, so I am creating instances of the various classes, depending on a list of class names.
e.g.
NSString *className = ... // #"Animal", #"Feline" or #"Cat".
id animal = [[NSClassFromString(className) alloc] init];
Problem:
If I create an instance of Cat, -private_Setup is being called multiple times, for each step in the inheritance chain. For example, the calling chain for a Cat:
-[Cat init]
-[Feline init]
-[Animal init]
-[Cat private_Setup] // First!
then, from:
-[Feline init]
-[Cat private_Setup] // Second!
then, from:
-[Cat init]
-[Cat private_Setup] // Third!
Thus, what I thought was a method private to each class, is being called after each -init in the hierarchy.
Could someone please advise how I could either fix this issue or redesign my setup strategies? Thankyou.
[edited, appended for clarity]
Some form of setup is required at each level of the inheritance, to supply data particular to that level. e.g. Cat specific settings.
Thus, what I need is for a Cat object to be fully set up as an Animal, as a Feline, and as a Cat.
I guess one approach is to have different setup method names at each level. e.g. setupAnimal, setupFeline, setupCat.
e.g. in Animal:
-(instancetype)init
{
self = [super init];
if (self) {
[self setupAnimal];
}
return self;
}
// in Feline:
-(instancetype)init
{
self = [super init];
if (self) {
[self setupFeline];
}
return self;
}
// in Cat:
-(instancetype)init
{
self = [super init];
if (self) {
[self setupCat];
}
return self;
}
Is there a better, more preferred design than this?
I think you should call private_Setup method just once - in Animal's init. That's all. Implement in all subclasses, call just once in supercalass
You're saying that you need a different form of setup for each subclass. Of course! That's why what you do is you implement private_Setup in every subclass. Implement just like you need it for every particular subclass. It's called overriding. Inside overridden method call [super private_Setup]. You'll get exactly what you need.
You are saying super init and super's init calls private_Setup, so why are you surprised?
I guess you're surprised because you expect each init in the chain to call its own class's private_Setup. But the thing to understand is that meaning of self changes depending on the original class of the instance that started the chain (polymorphism).
So, for example, if you call self private_Setup from Animal's init during the process of initializing a Cat, it is Cat's private_Setup that will be called. Thus, with the arrangement you've made, it is exactly true that on initialization of a Cat, Cat's private_Setup will be called three times and none of the others will be called.
The solution is simple: don't use an extra method. It is a capital mistake to have an initializer call any methods (and the problem you're having is one of the reasons why it's a mistake). Instead, simply perform the setup in init itself. That is what it's for.
If I understand correctly, you are implying that the call to private_Setup happens in all of your subclasses' init methods too.
Remove the call to private_Setup from all but the top (e.g. Animal) init method. As long as all of them call [super init], you'll get exactly one call to private_Setup.

Initializing a class with designated initializer - confusion

I am reading a book which teaches that we should implement a designated initializer of class, and then let other initializers call this one (which I agree with).
Now, I am in a section in this book, which says: "The designated initializer of UITableViewController is initWithStyle:". And then goes in the implementation like this:
// inside ItemsViewController.m, subclass of UITableViewController
-(id) init{
// Call the superclass's designated initializer
self = [super initWithStyle:UITableViewStyleGrouped];
if (self) {
}
return self;
}
- (id)initWithStyle:(UITableViewStyle)style
{
return [self init];
}
After this code, the book also says: "This will ensure that all instances of ItemsViewController use the UITableViewStyleGrouped style, no matter what initialization message is sent to it."
To stick to the principle I described in the beginning of the post, I might had implemented this class like this:
// inside ItemsViewController.m, subclass of UITableViewController
// Implement the designated initializer first
-(id) initWithStyle:(UITableViewStyle)style
// Call the superclass's designated initializer
self = [super initWithStyle:UITableViewStyleGrouped];
if (self) {
}
return self;
}
// Call designated initializer
- (id)init
{
return [self initWithStyle: nil];
}
But I think I am missing something? I think the book uses init as designated initializer in their case?
Construction
// Implement the designated initializer first
-(id) initWithStyle:(UITableViewStyle)style
// Call the superclass's designated initializer
self = [super initWithStyle:UITableViewStyleGrouped];
if (self) {
}
return self; }
is confusing for user of class, because he will think that you provided initWithStyle constructor to allow him setting controllers' style. You should use your first init method.
If you have custom tableviewcell class then you dont need to implement initwithstyle initializer and also that initialilzer will not be called for primitive and class member objects.you will have to suffer.so follow the standard initialiser.
Basically your code and the code in the book do the same thing. there are different types of initializers for classes, you use the one you need. What the example in the book wants to show you is how to overwrite them if you need some custom initializers later on. Sow it's understandable that it doesn't really make sense from a real live application point of view but from a learning overwriting initializers and customization i think it's ok.

Preventing subclasses overriding methods

Assume there is an object that initialises like so
- (void)doInit
{
NSLog(#"In BaseClass init");
}
- (id)init
{
self = [super init];
[self doInit];
return self;
}
and it has a subclass which is inited in a similar way
- (void)doInit
{
NSLog (#"In SubClass init");
}
- (id)init
{
self = [super init];
[self doInit];
return self;
}
Now if I create an instance of child class then I receive the following output:
In SubClass init
In SubClass init
when really, what I meant to happen is
In BaseClass init
In SubClass init
Is there a way to mark doInit to say that it shouldn't be overridden or do I need to create a unique name for all methods in a subclass?
I'm not entirely sure how I haven't come across this issue before, but there you go.
Edit:
I understand why this is happening, I hadn't expected that the base class would be able to call the overridden function.
I also can't just call [super doInit]; from the Subclass method because the BaseClass still needs to call doInit so that creating an instance of BaseClass will still work. If I called [super doInit], I'd still end up getting SubClass's doInit called twice.
It appears the answer is no and I'll just have to uniquely name each doInit like doBaseClassInit and doSubClassInit.
If you have a method that you don't want to by dynamically bound (i.e. don't want a subclass method to be called if it exists), you need to do it as a C function instead. So, you could do this instead:
In the base class:
static void DoInit(BaseClass *self)
{
NSLog(#"In BaseClass init");
}
- (id)init
{
self = [super init];
if (self) {
DoInit(self);
}
return self;
}
in the subclass:
static void DoInit(SubClass *self)
{
NSLog(#"In SubClass init");
}
- (id)init
{
self = [super init];
if (self) {
DoInit(self);
}
return self;
}
Note that both the DoInit methods are marked as static, so they are only visible each compilation unit (.m file) and don't conflict with each other.
You could, perhaps, try something like this in your base class. It would mean any time the init implementation inside BaseClass executed, the doInit implementation for BaseClass would be called.
- (void)doInit
{
NSLog(#"In BaseClass init");
}
- (id)init
{
self = [super init];
Class baseClass = [BaseClass class];
SEL selector = #selector(doInit);
IMP baseClassImplementation = class_getInstanceMethod(baseClass, selector);
baseClassImplementation(self, selector);
return self;
}
As I mentioned in my comment, if that's the narrowness of your need this should work as it gets around the dynamic method lookup involved with sending a message. Hope this helps!
EDIT:
Disclaimer - if you're in this situation it's probably not a good sign for the longevity of your design. This technique will get you up and running for now but please document it carefully, and consider ways to refactor your code so this is no longer used. Consider fixes like these to really be used only when extremely urgent.
The reason why you are not getting the "In BaseClass init" console message is because your subclass is not calling the super's implementation of doInit.
If you don't want doInit overridden the 'best' way to avoid doing so is to not publish the existence of this method. Remove it from your header and uniquely name the method so that a collision is unlikely. For example, many of the private methods in Apple's frameworks have a leading underscore. So, for example, you could call your method _doInit and it will be very unlikely that a subclass accidentally create it's own overiding implementation.
Nope, there's no enforceable way to prevent a subclass from overriding a method. The best you can do is to avoid putting it in the public header file for the class so someone is not likely to try to override it. If the method has to be public, you just put a note in the documentation for the method that it shouldn't be overridden or that subclasses should call super's implementation whichever the case may be. You'll find these kind of instructions all over in the documentation for Apple's own classes.
If you want your subclass to use the baseclass version of doInit then in the subclass don't declare a doInit method. Here's what I mean:
ClassA:
#interface ClassA :
-(void) doInit;
-(id) init;
#implementation
-(void) doInit {
NSLog(#"ClassA doInit");
}
-(id) init {
self = [super init];
if (self != NULL)
[self doInit];
return self;
}
ClassB
#interface ClassB : ClassA
-(id) init;
#implementation
-(id) init {
self = [super init];
if (self != NULL)
[self doInit];
return self;
}
And really you don't need to override the init method as well unless there's some special code that you want that class to do.

Why Default Constructor and Custom Constructor are called for subclasses of UINavigaionController?

#implementation NVController
//Plain Init method
-(id)init
{
self=[super init];
if(self)
{
}
return self;
}
//CustomInit Method
-(id)initWithRootViewController:(UIViewController *)rootViewController
{
self=[super initWithRootViewController:rootViewController];
if(self)`enter code here`
{
}
return self;
}
#end
NVController *instance=[[NVController alloc] initWithRootViewController:nil];
Here In above case ,Since I call only initWithRootViwController, another constructor init is also called. Any help would be appreciated.
This happens because you did not implement your initializers correctly.
In Objective C there is a concept of designated initializer, a single init function of your class that all other initializers must call. It is the designated initializer that calls [super init] directly; all other initializers need to call [super init] indirectly by invoking the designated initializer.
In your particular case you need to move the code common to both your init and initWithRootViewController:, if any, into the initWithRootViewController: initializer, and rewrite the plain init as follows:
-(id)init {
return [self initWithRootViewController:nil];
}
** EDIT :** (in response to the comment indicating that this solution causes an infinite recursion) I think the reason why you get infinite recursion has to do specifically with implementation details of UINavigationController, which should not be inherited. According to Apple's documentation,
The UINavigationController class implements a specialized view controller that manages the navigation of hierarchical content. This class is not intended for subclassing. Instead, you use instances of it as-is in situations where you want your application’s user interface to reflect the hierarchical nature of your content.
EDIT: The prohibition against subclassing has been lifted in iOS 6 - see the documentation for UINavigationController.
I guess initWithRootViewController: is implemented like this:
-(id)initWithRootViewController:(UIViewController *)rootViewController
{
self=[self init];
if(self)
{
// do something with rootViewController
}
return self;
}

Can you accomplish some form of dependency injection in objective-c?

Currently I'm hard coding another object directly when needed and I'd like the ability to take this in through the init method (I assume?). In the code below I create a new HatService in the viewDidLoad method and I'd prefer not to do this this for testability / coupling reasons.
- (void)viewDidLoad
{
[super viewDidLoad];
HatService* hatService = [[HatService alloc] init];
[hatService getHatById:self];
}
First question - How should this be done in objective-c?
Second question - Should I be worried about this or doing it at all?
Update
Here is what I'm starting with but I never seem to hit the first init method during runtime. I have both declared in the interface - anything else I missed that would allow me to override this init method and inject the service dependency?
Note - I can hit this manually when I create the object myself but when the appDeleage does the work I don't see it hit this (how does the app delegate create this object without calling my init method?)
-(id)initWithHatService:(HatService *)service
{
if (self = [super init])
{
[self setService:service];
}
return self;
}
-(id)init
{
HatService* hatService = [[HatService alloc] init];
return [self initWithHatService:hatService];
}
The designated initializer for UIViewController is - (id)initWithNibName:(NSString *)nibName bundle:(NSBundle *)nibBundle. You'll need to override this method if you want your view controller to be properly instantiated from a nib file, if that's what you're trying to do. Without seeing more code, it's impossible to tell, but this method is definitely being called somewhere.
Second, you need to re-write your -init method to look like this:
- (id)init {
return [self initWithHatService:nil];
}
Every init method should call your designated initializer.
Finally, when you're dealing with view controllers, folks usually don't pass in properties in the init method. Instantiate the new controller, then pass it whatever service properties you like.
MyViewController *newContrlr = [[MyViewController alloc] initWithNibName:nil bundle:nil];
newContrlr.hatService = [[[HatService alloc] init] autorelease];
If you're asking how to pass an object to another object's init method, just declare your init method as taking one or more arguments. i.e. - (id) initWithHatService:(HatService *)serv; init methods aren't special in any way except convention in objective-c.
Unrelated: your -getHatById: method is curious. What are the memory management semantics of that? get-prefixed methods are pretty uncommon in Cocoa programs.
It's quite easy to implement the dependency injection design pattern in Objective-C. You could do it manually by creating an 'application assembly' class that defines individual collaborators.
Alternatively, you could use a framework to assist. I recently created a DI container called Typhoon: http://www.typhoonframework.org