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

Crash using TyphoonFactoryProvider using Xcode 5.1 after building for Release #200

Closed
jervine10 opened this issue Mar 20, 2014 · 18 comments
Closed
Labels

Comments

@jervine10
Copy link
Contributor

This is an incredibly odd scenario. Our team has spent the better part of two days tracking this issue down. We've recently upgrade to Xcode 5.1 which came out March 10th. Any time we build and run the app in Debug mode all is well. However, as soon as we Archive the app using the Release build configuration and install on a device we get a continuous crash at launch. We've investigated the crash logs and have found a segmentation fault that is occurring when instantiating a ViewController using Typhoon's FactoryProvider. It's incredibly weird.

We updated our scheme's run configurations to use the Release configuration and installed directly on the device. The exception breakpoint does indeed stop on the line that's using the FactoryProvider. The debug console says that self is nil.... so that's a huge red flag.

I'm going to link out to a project that simulates this issue. Keep in mind this is only affected with Xcode version 5.1. Here's the snippet that causes the crash.

@implementation ToolsCrasherDataSource

#pragma mark - ToolsCrashserViewControllerDataSource methods

- (UIViewController *)toolsCrasherViewController:(ToolsCrasherViewController *)toolsCrasherViewController viewControllerAtIndex:(NSInteger)index {
    if (index < 3) {
        // crashes here because self and toolsCrasherViewController is nil
        return [self.carFactory carViewControllerWithNumberOfWheels:index];

        // using this instead of the factory does work
//        return [[CarViewController alloc] initWithNumberOfWheels:index make:@"Ford"];
    }

    return nil;
}

@end

NOTE: After downloading the project run pod install and open the workspace.
The included project is using the latest commit from Typhoon's master branch.

Repo Link: https://github.com/jervine10/ToolsCrash

@jasperblues
Copy link
Member

@jervine10 Sorry to hear that you're having trouble.

@drodriguez Can you take a look?

Meantime Typhoon now allows using the assembly interface itself as a factory. Here's an example:

- (id)couponListControllerWithStore:(VBStore*)store category:(NSString*)category;
{
    return [TyphoonDefinition withClass:[VBCouponListViewController class] initialization:^(TyphoonInitializer* initializer)
    {
        initializer.selector = @selector(initWithCouponDao:couponListView:store:category:);
        [initializer injectParameterWith:[_persistenceComponents couponDao]];
        [initializer injectParameterWith:[self couponListView]];
        [initializer injectParameterWith:store];
        [initializer injectParameterWith:category];
    } properties:^(TyphoonDefinition* definition)
    {
        definition.scope = TyphoonScopePrototype;
    }];
}

@alexgarbarev
Copy link
Contributor

@jasperblues that is right, but

  • I changed API from initialization:properties: to single injections: block
  • it is not a true factory because you cant relay on runtime attributes - only pass as is to parameter or property.

@jervine10
Copy link
Contributor Author

I forgot to mention that this crash only occurs when running on a device. The simulator works fine.

@jasperblues
Copy link
Member

@jervine10 That's not unusual for a memory related bug. Thanks for letting us know - will help to reproduce. . . speaking of that, is there any chance you could submit a failing test or project that shows the problem? This will help to resolve this issue more quickly.

@jervine10
Copy link
Contributor Author

I linked out to a project that you can download. Where can I submit one?

@jasperblues
Copy link
Member

@jervine10 Please send it to [email protected]

@jervine10
Copy link
Contributor Author

Thanks guys, I sent out the project.

@alexgarbarev
Copy link
Contributor

@jervine10 I saw your project linked at first message.
With new API you can solve your problem with another way, see steps:

  1. Define in Assembly method:
- (id)carViewControllerWithNumberOfWeels:(NSNumber *)numberOfWeels
{
    return [TyphoonDefinition withClass:[CarViewController class] injections:^(TyphoonDefinition *definition) {
       [definition injectInitializer:@selector(initWithNumberOfWheels:make:) withParameters:^(TyphoonMethod *initializer) {
           [initializer injectParameterWith:numberOfWeels];
           [initializer injectParameterWith:@"Ford"];
       }];
    }];
}
  1. Inject ToolsCrasherAssembly to your ToolsCrasherDataSource, for example as property.
    like this:
@interface ToolsCrasherDataSource : NSObject <ToolsCrasherViewControllerDataSource>
@property (nonatomic, strong) ToolsCrasherAssembly *assembly;
@end
  1. instead of calling [self.carFactory carViewControllerWithNumberOfWheels:index];
    try this:
[self.assembly carViewControllerWithNumberOfWeels:@(index)]

Hope that helps, will be glad to any feedback.

@drodriguez
Copy link
Contributor

I will try to have a look at the project from the top of the thread in a couple of hours. One question @jervine10: you say “crashes here because self and toolsCrasherViewController is nil”. Is “self” the instance of ToolsCrasherDataSource or some other “self” inside carViewControllerWithNumberOfWheels?

