views:

283

answers:

3

I have a core data application with two NIBs, the main window with a pair of NSTableViews and a form for adding content. The form has an NSTextView and an NSTextField. I have two entities in core data, and manage the content with two NSArrayControllers. With one NSArrayController, the following code in my AppDelegate works fine for adding content:

    id o = [bookController newObject];
    [o setValue:@"Drafts" forKey:@"bookName"];
    [o setValue:0 forKey:@"sortOrder"];
    [bookController addObject:o];

But, this code in my AppController class always returns null:

NSObject *o = [chapterArrayController newObject];

[o setValue:contentOfchapter forKey:@"chapterText"];
[o setValue:chapterTitleString forKey:@"chapterTitle"];
[o setValue:@"Drafts" forKey:@"bookChapter"];
NSLog(@"Where is the object?: %@", o);
[chapterArrayController addObject:o];

It seems like the chapterArrayController is not connected to the Chapter entity in core data, but the bindings in IB are correct. I'm thinking that this has something to do with the multiple nibs, but I'm kind of at a loss here.

Any pointers in the right direction are appreciated.

Thanks.

Update 2: I created a class named JBAddChapter, which looks like this:

JBAddChapter.h

#import <Cocoa/Cocoa.h>


@interface JBAddChapter : NSObject {

IBOutlet NSArrayController *bookController;
IBOutlet NSArrayController *chapterArrayController;

}
- (IBAction)testArrayControllers:(id)sender;
+ (void)getChapterData:(NSString *)passedChapterTitle withChapterText:(NSString *)passedChapterText;
- (void)standAloneTestArrayControllers;

@end

JBAddChapter.m

#import "JBAddChapter.h"


@implementation JBAddChapter


+ (void)getChapterData:(NSString *)passedChapterTitle withChapterText:(NSString *)passedChapterText;
{
    [[self alloc] standAloneTestArrayControllers];
}


- (IBAction)testArrayControllers:(id)sender
{
    [self standAloneTestArrayControllers];
}

- (void)standAloneTestArrayControllers
{
    [chapterArrayController fetchWithRequest:nil merge:NO error:nil];
    NSLog(@"1. chapterArrayController %@", chapterArrayController);

    NSArray *a = [chapterArrayController arrangedObjects];
    NSLog(@"2. NSArray a = %@", a);

    NSUInteger numberOfMenuItems = [a count];
    NSLog(@"3. Count of items in array: %d", numberOfMenuItems);

    [bookController fetchWithRequest:nil merge:NO error:nil];
    NSLog(@"1. bookController %@", chapterArrayController);

    NSArray *b = [chapterArrayController arrangedObjects];
    NSLog(@"2. NSArray b = %@", b);

    NSUInteger newNumberOfMenuItems = [a count];
    NSLog(@"3. Count of items in array: %d", newNumberOfMenuItems);
}
@end

I've created two buttons in IB in my main window, and hooked one up to the testArrayControllers IBAction above. The other button I hook up to AppController and this IBAction:

- (IBAction)testArrayControllers:(id)sender
{
    [JBAddChapter getChapterData:nil withChapterText:nil];
}

If I call the standAloneTestArrayControllers from JBAddChapter's IBAction, everything works fine. If I call that same method from AppController using the a factory method in JBAddChapter, I the array controllers are nil.

2010-01-07 06:15:36.971 Scout[3881:a0f] 1. chapterArrayController <NSArrayController: 0x200060b40>[entity: Chapter, number of selected objects: 1]
2010-01-07 06:15:36.972 Scout[3881:a0f] 2. NSArray a = (
loads of stuff
)
2010-01-07 06:15:36.973 Scout[3881:a0f] 3. Count of items in array: 9
2010-01-07 06:15:36.974 Scout[3881:a0f] 1. bookController <NSArrayController: 0x200060b40>[entity: Chapter, number of selected objects: 1]
2010-01-07 06:15:36.978 Scout[3881:a0f] 2. NSArray b = (
loads of stuff
)
2010-01-07 06:15:36.979 Scout[3881:a0f] 3. Count of items in array: 8
2010-01-07 06:15:38.402 Scout[3881:a0f] 1. chapterArrayController (null)
2010-01-07 06:15:38.402 Scout[3881:a0f] 2. NSArray a = (null)
2010-01-07 06:15:38.402 Scout[3881:a0f] 3. Count of items in array: 0
2010-01-07 06:15:38.403 Scout[3881:a0f] 1. bookController (null)
2010-01-07 06:15:38.403 Scout[3881:a0f] 2. NSArray b = (null)
2010-01-07 06:15:38.403 Scout[3881:a0f] 3. Count of items in array: 0

