Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Objective C : class init with block ?

Is it possible to, say, use a block as a completion handler in a View Controller's init method so that the parent view controller is able to fill in the details in a block without having to create a custom initWithNibName:andResourceBundle:andThis:andThat: for each possible properties ?

// ... in the didSelectRowAtIndexPath method of the main view controller :
SubViewController *subviewController = [[SubViewController alloc] initWithNibName:nil bundle:nil completionHandler:^(SubViewController * vc) {
    vc.property1 = NO;
    vc.property2 = [NSArray array];
    vc.property3 = SomeEnumValue;
    vc.delegate = self;
}];
[self.navigationController pushViewController:subviewController animated:YES];
[subviewController release];

in SubViewController.m :

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil completionHandler:(void (^)(id newObj))block {
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        block(self);
    }
    return self;
}

instead of

// ... in the didSelectRowAtIndexPath method of the main view controller :
SubViewController *subviewController = [[SubViewController alloc] initWithNibName:nil bundle:nil andProperty1:NO andProperty2:[NSArray array] andProperty3:SomeEnumValue andDelegate:self];
[self.navigationController pushViewController:subviewController animated:YES];
[subviewController release];

with

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil andProperty1:(BOOL)p1 andProperty2:(NSArray *)p2 andProperty3:(enum SomeEnum)p3 andDelegate:(id<MyDelegateProtocol>)myDelegate {
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
       self.property1 = p1;
       self.property2 = p2;
       self.property3 = p3;
       self.delegate = myDelegate;
    }
    return self;
}

So that I can do whatever I want in the main controller vs calling a predefined init method (and having to write one for each possible initialization).

Is it something bad ? will there be retain cycles ?

like image 716
David Lacourt Avatar asked Oct 09 '12 09:10

David Lacourt


4 Answers

Which advantages do you see in using a block? The initializer is commonly used to set up private state of the instance. This private state could not be accessed from the block since the block is implemented somewhere else.

If you only use public properties why not setting them up after initialization?

SubViewController *vc = [[SubViewController alloc] initWithNibName:nil bundle:nil];
vc.property1 = NO;
vc.property2 = [NSArray array];
vc.property3 = SomeEnumValue;
vc.delegate = self;

That's exactly what the block version does (without the hassle).

Is it something bad ? will there be retain cycles ?

No, but I would dismiss your proposition for architectural reasons: You are either breaking encapsulation of the class or do not gain any advantage over just doing what the block does after initialization.

like image 77
Nikolai Ruhe Avatar answered Oct 05 '22 19:10

Nikolai Ruhe


The questions are:

  1. What is the benefit of this way?
  2. Will you ba able to manage this code efficient?

Be aware that when program is growing new levels of invocation will be added and this will make your code hard to read, maintain, extend or develop. Think about future subclassing as well and how will you debug this code in the future to find some mismatched value. Blocks can make you code faster, but delegate pattern will make your code clean and run in one thread, which is easy to maintain in the future, and this is real value for the professional programmer.

like image 37
pro_metedor Avatar answered Oct 05 '22 19:10

pro_metedor


You could define your

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil completionHandler:(void (^)(id newObj))block;

method in a category of UIViewController; from there you would call initWithNib and then execute your completion block on the just allocated self:

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil completionHandler:(void (^)(id newObj))block
{
    if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
        block(self);
    }
    return self;
}

I think this should work fine.

like image 26
sergio Avatar answered Oct 05 '22 20:10

sergio


This is possible, no retain problems. Everything will be called synchronously on the same thread.

BUT What is the benefit of not doing this the simple way - calling another method after init, e.g.

MyController* controller = [[[MyController alloc] init] autorelease];
[self updateController:controller];
Does the code have be called from the init method?

In general, I would advice to create separate init... methods if you want to initialize the object in different ways.

like image 20
Sulthan Avatar answered Oct 05 '22 19:10

Sulthan