@alexgarbarev
Copy link
Contributor

The debug console says that self is nil.... so that's a huge red flag.

That can be because you run debug console on release configuration. Release configuration haven't debug information and symbols. Don't trust that you see in console when run on release - it is time when 2+2=nil or something else :-)

@alexgarbarev
Copy link
Contributor

To be sure, try to print self with NSLog instead of debug console usage

@jervine10
Copy link
Contributor Author

@drodriguez
self is an instance of ToolsCrasherDataSource. I've continued to investigate the problem in our app and have discovered a bit more info that might be useful. I'm using Typhoon's Factory Provider to create four different factories within our app. Just to see what's going on, I provided a custom implementation of the factory in question and was able to launch the app successfully.

When this happens, another factory is invoked and a few other objects are created. This factory does not blow up. The difference between the factory that does work and the factory that does not work is in the assembly definition. In the successful factory, I provide my own factoryMethods in the assembly, in the unsuccessful I let Typhoon do its magic for me.

So out of my 4 factories, the app crashes on all of them except for the one where I provide the factoryMethod bodies myself.

Here's a snippet of some of my definitions:

Fails:

- (id)weatherStripViewControllerFactory {
    return [TyphoonFactoryProvider withProtocol:@protocol(AWWeatherStripViewControllerFactory) dependencies:^(TyphoonDefinition *definition) {
        [definition injectProperty:@selector(stripViewCellViewControllerFactory) withDefinition:[self stripViewCellViewControllerFactory]];
        [definition injectProperty:@selector(animationViewController) withDefinition:[self.navigationAssembly animationViewController]];
        [definition injectProperty:@selector(pullToRefreshGestureHandler) withDefinition:[self.navigationAssembly pullToRefreshGestureHandler]];
        [definition injectProperty:@selector(scrollListenerController) withDefinition:[self.necessaryEvilsAssembly weatherStripViewControllerScrollListener]];
        [definition injectProperty:@selector(stripViewController) withDefinition:[self verticalStripViewController]];
    } returns:[AWWeatherStripViewController class]];
}

Successful:

- (id)stripViewCellViewControllerFactory {
    return [TyphoonFactoryProvider withProtocol:@protocol(AWStripViewCellViewControllerFactory) dependencies:^(TyphoonDefinition *definition) {
        [definition injectProperty:@selector(refreshDelegate) withDefinition:[self.navigationAssembly refreshController]];
        [definition injectProperty:@selector(presentationController) withDefinition:[self.navigationAssembly presentationController]];
    } factories:^(TyphoonAssistedFactoryDefinition *definition) {
        [definition factoryMethod:@selector(landingScreenViewControllerWithUserLocation:resizeDelegate:)
                             body:^id (TyphoonAssistedFactoryBase<AWStripViewCellViewControllerFactory> *factory,
                                       AWUserLocation *userLocation,
                                       id<AWResizableViewControllerDelegate> resizeDelegate) {

                                 id componentFactory = factory.componentFactory;
                                 id<AWDailyDetailsPagingViewControllerFactory> dailyDetailsPagingViewControllerFactory = [componentFactory dailyDetailsPagingViewControllerFactory];
                                 return [[AWLandingScreenViewController alloc] initWithResizeDelegate:resizeDelegate
                                                                               presentationController:factory.presentationController
                                                                                      refreshDelegate:factory.refreshDelegate
                                                                                         userLocation:userLocation
                                                              dailyDetailsPagingViewControllerFactory:dailyDetailsPagingViewControllerFactory];

                             }];
    }];
}

@jervine10
Copy link
Contributor Author

@alexgarbarev
Thanks for that, we actually talked about that possibly being the reason why things were looking funny in the debug console. It completely slipped my mind =)

@jervine10
Copy link
Contributor Author

Alright, so I updated all of my factory definitions to provide method bodies manually and everything is working as expected again. Is this issue affecting everyone who is using FactoryProviders without providing any custom method bodies through the factoryMethod:body: method of TyphoonAssistedFactoryDefinition?

@drodriguez
Copy link
Contributor

@jervine10, can you try the diff at https://gist.github.com/drodriguez/970cdcb0517b4fdfc954 and tell me if that works in your real project? I cannot understand why it was working and no longer works, but that seems to stop the crashing.

@jervine10
Copy link
Contributor Author

@drodriguez
I tried your diff with our project and it seems like everything is working as expected, thanks!

@drodriguez
Copy link
Contributor

I will apply the change to the master branch. Until we release the next version you must use the :head version to have the fix.

Thanks for the help and the minimal example. It would have been impossible or very difficult without those.

drodriguez added a commit to drodriguez/Typhoon that referenced this issue Mar 21, 2014
Compiling with Xcode 5.1, with Release configuration and running on
the device, it seems that the returned value was invalid when
reaching user code, and caused crashes.

See appsquickly#200.
@drodriguez
Copy link
Contributor

Closing.

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

No branches or pull requests

4 participants