Circular initialization of controllers in NIB file

My application is an installer whose main window consists of a few
controls and an area (NSBox) on which one of several "panels" is
displayed. As the user progresses through the install, theses panels
(custom NSViews) are swapped in and out of the main window. The logic
for some of the panels can be quite complicated so I've implemented a
unique controller for each panel which holds all the logic pertaining to it.

The main controller holds a pointer to each of the panel controllers so
that it handle swapping the panels. Some of the panel controllers need
to communicate with the main window controller as well to do things like
set the enabled status of the "Continue" and "Go Back" buttons on the
main window.

Thus, the main window controller has an IBOutlet for a panel controller,
and the panel controller has an IBOutlet for the main window controller.
These are both assigned in Interface Builder. After loading the NIB
file, the IBOutlet pointing to the main window controller from the panel
controller is never set (value is nil). I think this must be because
there is a circular dependency during initialization of the NIB file.

Is this expected behaviour? Can I do something to force the
initialization of both outlets, or do I just need to work around this in
my code? (Working around this shouldn't be difficult, but I'm new to
Cocoa, so I just want to make sure that I correctly understand what is
going on before working around the issue.)

> Thus, the main window controller has an IBOutlet for a panel
> controller, and the panel controller has an IBOutlet for the main
> window controller. These are both assigned in Interface Builder.
> After loading the NIB file, the IBOutlet pointing to the main window
> controller from the panel controller is never set (value is nil). I
> think this must be because there is a circular dependency during
> initialization of the NIB file.

No, that's not the problem. Most likely you have chosen a name for
your variable for which there is an unrelated -setSomeName: method;
that method will be being called during initialisation, rather than
just setting the variable directly.

> On 3 Jan 2008, at 18:16, Nathan Auch wrote:
> >> Thus, the main window controller has an IBOutlet for a panel
>> controller, and the panel controller has an IBOutlet for the main
>> window controller. These are both assigned in Interface Builder.
>> After loading the NIB file, the IBOutlet pointing to the main window
>> controller from the panel controller is never set (value is nil). I
>> think this must be because there is a circular dependency during
>> initialization of the NIB file.>
> No, that's not the problem. Most likely you have chosen a name for
> your variable for which there is an unrelated -setSomeName: method;
> that method will be being called during initialisation, rather than
> just setting the variable directly.

This doesn't appear to be the case, the name of the variable was
"main_controller", I've changed it to "my_main_controller" and
reconnected the outlets in IB but I'm still seeing the same behaviour.
In general, are there any best practices for choosing variable names to
avoid the situation you describe?

What's the best way to go about debugging issues with NIB file loading?
Can I break on something specific in the debugger? In the mean time,
I'll see if I can get a small repro together, and post the code to this
list.

Outlet cycles are legal and supported. When are you expecting to see a
value for the instance variable and finding it to be nil? Are you
checking during one of your init methods? During one of your setters?
In the awakeFromNib method? The outlets of your objects won't be
established until just before awakeFromNib is invoked on your object.

Jon Hess

On Jan 3, 2008, at 11:00 AM, Nathan Auch wrote:

> Alastair Houghton wrote:>> On 3 Jan 2008, at 18:16, Nathan Auch wrote:
>> >>> Thus, the main window controller has an IBOutlet for a panel
>>> controller, and the panel controller has an IBOutlet for the main
>>> window controller. These are both assigned in Interface Builder.
>>> After loading the NIB file, the IBOutlet pointing to the main
>>> window controller from the panel controller is never set (value is
>>> nil). I think this must be because there is a circular dependency
>>> during initialization of the NIB file.>>
>> No, that's not the problem. Most likely you have chosen a name for
>> your variable for which there is an unrelated -setSomeName: method;
>> that method will be being called during initialisation, rather than
>> just setting the variable directly.>
> This doesn't appear to be the case, the name of the variable was
> "main_controller", I've changed it to "my_main_controller" and
> reconnected the outlets in IB but I'm still seeing the same behaviour.
> In general, are there any best practices for choosing variable names
> to
> avoid the situation you describe?
>
> What's the best way to go about debugging issues with NIB file
> loading?
> Can I break on something specific in the debugger? In the mean time,
> I'll see if I can get a small repro together, and post the code to
> this
> list.>>
>> Kind regards,
>>
>> Alastair.
>>
>> --
>> http://alastairs-place.net
>>
>>
>>
>>
>>

