Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Property nil-ed after constructor injection. #306

Closed
mmiedlarz opened this issue Jan 15, 2015 · 18 comments
Closed

Property nil-ed after constructor injection. #306

mmiedlarz opened this issue Jan 15, 2015 · 18 comments

Comments

@mmiedlarz
Copy link

Hey,

Its my first approach to DI in obj-c, so I'm using Typhoon to inject some components to my ViewController (using constuctor injection).
In constructor everything works great, but in viewDidLoad all injected parameters are set to nil.
Here is my code:

ViewController:

@interface MyViewController ()
@property (nonatomic, strong, readonly) MyScanner *scanner;
@end

@implementation MyViewController

- (instancetype)initWithScanner:(MyScanner *)scanner
{
    self = [super init];
    if(self)
    {
        _scanner = scanner;
    }
    DDLogInfo(@"Constructor %@",_scanner);
    return self;
}

- (void) viewDidLoad
{
    [super viewDidLoad];
    DDLogInfo(@"viewDidLoad: %@",_scanner);
}

Factory:

- (MyViewController *)myViewController
{
    return [TyphoonDefinition withClass:[MyViewController class]
            configuration:^(TyphoonDefinition *definition)
            {
                [definition useInitializer:@selector(initWithScanner:) parameters:^(TyphoonMethod *initializer)
                 {
                     [initializer injectParameterWith:[self.componentsAssembly scanner]];
                 }];
            }];
}

ComponentsAssembly:

- (MyScanner*) scanner;
{
    return [TyphoonDefinition withClass:[MyScanner class]
            configuration:^(TyphoonDefinition *definition)
            {             
                [definition setScope:TyphoonScopePrototype];
            }];
}

Output:

Constructor: <MyScanner: 0x17008f320>
viewDidLoad: (null)

Am I doing something wrong, or did I forgot something?

Cheers,
M.

@jasperblues
Copy link
Member

It looks ok, did not notice any problems. What version of Typhoon are you using? Is there a difference between sim and device? Some early 2.x versions showed problems on 64bit devices.

@alexgarbarev
Copy link
Contributor

Hey,

Your code looks correct.
Try to override this method
- (void)setScanner:(MyScanner *)scanner
inside MyViewController and set breakpoint inside to be sure that no one set nil.
Also try to override
dealloc method inside MyScanner class and set breakpoint inside, then you'll see time when it's dealloced

@jasperblues
Copy link
Member

Or if still no luck, a sample project.

@mmiedlarz
Copy link
Author

Thanks for help!
It's very strange, but after total clean (clean, clear derived data, remove app from device, and reset simulator) problem disappeared.

@jasperblues
Copy link
Member

That is very strange. But I think no need to worry now. We've got Typhoon behaving solidly in plenty of apps.

@jasperblues
Copy link
Member

Please report back if you notice the issue again.

@mmiedlarz
Copy link
Author

Sorry, my bad. I haven't noticed one change :/ Problem Is back.

So here is full code of factory:

- (MySidePanelController *)sidePanelController
{
    return [TyphoonDefinition withClass:[MySidePanelController class]
            configuration:^(TyphoonDefinition *definition)
            {
                [definition useInitializer:@selector(init)];
                [definition injectProperty:@selector(leftPanel)   with:[self moreMenuViewController]];
                [definition injectProperty:@selector(centerPanel) with:[self navigationControllerWithRootController:[self myViewController]]];
            }];
}

- (NavigationController*) navigationControllerWithRootController:(UIViewController*)rootVC
{
    return [TyphoonDefinition withClass:[NavigationController class]
            configuration:^(TyphoonDefinition *definition)
            {
                [definition useInitializer:@selector(initWithRootViewController:)
                    parameters:^(TyphoonMethod *initializer)
                    {
                        [initializer injectParameterWith:rootVC];
                    }
                 ];
            }];
}

- (MoreMenuViewController *)moreMenuViewController
{
    return [TyphoonDefinition withClass:[MoreMenuViewController class]
            configuration:^(TyphoonDefinition *definition)
            {
                [definition useInitializer:@selector(init)];
            }];
}

- (MyViewController *)myViewController
{
    return [TyphoonDefinition withClass:[MyViewController class]
            configuration:^(TyphoonDefinition *definition)
            {
                [definition useInitializer:@selector(initWithScanner:) parameters:^(TyphoonMethod *initializer)
                 {    
                     [initializer injectParameterWith:[self.componentsAssembly scanner]];
                 }];
            }];
}