So, why would the array controllers be returning nil from one method, but not the other? All bindings in IB are correct, as far as I can tell.

A: 

Is chapterArrayController nil when you ask it for an object?

You say you have two nibs... is chapterArrayController instantiated in that second nib? If so, has the nib been loaded by the time newObject is called?

MyztikJenz
Well, I'm forcing chapterArrayController to load with: [chapterArrayController fetchWithRequest:nil merge:NO error:nil];and I have chapterArrayController bound to an NSArrayController object with bindings in both nibs. NSArray a = [chapterArrayController arrangedObjects] int b = [a count]; NSLog (@"items in array: %d", b);The int b above is 0 every time, and if I try to do this: NSObject *o = [chapterArrayController newObject]; NSLog(@"Where is the object: %@", o);NSLog will log "null".
Jon Buys
If chapterArrayController is nil when fetchWithRequest:merge:error: is called, nothing is going to happen. I assume chapterArrayController is an IBOutlet ivar, yes? Try putting: NSLog( @"controller: %@", chapterArrayController ); right after your fetch request. I'm thinking it'll return nil.Can you show the code that's loading the second nib (the one that chapterArrayController is referencing)?
MyztikJenz
MyztikJenz: If `chapterArrayController` were `nil`, asking it for `arrangedObjects` would return `nil`. Asking `nil` for `count` would return 0. He says `count` returns non-zero; therefore, `chapterArrayController` is not `nil`.
Peter Hosey
Peter: My chapterArrayController returns (null) when sent to NSLog. I'm just at a loss of why its doing that. `NSLog(@"Chapter Array Controller %@", chapterArrayController);`
Jon Buys
Are you positive `chapterArrayController` is hooked up in the nib? Your code looks like it should work fine. The fact that you can click the button to fire `saveChapter:` says the nib has loaded but why that ivar is nil is curious.
MyztikJenz
Yep, the bindings are correct in IB. I might just take this another direction and use the managed object context to add the data and bypass the array controller.
Jon Buys
Jon Buys: OK, that doesn't reconcile with what you said about `count` returning values “above is 0 every time”. `[[nil arrangedObjects] count]` == 0 on every currently-supported architecture. Where did you attempt to log `chapterArrayController`? Perhaps you logged it before you loaded the nib. At any rate, whether your controller outlet just isn't hooked up (and is therefore `nil`) or something else is wrong, you should find and fix this problem before trying to do something else.
Peter Hosey
Ok, I've updated my question with a little more information.
Jon Buys
A: 
[[self alloc] standAloneTestArrayControllers];

Never ever do this. Always initialize the instance by sending it an init (or initWithSomethingElse:, depending on the initializers the class provides) message. If you send any other message to a freshly-allocated-but-not-yet-initialized instance, the results are undefined and almost certainly wrong.

[bookController fetchWithRequest:nil merge:NO error:nil];
NSLog(@"1. bookController %@", chapterArrayController);

Are you sure you don't mean to log bookController here, not chapterArrayController?

If I call the standAloneTestArrayControllers from JBAddChapter's IBAction, everything works fine. If I call that same method from AppController using the a factory method in JBAddChapter, I the array controllers are nil.

Because you haven't loaded the nib yet. Neither have you loaded the chapter object from a nib, nor the chapter object loaded a nib itself, nor anything explicitly (as in setChapterArrayController:) given the chapter object an array controller to hold on to.

It works from the action method because in that context, you have loaded the nib.

So, why would the array controllers be returning nil from one method, but not the other?

The array controllers are not returning nil; they don't exist. (Moreover, functions and methods return values, not objects or classes.) The variables you've created to hold array controllers hold nil because you have not put any array controllers there.

Assuming that JBAddChapter is meant to own the add-chapter sheet, it should load the nib (or instantiate a window controller) in its init method, and you should either create the object from your main window's owner (and have that object own the add-chapter object, and only ever use the add-chapter object that it owns) or make the add-chapter object a singleton. The former is easier.

And, of course, when you create this object or any other, you must always use the alloc + init/initWith… pair (or provided autoreleasing convenience method); otherwise, as now, your object will not load its nib because you never sent it an init message.

Peter Hosey
A: 

Guess I'll answer my own question here. Something was wrong in my NIB, and once I deleted my array controllers, and reconnected them in IB, everything just automagically worked again.

Jon Buys
You know, I've seen that before. The only other option I found that worked (and only some of the time) is to save nibs as xibs, or vice versa. Bug in IB? I've never been able to reliably reproduce it.
MyztikJenz