I'm checking in the awakeFromNib method. I've recreated my application's
setup in a small test program, but I can't reproduce the issue that I am
seeing. I'll keep investigating and post back if/when I find a solution.

Thanks,
-Nathan

Jonathan Hess wrote:

> Hey Nathan?
>
> Outlet cycles are legal and supported. When are you expecting to see a
> value for the instance variable and finding it to be nil? Are you
> checking during one of your init methods? During one of your setters?
> In the awakeFromNib method? The outlets of your objects won't be
> established until just before awakeFromNib is invoked on your object.
>
> Jon Hess
>
> On Jan 3, 2008, at 11:00 AM, Nathan Auch wrote:
> >> Alastair Houghton wrote:>>> On 3 Jan 2008, at 18:16, Nathan Auch wrote:
>>> >>>> Thus, the main window controller has an IBOutlet for a panel
>>>> controller, and the panel controller has an IBOutlet for the main
>>>> window controller. These are both assigned in Interface Builder.
>>>> After loading the NIB file, the IBOutlet pointing to the main
>>>> window controller from the panel controller is never set (value is
>>>> nil). I think this must be because there is a circular dependency
>>>> during initialization of the NIB file.>>>
>>> No, that's not the problem. Most likely you have chosen a name for
>>> your variable for which there is an unrelated -setSomeName: method;
>>> that method will be being called during initialisation, rather than
>>> just setting the variable directly.>>
>> This doesn't appear to be the case, the name of the variable was
>> "main_controller", I've changed it to "my_main_controller" and
>> reconnected the outlets in IB but I'm still seeing the same behaviour.
>> In general, are there any best practices for choosing variable names to
>> avoid the situation you describe?
>>
>> What's the best way to go about debugging issues with NIB file loading?
>> Can I break on something specific in the debugger? In the mean time,
>> I'll see if I can get a small repro together, and post the code to this
>> list.>>>
>>> Kind regards,
>>>
>>> Alastair.
>>>
>>> --
>>> http://alastairs-place.net
>>>
>>>
>>>
>>>
>>> >
>

I dealt with this after trying to reduce nib sizes and make various
parts of my program reusable. I found that if I set File's Owner to
my "Main Controller" in these "sub-controller" nib files and then add
a new NSObject that I set the class in the inspector window to my "sub-
controller." This keeps the circular initialization from occurring,
while allowing cross communication between the nib files.

Hope this helps.

On Jan 3, 2008, at 10:16 AM, Nathan Auch wrote:

> My application is an installer whose main window consists of a few
> controls and an area (NSBox) on which one of several "panels" is
> displayed. As the user progresses through the install, theses panels
> (custom NSViews) are swapped in and out of the main window. The
> logic for some of the panels can be quite complicated so I've
> implemented a unique controller for each panel which holds all the
> logic pertaining to it.
>
> The main controller holds a pointer to each of the panel controllers
> so that it handle swapping the panels. Some of the panel controllers
> need to communicate with the main window controller as well to do
> things like set the enabled status of the "Continue" and "Go Back"
> buttons on the main window.
>
> Thus, the main window controller has an IBOutlet for a panel
> controller, and the panel controller has an IBOutlet for the main
> window controller. These are both assigned in Interface Builder.
> After loading the NIB file, the IBOutlet pointing to the main window
> controller from the panel controller is never set (value is nil). I
> think this must be because there is a circular dependency during
> initialization of the NIB file.
>
> Is this expected behaviour? Can I do something to force the
> initialization of both outlets, or do I just need to work around
> this in my code? (Working around this shouldn't be difficult, but
> I'm new to Cocoa, so I just want to make sure that I correctly
> understand what is going on before working around the issue.)
>
> Thanks,
> -Nathan

> Alastair Houghton wrote:
> >> No, that's not the problem. Most likely you have chosen a name for
>> your variable for which there is an unrelated -setSomeName: method;
>> that method will be being called during initialisation, rather than
>> just setting the variable directly.>
> This doesn't appear to be the case, the name of the variable was
> "main_controller", I've changed it to "my_main_controller" and
> reconnected the outlets in IB but I'm still seeing the same
> behaviour. In general, are there any best practices for choosing
> variable names to avoid the situation you describe?

Not really, no. You just need to be aware of the problem. I suppose
you could tack "Outlet" onto the end of all of your outlets' names
(for instance), but I don't know that there is any convention or even
that doing this kind of thing is terribly common.

If it isn't the outlet name clashing with a -setMyOutletName: method,
then check that:

1. They really are connected in IB. It's easy to forget to connect
things up, or to accidentally disconnect them.

2. The object in question really has been created, and hasn't e.g.
returned nil from its -init method. (An NSLog() in the -init
implementation should be sufficient...)

3. You aren't accessing the member variable from a point before nib
file has been completely loaded. For instance, trying to use an
outlet from an -init method of an object that was loaded from the same
nib file won't work reliably. In that case, you should probably
implement -awakeFromNib on your object to do whatever you need to do.

I may be confused, but It sounds like you may be misunderstanding the
role of File's Owner. Before was it the case that you had a blue cube
for "Controller" and "Sub-Controller" in each nib? If that's the case,
then you were actually creating many instance of "Controller" and
that's why you weren't seeing the connections you expected on your
actual master controller.

Good Luck -
Jon Hess

On Jan 4, 2008, at 12:46 AM, Jim Murry wrote:

> I dealt with this after trying to reduce nib sizes and make various
> parts of my program reusable. I found that if I set File's Owner
> to my "Main Controller" in these "sub-controller" nib files and then
> add a new NSObject that I set the class in the inspector window to
> my "sub-controller." This keeps the circular initialization from
> occurring, while allowing cross communication between the nib files.
>
> Hope this helps.
>
> On Jan 3, 2008, at 10:16 AM, Nathan Auch wrote:
> >> My application is an installer whose main window consists of a few
>> controls and an area (NSBox) on which one of several "panels" is
>> displayed. As the user progresses through the install, theses
>> panels (custom NSViews) are swapped in and out of the main window.
>> The logic for some of the panels can be quite complicated so I've
>> implemented a unique controller for each panel which holds all the
>> logic pertaining to it.
>>
>> The main controller holds a pointer to each of the panel
>> controllers so that it handle swapping the panels. Some of the
>> panel controllers need to communicate with the main window
>> controller as well to do things like set the enabled status of the
>> "Continue" and "Go Back" buttons on the main window.
>>
>> Thus, the main window controller has an IBOutlet for a panel
>> controller, and the panel controller has an IBOutlet for the main
>> window controller. These are both assigned in Interface Builder.
>> After loading the NIB file, the IBOutlet pointing to the main
>> window controller from the panel controller is never set (value is
>> nil). I think this must be because there is a circular dependency
>> during initialization of the NIB file.
>>
>> Is this expected behaviour? Can I do something to force the
>> initialization of both outlets, or do I just need to work around
>> this in my code? (Working around this shouldn't be difficult, but
>> I'm new to Cocoa, so I just want to make sure that I correctly
>> understand what is going on before working around the issue.)
>>
>> Thanks,
>> -Nathan

> On 3 Jan 2008, at 18:59, Nathan Auch wrote:
> >> Alastair Houghton wrote:
>> >>> No, that's not the problem. Most likely you have chosen a name for
>>> your variable for which there is an unrelated -setSomeName: method;
>>> that method will be being called during initialisation, rather than
>>> just setting the variable directly.>>
>> This doesn't appear to be the case, the name of the variable was
>> "main_controller", I've changed it to "my_main_controller" and
>> reconnected the outlets in IB but I'm still seeing the same
>> behaviour. In general, are there any best practices for choosing
>> variable names to avoid the situation you describe?>
> Not really, no. You just need to be aware of the problem. I suppose
> you could tack "Outlet" onto the end of all of your outlets' names
> (for instance), but I don't know that there is any convention or even
> that doing this kind of thing is terribly common.
>
> If it isn't the outlet name clashing with a -setMyOutletName: method,
> then check that:
>
> 1. They really are connected in IB. It's easy to forget to connect
> things up, or to accidentally disconnect them.

I've checked and doubled checked this, in fact, I've even unset and
reset the connections a few times just to make sure.

>
> 2. The object in question really has been created, and hasn't e.g.
> returned nil from its -init method. (An NSLog() in the -init
> implementation should be sufficient...)

NSLog in the -init implementations of the relevant classes indicates
they are indeed being created.

>
> 3. You aren't accessing the member variable from a point before nib
> file has been completely loaded. For instance, trying to use an
> outlet from an -init method of an object that was loaded from the same
> nib file won't work reliably. In that case, you should probably
> implement -awakeFromNib on your object to do whatever you need to do.

I am not attempting to use the outlet until after awakeFromNib has been
called. In fact, the problem is easily reproducible in my application by
adding an assertion that the outlet in question is not NULL to the
-awakeFromNib method. Unfortunately, as I said before, I'm not able to
reproduce this scenario outside of my application.

Do NIB files corrupt often/easily? I'm pretty sure this NIB has been
around for awhile (10.2 days?) and has been migrated through several
versions of IB since then. Is there some tool that can verify the
integrity of a NIB file?

Are you sure you're only creating one instance of your master
controller? Do you have only one nib? Or many? If you have many, is
the master controller, the file's owner of the other nibs?

Jon Hess

On Jan 4, 2008, at 12:57 PM, Nathan Auch wrote:

> Alastair Houghton wrote:>> On 3 Jan 2008, at 18:59, Nathan Auch wrote:
>> >>> Alastair Houghton wrote:
>>> >>>> No, that's not the problem. Most likely you have chosen a name
>>>> for your variable for which there is an unrelated -setSomeName:
>>>> method; that method will be being called during initialisation,
>>>> rather than just setting the variable directly.>>>
>>> This doesn't appear to be the case, the name of the variable was
>>> "main_controller", I've changed it to "my_main_controller" and
>>> reconnected the outlets in IB but I'm still seeing the same
>>> behaviour. In general, are there any best practices for choosing
>>> variable names to avoid the situation you describe?>>
>> Not really, no. You just need to be aware of the problem. I
>> suppose you could tack "Outlet" onto the end of all of your
>> outlets' names (for instance), but I don't know that there is any
>> convention or even that doing this kind of thing is terribly common.
>>
>> If it isn't the outlet name clashing with a -setMyOutletName:
>> method, then check that:
>>
>> 1. They really are connected in IB. It's easy to forget to connect
>> things up, or to accidentally disconnect them.> I've checked and doubled checked this, in fact, I've even unset and
> reset the connections a few times just to make sure.>>
>> 2. The object in question really has been created, and hasn't e.g.
>> returned nil from its -init method. (An NSLog() in the -init
>> implementation should be sufficient...)> NSLog in the -init implementations of the relevant classes indicates
> they are indeed being created.>>
>> 3. You aren't accessing the member variable from a point before nib
>> file has been completely loaded. For instance, trying to use an
>> outlet from an -init method of an object that was loaded from the
>> same nib file won't work reliably. In that case, you should
>> probably implement -awakeFromNib on your object to do whatever you
>> need to do.> I am not attempting to use the outlet until after awakeFromNib has
> been called. In fact, the problem is easily reproducible in my
> application by adding an assertion that the outlet in question is
> not NULL to the -awakeFromNib method. Unfortunately, as I said
> before, I'm not able to reproduce this scenario outside of my
> application.
>
> Do NIB files corrupt often/easily? I'm pretty sure this NIB has been
> around for awhile (10.2 days?) and has been migrated through several
> versions of IB since then. Is there some tool that can verify the
> integrity of a NIB file?
>
> Thanks,
> -Nathan

I had realized that I was creating multiple instances of "Controller"
and I know that I was not using File's Owner properly for this
particular problem. I meant to state that I knew that this was not
the "correct way", but it did work while trying to reduce nib size,
make controllers portable while learning how to communicate between
nib's and eliminate the circular initialization.

Thanks Jim

On Jan 4, 2008, at 11:44 AM, Jonathan Hess wrote:

> Hey Jim -
>
> I may be confused, but It sounds like you may be misunderstanding
> the role of File's Owner. Before was it the case that you had a blue
> cube for "Controller" and "Sub-Controller" in each nib? If that's
> the case, then you were actually creating many instance of
> "Controller" and that's why you weren't seeing the connections you
> expected on your actual master controller.
>
> Good Luck -
> Jon Hess
>
> On Jan 4, 2008, at 12:46 AM, Jim Murry wrote:
> >> I dealt with this after trying to reduce nib sizes and make various
>> parts of my program reusable. I found that if I set File's Owner
>> to my "Main Controller" in these "sub-controller" nib files and
>> then add a new NSObject that I set the class in the inspector
>> window to my "sub-controller." This keeps the circular
>> initialization from occurring, while allowing cross communication
>> between the nib files.
>>
>> Hope this helps.
>>
>> On Jan 3, 2008, at 10:16 AM, Nathan Auch wrote:
>> >>> My application is an installer whose main window consists of a few
>>> controls and an area (NSBox) on which one of several "panels" is
>>> displayed. As the user progresses through the install, theses
>>> panels (custom NSViews) are swapped in and out of the main window.
>>> The logic for some of the panels can be quite complicated so I've
>>> implemented a unique controller for each panel which holds all the
>>> logic pertaining to it.
>>>
>>> The main controller holds a pointer to each of the panel
>>> controllers so that it handle swapping the panels. Some of the
>>> panel controllers need to communicate with the main window
>>> controller as well to do things like set the enabled status of the
>>> "Continue" and "Go Back" buttons on the main window.
>>>
>>> Thus, the main window controller has an IBOutlet for a panel
>>> controller, and the panel controller has an IBOutlet for the main
>>> window controller. These are both assigned in Interface Builder.
>>> After loading the NIB file, the IBOutlet pointing to the main
>>> window controller from the panel controller is never set (value is
>>> nil). I think this must be because there is a circular dependency
>>> during initialization of the NIB file.
>>>
>>> Is this expected behaviour? Can I do something to force the
>>> initialization of both outlets, or do I just need to work around
>>> this in my code? (Working around this shouldn't be difficult, but
>>> I'm new to Cocoa, so I just want to make sure that I correctly
>>> understand what is going on before working around the issue.)
>>>
>>> Thanks,
>>> -Nathan>

I'm positive I'm only creating one instance of my master controller. I
have only one NIB file and it is loaded automatically when my
application starts up.

-Nathan

Jonathan Hess wrote:

> Hey Nathan -
>
> Are you sure you're only creating one instance of your master
> controller? Do you have only one nib? Or many? If you have many, is
> the master controller, the file's owner of the other nibs?
>
> Jon Hess
>
> On Jan 4, 2008, at 12:57 PM, Nathan Auch wrote:
> >> Alastair Houghton wrote:>>> On 3 Jan 2008, at 18:59, Nathan Auch wrote:
>>> >>>> Alastair Houghton wrote:
>>>> >>>>> No, that's not the problem. Most likely you have chosen a name
>>>>> for your variable for which there is an unrelated -setSomeName:
>>>>> method; that method will be being called during initialisation,
>>>>> rather than just setting the variable directly.>>>>
>>>> This doesn't appear to be the case, the name of the variable was
>>>> "main_controller", I've changed it to "my_main_controller" and
>>>> reconnected the outlets in IB but I'm still seeing the same
>>>> behaviour. In general, are there any best practices for choosing
>>>> variable names to avoid the situation you describe?>>>
>>> Not really, no. You just need to be aware of the problem. I
>>> suppose you could tack "Outlet" onto the end of all of your outlets'
>>> names (for instance), but I don't know that there is any convention
>>> or even that doing this kind of thing is terribly common.
>>>
>>> If it isn't the outlet name clashing with a -setMyOutletName:
>>> method, then check that:
>>>
>>> 1. They really are connected in IB. It's easy to forget to connect
>>> things up, or to accidentally disconnect them.>> I've checked and doubled checked this, in fact, I've even unset and
>> reset the connections a few times just to make sure.>>>
>>> 2. The object in question really has been created, and hasn't e.g.
>>> returned nil from its -init method. (An NSLog() in the -init
>>> implementation should be sufficient...)>> NSLog in the -init implementations of the relevant classes indicates
>> they are indeed being created.>>>
>>> 3. You aren't accessing the member variable from a point before nib
>>> file has been completely loaded. For instance, trying to use an
>>> outlet from an -init method of an object that was loaded from the
>>> same nib file won't work reliably. In that case, you should
>>> probably implement -awakeFromNib on your object to do whatever you
>>> need to do.>> I am not attempting to use the outlet until after awakeFromNib has
>> been called. In fact, the problem is easily reproducible in my
>> application by adding an assertion that the outlet in question is not
>> NULL to the -awakeFromNib method. Unfortunately, as I said before,
>> I'm not able to reproduce this scenario outside of my application.
>>
>> Do NIB files corrupt often/easily? I'm pretty sure this NIB has been
>> around for awhile (10.2 days?) and has been migrated through several
>> versions of IB since then. Is there some tool that can verify the
>> integrity of a NIB file?
>>
>> Thanks,
>> -Nathan>
>