And my appDelegate

//Everything works fine
self.window.rootViewController = [(ViewControllerAssembly*)[MyAssembly assembly] myViewController];
//scanner nil-ed
self.window.rootViewController = [(ViewControllerAssembly*)[MyAssembly assembly] sidePanelController]

In second case, both MyViewController and MyScanner are dellacoted during TyphoonStackElement destrucion (TyphoonStackElement.cxx_destruct).

@jasperblues jasperblues reopened this Jan 15, 2015
@alexgarbarev
Copy link
Contributor

This line looks strange:
[(ViewControllerAssembly*)[MyAssembly assembly] myViewController];
Because you have to create TyphoonBlockComponentFactory instance instead of calling [MyAssembly assembly]
Like this:

TyphoonComponentFactory *factory = [[TyphoonBlockComponentFactory alloc] 
    initWithAssemblies:@[
    [ViewControllerAssembly assembly],
    [ComponentsAssembly assembly]
]];

self.window.rootViewController = [(ViewControllerAssembly*) factory sidePanelController]; 

Check quick start guide or wiki: https://github.com/appsquickly/Typhoon/wiki/Quick-Start
Also you might be interested in plist bootstrap integration.

@mmiedlarz
Copy link
Author

I'm doing it:
MyAssembly (inherits from NSObject) :

+ (TyphoonComponentFactory *) assembly
{
    static TyphoonComponentFactory *factory = nil;

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        factory = [[TyphoonBlockComponentFactory alloc] initWithAssemblies:
                    @[
                        [ViewControllerAssembly assembly],
                        [ComponentsAssembly assembly],
                     ]];
    });
    return factory;
}

@jasperblues
Copy link
Member

[removed incorrect comment - mistook MyAssembly for one of your Typhoon assemblies]

@alexgarbarev
Copy link
Contributor

I can't see any usage of myViewController definition. Where is it called?

@mmiedlarz
Copy link
Author

@jasperblues
MyAssebly is just a wrapper. It holds instance of TyphoonComponentFactory and nothing else (I don't want to call [TyphoonBlockComponentFactory alloc] initWithAssemblies:..] directly in appDelegate).
Maybe name [MyAssembly getFactory] should be better.

@alexgarbarev
My bad (one more time, I have to change method and class names due to NDA). Its used in:

- (MySidePanelController *)sidePanelController
{
    return [TyphoonDefinition withClass:[MySidePanelController class]
            configuration:^(TyphoonDefinition *definition)
            {
                [definition useInitializer:@selector(init)];
                [definition injectProperty:@selector(leftPanel)   with:[self moreMenuViewController]];
                [definition injectProperty:@selector(centerPanel) with:[self navigationControllerWithRootController:[self myViewController]]];
            }];
}

@jasperblues
Copy link
Member

@mmiedlarz, [not related to current problem, but] . . .

it definitely sounds like you want plist integration then. Besides injecting into your app delegate, it will also give injection on UIStoryboard created classes according to definitions in your assemblies or with autowiring. Furthermore it bootstraps Typhoon at the right time for UIStateRestoration to work correctly and encourages avoiding the use of singletons, by instead injecting Typhoon as you proceed from one object graph to another.

@alexgarbarev
Copy link
Contributor

What properties attributes you have for MySidePanelController.centerPanel ? Strong?

I see next strange thing: your MyViewController must be retained by UINavigationController which should be retained by MySidePanelController.. So I can't see reason of MyViewController deallocation

@mmiedlarz
Copy link
Author

@alexgarbarev
Yes. All panels have Strong references.

@jasperblues
Probably I'll add this in the future :) I just wanted to make it simply at the beginning.

@alexgarbarev
Copy link
Contributor

Hmm. Looks strange.. Try to include myViewController into NavigationController definition instead of passing via runtime argument and say if that works..

- (NavigationController*) rootNavigationController
{
    return [TyphoonDefinition withClass:[NavigationController class]
            configuration:^(TyphoonDefinition *definition)
            {
                [definition useInitializer:@selector(initWithRootViewController:)
                    parameters:^(TyphoonMethod *initializer)
                    {
                        [initializer injectParameterWith:[self myViewController]];
                    }
                 ];
            }];
}

@mmiedlarz
Copy link
Author

Hey,

Okay, solved. Problem was in totally different place. SidePanelController was recreating all panels in viewDidLoad.
Once again, thanks for help!

Cheers,
M.

@jasperblues
Copy link
Member

@mmiedlarz Thanks for choosing Typhoon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants