views:

211

answers:

1

I am writing an application that needs to interfaces with different backend systems. I decided to use a Protocol in order to abstract my backend Classes. I created a nib called LoginViewController that contains an "External Object" reference of type "NSObject", and wired it to the systemDelegate outlet in my LoginViewController.

@interface LoginViewController : UIViewController {

}

@property (nonatomic, retain) IBOutlet UITextField *usernameTextView;
@property (nonatomic, retain) IBOutlet UIImageView *captchaImageView;
@property (nonatomic, retain) IBOutlet UITextField *captchaTextView;
@property (nonatomic, retain) IBOutlet NSObject <BackEndSystemDelegate> *systemDelegate;

- (IBAction) submitCaptcha:(id) sender;
- (IBAction)dismissKeyboard: (id)sender;
- (IBAction) animateViewUp: (id) sender;
- (IBAction) animateViewDown: (id) sender;
- (void) animateViewOnYAxis: (int) offset;
- (void) loadCaptchaImage;

@end

I instanciate the LoginViewController in my application delegate, then try to load the nib with the external object reference. My code calls the loadNibNamed and crashes without a stack trace. I do not reach the NSLog statements after the invocation:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    
    NSObject <BackEndSystemDelegate> *systemDelegate = [[ACMEBackEndSystemDelegate alloc] init];


    // Init LoginView, and load nib with systemDelegate
    self.viewController = [[LoginViewController alloc] init];
    NSDictionary *proxies = [NSDictionary dictionaryWithObject:systemDelegate forKey:@"systemDelegate"];
    NSDictionary *options = [NSDictionary dictionaryWithObject:proxies forKey:UINibExternalObjects];

    NSArray *toplevelobjects = [[NSBundle mainBundle] loadNibNamed:@"LoginViewController" 
                                                             owner:self.viewController 
                                                           options:options];

    if (toplevelobjects) {
        NSLog(@"toplevelobjects is nil");
    } else {
        NSLog(@"toplevelobjects count %d", [toplevelobjects count]);
    }


    NSLog(@"Controller: %@, View: %@", viewController, viewController.view);

    // Add the view controller's view to the window and display.
    [window addSubview:viewController.view];
    [window makeKeyAndVisible];

    return YES;
}

I am at a loss trying to figure this out. Any help would be appreciated.

Thanks, J Garcia

+1  A: 

A few comments:

  • Generally you refer to a delegate object using the pattern id<SomeProtocolName> and not NSObject<SomeProtocolName>

  • You alloc your systemDelegate variable but never release it. This is a memory leak.

  • If the ACMEBackEndSystemDelegate class implements the BackEndSystemDelegate protocol, it is sufficient to allocate it like ACMEBackEndSystemDelegate* systemDelegate = [[ACMEBackEndSystemDelegate alloc] init];

Now, as for your crash, you said there's a crash on this line:

NSArray *toplevelobjects = [[NSBundle mainBundle] loadNibNamed:@"LoginViewController" 
                                                             owner:self.viewController 
                                                           options:options];

I assume you have a .xib called LoginViewController.xib. Open it up. That is the class type set for "File's Owner"? Is it LoginViewController? If not, set it. Now check the view outlet property. Is it set to a UIView in the .xib (perhaps a top-level UIView object)? If not, set it.

Shaggy Frog
I followed your advice by adding the autorelease to ACMEBackEndSystemDelegate, verifying all the wires, and confirming the file owner is LoginViewController. The app still crashes. I noticed that it loads, if I remove the "External Object" from the nib. Re-adding and wiring causes the same problem.
Jesus Garcia
The problem is that "External Object". You are using it as a way for the `LoginViewController` to know about a `BackEndSystemDelegate`? Just use a `@property` instead; create your `BackEndSystemDelegate` in your app delegate (as you are now), and then just set the property when you initialize your `LoginViewController`. I've never had a reason to use "External Objects" in iOS applications yet.
Shaggy Frog
You make a good point. I was using an External Object, since the LoginViewController was calling a delegate method on viewDidLoad. I made the delegate a property as suggested, and called the delegate method after the view controller was instantiated. I guess I was making the problem harder than it need to be. Thanks a bunch for the assistance.
Jesus Garcia