Just a note for anybody searching the archives of this list. After
deleting and recreating the main window and the main controller in my
NIB file and doing a "Clean All Targets" in XCode the problem has
disappeared, my outlets now get set as expected.

I wish I knew precisely what the problem was, but I suspect something in
the NIB file was just in a wacky state. I'll ask this again just in case
it was missed: Does anybody know of a tool that can validate a NIB file
and check for any possible inconsistencies or corruptions?

Thank you everyone for your help,
-Nathan Auch

Nathan Auch wrote:

> Hi Jon,
>
> I'm positive I'm only creating one instance of my master controller. I
> have only one NIB file and it is loaded automatically when my
> application starts up.
>
> -Nathan
>
> Jonathan Hess wrote:>> Hey Nathan -
>>
>> Are you sure you're only creating one instance of your master
>> controller? Do you have only one nib? Or many? If you have many, is
>> the master controller, the file's owner of the other nibs?
>>
>> Jon Hess
>>
>> On Jan 4, 2008, at 12:57 PM, Nathan Auch wrote:
>> >>> Alastair Houghton wrote:>>>> On 3 Jan 2008, at 18:59, Nathan Auch wrote:
>>>> >>>>> Alastair Houghton wrote:
>>>>> >>>>>> No, that's not the problem. Most likely you have chosen a name
>>>>>> for your variable for which there is an unrelated -setSomeName:
>>>>>> method; that method will be being called during initialisation,
>>>>>> rather than just setting the variable directly.>>>>>
>>>>> This doesn't appear to be the case, the name of the variable was
>>>>> "main_controller", I've changed it to "my_main_controller" and
>>>>> reconnected the outlets in IB but I'm still seeing the same
>>>>> behaviour. In general, are there any best practices for choosing
>>>>> variable names to avoid the situation you describe?>>>>
>>>> Not really, no. You just need to be aware of the problem. I
>>>> suppose you could tack "Outlet" onto the end of all of your
>>>> outlets' names (for instance), but I don't know that there is any
>>>> convention or even that doing this kind of thing is terribly common.
>>>>
>>>> If it isn't the outlet name clashing with a -setMyOutletName:
>>>> method, then check that:
>>>>
>>>> 1. They really are connected in IB. It's easy to forget to connect
>>>> things up, or to accidentally disconnect them.>>> I've checked and doubled checked this, in fact, I've even unset and
>>> reset the connections a few times just to make sure.>>>>
>>>> 2. The object in question really has been created, and hasn't e.g.
>>>> returned nil from its -init method. (An NSLog() in the -init
>>>> implementation should be sufficient...)>>> NSLog in the -init implementations of the relevant classes indicates
>>> they are indeed being created.>>>>
>>>> 3. You aren't accessing the member variable from a point before nib
>>>> file has been completely loaded. For instance, trying to use an
>>>> outlet from an -init method of an object that was loaded from the
>>>> same nib file won't work reliably. In that case, you should
>>>> probably implement -awakeFromNib on your object to do whatever you
>>>> need to do.>>> I am not attempting to use the outlet until after awakeFromNib has
>>> been called. In fact, the problem is easily reproducible in my
>>> application by adding an assertion that the outlet in question is
>>> not NULL to the -awakeFromNib method. Unfortunately, as I said
>>> before, I'm not able to reproduce this scenario outside of my
>>> application.
>>>
>>> Do NIB files corrupt often/easily? I'm pretty sure this NIB has been
>>> around for awhile (10.2 days?) and has been migrated through several
>>> versions of IB since then. Is there some tool that can verify the
>>> integrity of a NIB file?
>>>
>>> Thanks,
>>> -Nathan>>
>>
>