Let say I have a NSObject AppController:NSObject. Using IB, I drag an NSObject control into MainMenu.xib and points the class to AppController. Since MainMenu.xib is loaded once and objects inside MainMenu.xib are in memory for the life of the app, does it make the AppController object a singleton?
Then I can drag an IBOutlet to AppDelegate to access this singleton object. This looks like a quick way. Is this a good practice or to be discouraged?
The standard method I supposed is to add a static AppController *sharedInstance inside the class and use a +(AppController *)sharedAppController for access.
No, it's not a singleton because nothing stops you from creating another instance of the same class in code.
It's just a convenient way to create a single instance.
and objects inside MainMenu.xib are in memory for the life of the app
This is not true. If nobody retains these objects (or holds a strong reference to them under GC), they will get deallocated. This is true. See Peter Hosey's comment below.
Related
I use an IBOutlet to refer between objects created in Interface Builder, BUT...
I need to connect an object pointer (or sth) declared in an NSOperation (MyOperation) subclass with my application Controller (with an IBOutlet?) to invoke some methods of AppController. Is there any way to connect (or bind) them ?
What is the best practice to refer to AppController or any other instance created in Interface builder (added as objects) from other objects that are not created on IB too (lets say dynamically created in runtime) ?
The way to do this is to have something that does have an IBOutlet to your object either push pointers to that object into things that are dynamically created, or provide a method for other objects to get that reference.
IBOutlet doesn't make sense in the context of something that is created dynamically, and thus doesn't exist in your nib.
When working in interface builder you can layout your interface with different types of objects but I'm unclear as to whether these are instances of objects or some sort of factory method or something like that. Basically if you use Interface builder to layout your objects, especially with subclasses of uiview, how do you refer to a specific instances/objects in your code?
When you place any objects such as UIButton via Interface Builder, these objects are instantiated when a xib file which you place the object is loaded.
You can refer to the object, if you hook it to a instance variable which is defined in a header file via Interface Builder.
But you need to define the instance variable to be displayed in Interface Builder like as follows.
#interface FooObject : NSObject {
UIButton *button;
}
#property (nonatomic, retain) IBOutlet UIButton * button;
You can draw connections between objects in the nib to give those objects references to each other, and you can also draw connections from the file's owner (which will be some pre-existing object from before the nib is loaded, such as a UIViewController). Generally, you'll need to structure your code so that everything that needs to access things in the nib either knows the owning object or is known by the owning object.
On the other hand, there shouldn't be that many outside objects that need to know about the specific elements in your nib or you might have a bit too much coupling.
I'm developing for the iPhone, and I have a class DataManager, that is used to maintain my application data. When the application launches/exits, the data is read from/written to disk to create an instance of this class, using the NSKeyedArchiver (and Unarchiver) classes, since the DataManager adheres to the NSCoding protocol.
One problem I'm having is that I need the DataManager to be accessible by many of my other IB classes, so it is defined as an object in IB, and those classes have an outlet to it. The DataManager is being created using the standard init: method (or maybe initWithCoder:?), but since IB doesn't have the proper file (or NSData from the file) to instantiate the object, it has no initial contents.
So, is there any way to tell IB not to instantiate the class automatically? This will instead be performed using my application delegate, something like:
AppDelegate.h
IBOutlet DataContext *context;
AppDelegate.m
context = [NSKeyedUnarchiver unarchiveObjectWithData:dataLoadedFromFile];
As you can see, this presents a problem. Wouldn't the context be instantiated twice, once by InterfaceBuilder, then a second time by my application delegate?
I would like to prevent maintaining the context as an ivar in the delegate, since that seems to stray from the MVC paradigm, and leans toward the singleton pattern instead. (The controller should not be responsible for the data in my mind. It can maintain a reference to it obviously, but should not be responsible for offering it to other classes.)
When the application launches/exits, the data is read from/written to disk to create an instance of this class, using the NSKeyedArchiver (and Unarchiver) classes, since the DataManager adheres to the NSCoding protocol.
One problem I'm having is that I need the DataManager to be accessible by many of my other IB classes, so it is defined as an object in IB … As you can see, this presents a problem. Wouldn't the context be instantiated twice, once by InterfaceBuilder, then a second time by my application delegate?
Yup.
First, you should think about whether this is a controller or a model object. It sounds to me like it's a controller.
If it is, then you should move the model to a separate object or objects, and make those NSCoding-compliant, and make the data manager load and save those objects. A bonus of this solution is that you could tell the data manager to save the objects and purge them when you get a low-memory warning, not just at quit time.
Can someone explain in an humanly understandable way what an "Outlet" is?
It's an instance variable that shows up in Interface Builder, so that you can use IB to plug another object into the outlet.
When you load the nib, the nib-loading system will do the requisite magic to make sure the right object shows up in each outlet.
Edit: I'd intended to write a full blog post around this image (I changed my mind after finishing the image), but even alone, it should help clarify outlets for people. Here you go:
(source: boredzo.org)
From a code point-of-view and IBOutlet is only a hint for Interface Builder. It's actually a macro that compiles to, well, nothing at all. That is, the compiler completely removes when compiling.
But Interface Builder can scan your code for IBOutlet so when you right-click on an object in IB you can see all the outlets that you could connect to other objects.
alt text http://img27.imageshack.us/img27/5512/picture820090228.png
In this example, delegate is a member variable of UIApplication and it is an IBOutlet too.
I just think of it as a pointer to a UI control. Once I made that mental connection in my mind, it made sense.
I would say they are the bridge that connects your user interface objects to the code that uses them. Like the name suggests, they provide a spot to "plug in" your UI to your code.
The IBOutlet keyword is defined like this:
#ifndef IBOutlet
#define IBOutlet
#endif
IBOutlet does absolutely nothing as far as the compiler is concerned. Its sole
purpose is to act as a hint to tell Interface Builder that this is an instance variable that we’re
going to connect to an object in a nib. Any instance variable that you create and want to
connect to an object in a nib file must be preceded by the IBOutlet keyword.
IBOutlet is a symbol that indicates to Interface Builder that an object instance variable delcared as
IBOutlet id ivar_name;
should be presented as an outlet of an instance of the associated class. This allows you to graphically connect objects in Interface Builder such that, after the NIB is loaded (i.e. when the object is sent an -awakeFromNib message), the value of ivar_name will be a pointer to the object you selected as the outlet's value in Interface Builder.
From the Objective-C language standpoint, IBOutlet means nothing.
An outlet is an instance variable in your code (in X-code) that can be assigned a reference to a user interface object (in Interface Builder). You plug the user interface object into the instance variable. The assignment is specified in the NIB file created by Interface Builder.
I've got two controls in my Interface Builder file, and each of those controls I've created a separate delegate class for in code (Control1Delegate and Control2Delegate). I created two "Objects" in interface builder, made them of that type, and connected the controls to them as delegates. The delegates work just fine. My problem is, I need to share information from one delegate to the other delegate, and I'm not sure how.
What is the best way to do this? Combine the two delegates into one class, or somehow access a third class that they can both read? Since I'm not actually initializing the class anywhere in my code, I'm not sure how to get a reference to the actual instance of it (if there is an actual instance of it), or even access the "main" class that the project came with.
You can add outlets from either delegate to the other delegate. There are two ways to add an outlet to an object in IB (assuming you're using Xcode/IB version 3.0 or later:
If you have not generated the code for your delegate classes yet, select the desired delegate, then open the "Object Identity" tab in the IB inspector. Add a "Class outlet" of type NSObject. You should then be able to set this new outlet to the other delegate. Of course you will have to generate the code for your delegate class and add the generated source files to your Xcode project before you can load the nib.
If you've already generated the code for the delegate class (or added an NSObject to your NIB and set its Class to an existing class in your Xcode project), add an instance variable to the delegate class:
IBOutlet id outletToOtherDelegate;
As long as your Xcode project is open (as indicated by the green bubble in the lower-left of your NIB window), IB will automatically detect the new outlet and allow you to assign it to the other delegate object in your NIB.
Cocoa automatically connects these outlets at NIB load time. Once awakeFromNib is called on instances of your delegate objects, you may assume that all the other objects in the NIB have been instantiated and all outlets have been connected. You should not assume an order on calls to awakeFromNib, however.
I think you can create outlets on each one and cross-bind them so that they each have the same data all the time. If there's one model object they need to share, that's pretty tidy. I don't actually know how to do this; I think I saw it in an iPhone tutorial one time!
I don't have my Mac in front of me currently since I'm at work, but would it be possible to bind an instance of one delegate to a member of the other delegate? This would be similar to binding an NSArrayController to a member of another controller class, for example.
However, depending on what the delegate classes are doing, if the tasks are similar I would probably just combine them into once class. That would eliminate the problem altogether.