How to initialize main window objects from custom class during program start? - objective-c

I have a main window with a couple of popupbuttons. I want to clear them, then load the lists from a method in a custom class. I've got my view controller working and I know the method in the custom class (newRequest) is working because I added a NSLog command to print "Test" when the method executes. In AppDelegate I'm calling the method via:
[polyAppRequest newRequest];.
As I said, I know the method is executing. Why can't I removeallitems from the popupbutton from this custom class method?
Thanks
Keith

I read that you should use an NSWindowController to manage a window. See here:
Windows and window controllers
Adding views or windows to MainWindow
Then if your window gets complicated enough, the NSWindowController can employ various NSViewControllers to manage parts of the window.
In any case, I used an NSWindowController in my answer.
The image below shows the outlet's for File's Owner, which is my MainWindowController:
I created MainWindowController .h/.m in Xcode6.2 by:
Selecting File>New>File>OS X - Source - Cocoa Class
Selecting NSWindowController for Subclass of:
Checking also create .xib file for user interface
Then I deleted the window--not the menu--in the default MainMenu.xib, and I changed the name of MainWindowController.xib, created by the steps above, to MainWindow.xib.
The following code works for me (but I'm a Cocoa beginner!):
//
// AppDelegate.m
// PopUpButtons
#import "AppDelegate.h"
#import "MainWindowController.h"
#interface AppDelegate ()
#property(strong) MainWindowController* mainWindowCtrl;
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
[self setMainWindowCtrl:[[MainWindowController alloc] init]];
[[self mainWindowCtrl] showWindow:nil];
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}
#end
...
//
// MainWindowController.m
// PopUpButtons
//
#import "MainWindowController.h"
#import "MyData.h"
#interface MainWindowController ()
#property(strong) MyData* data;
#property(weak) IBOutlet NSPopUpButton* namePopUp;
#property(weak) IBOutlet NSPopUpButton* agePopUp;
#end
#implementation MainWindowController
-(id)init {
if (self = [super initWithWindowNibName:#"MainWindow"]) {
_data = [[MyData alloc] init]; //Get data for popups
}
return self;
}
- (void)windowDidLoad {
[super windowDidLoad];
// Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
[[self namePopUp] removeAllItems];
[[self namePopUp] addItemsWithTitles:[[self data] drinks]];
[[self agePopUp] removeAllItems];
[[self agePopUp] addItemsWithTitles:[[self data] extras]];
}
#end
...
//
// MyData.h
// PopUpButtons
//
#import <Foundation/Foundation.h>
#interface MyData : NSObject
#property NSArray* drinks;
#property NSArray* extras;
#end
...
//
// MyData.m
// PopUpButtons
//
#import "MyData.h"
#implementation MyData
- (id)init {
if (self = [super init]) {
_drinks = #[#"coffee", #"tea"];
_extras = #[#"milk", #"sugar", #"honey"];
}
return self;
}
#end
I hope that helps. If you need any more screenshots, let me know.
Edit1:
I think I see what you are asking about. Although I don't think it is a very good approach, if I change my code to this:
//
// MyData.h
// PopUpButtons
//
#import <Cocoa/Cocoa.h>
#interface MyData : NSObject
#property (copy) NSArray* drinks;
#property (copy) NSArray* extras;
-(void)newRequest;
#end
...
//
// MyData.m
// PopUpButtons
//
#import "MyData.h"
#interface MyData()
#property (weak) IBOutlet NSPopUpButton* drinksPopUp;
#property (weak) IBOutlet NSPopUpButton* extrasPopUp;
#end
#implementation MyData
- (id)init {
if (self = [super init]) {
_drinks = #[#"coffee", #"tea"];
_extras = #[#"milk", #"sugar", #"honey"];
}
return self;
}
-(void)newRequest {
[[self drinksPopUp] removeAllItems];
[[self drinksPopUp] addItemsWithTitles:[self drinks]];
[[self extrasPopUp] removeAllItems];
[[self extrasPopUp] addItemsWithTitles:[self extras]];
}
#end
I am unable to populate the NSPopUpButtons. This is what I did:
I dragged an Object from the Object Library to the dock in IB, and in the Identity Inspector, I changed the Object's class to MyData.
Then I clicked on the Connections Inspector, and the two instance variables in MyData, drinksPopUp and extrasPopUp, were listed in the Outlets.
I dragged from the outlets to the respective NSPopUpButtons.
I guess I assumed, like you, that when my program ran, the NSPopUpButtons would be assigned to the instance variables drinksPopUp and extrasPopUp--but that doesn't seem to be the case. According to the Apple docs, you should be able to do that:
An application typically sets outlet connections between its custom
controller objects and objects on the user interface, but they can be
made between any objects that can be represented as instances in
Interface Builder,...
Edit2:
I am able to pass the NSPopUpButtons from my MainWindowController to the newRequest method, and I can use the NSPopUpButtons inside newRequest to successfully populate the data.
Edit3:
I know the method in the custom class (newRequest) is working because
I added a NSLog command to print "Test" when the method executes.
But what happens when you log the variables that point to the NSPopUpButtons? With my code in Edit1, I get NULL for the variables, which means the NSPopUpButtons never got assigned to the variables.
Edit4:
If I add an awakeFromNib method to MyData, and inside awakeFromNib I log the NSPopUpButton variables for the code in Edit1, I get non NULL values. That tells me that the MainWindowController's windowDidLoad method is executing before MyData's awakeFromNib method, and therefore you cannot call newRequest inside MainWindowController's windowDidLoad method because MyData has not been fully initialized.
Edit5:
Okay, I got the code in Edit1 to work. The Apple docs say this:
About the Top-Level Objects
When your program loads a nib file, Cocoa recreates the entire graph
of objects you created in Xcode. This object graph includes all of the
windows, views, controls, cells, menus, and custom objects found in
the nib file. The top-level objects are the subset of these objects
that do not have a parent object [in IB]. The top-level objects typically
include only the windows, menubars, and custom controller objects that
you add to the nib file [like the MyData Object]. (Objects such as File’s Owner, First
Responder, and Application are placeholder objects and not considered
top-level objects.)
Typically, you use outlets in the File’s Owner object to store
references to the top-level objects of a nib file. If you do not use
outlets, however, you can retrieve the top-level objects from the
nib-loading routines directly. You should always keep a pointer to
these objects somewhere because your application is responsible for
releasing them when it is done using them. For more information about
the nib object behavior at load time, see Managing the Lifetimes of
Objects from Nib Files.
In accordance with the bolded line above, I changed this declaration in MainWindowController.m:
#interface MainWindowController ()
#property(strong) MyData* data;
...
#end
to this:
#interface MainWindowController ()
#property(strong) IBOutlet MyData* data;
...
#end
Then, in IB I dragged a connection from the MainWindowController data outlet to the MyData Object(the Object I had previously dragged out of the Object Library and onto the doc).
I guess that causes MyData to unarchive from the .xib file and initialize before MainWindowController.

Related

Sending a message to an instance method from inside a block

I am relatively new to Cocoa programming. Basically, I want to send a message within a method in my Document class to an intense of a class (that inherits from NSView) that I have initialised as a property in the #interface of the Document class.
Here is the simplified version:
///////////////////////////KOZDocument.h///////////////////////////
#import <Cocoa/Cocoa.h>
#import "KOZOtherClass.h"
#interface KOZDocument : NSDocument
#property (assign) IBOutlet KOZOtherClass *otherClassInstance; //this would be connected to the relevant CustomView in the IB
#end
///////////////////////////KOZDocument.m///////////////////////////
#import "KOZDocument.h"
#implementation KOZDocument
- (id)init
{
self = [super init];
if (self) {
// I want to send a message to otherClassInstance from some method e.g. init
NSLog(#"INITIALISING");
[[self otherClassInstance] printMessage];// this is the message I want to work but which doesn't (even though i don't any errors)
//sending the message to a locally initiated instance works but I don't want to use a local instance because i want to connect it to a CustomView in IB
KOZOtherClass *otherClassLocalInstance = [[KOZOtherClass alloc] init];
[otherClassLocalInstance printMessage];
}
return self;
}
//.….
///////////////////////////KOZOtherClass.h///////////////////////////
#import <Foundation/Foundation.h>
#interface KOZOtherClass : NSView
- (void) printMessage;
#end
///////////////////////////KOZOtherClass.m///////////////////////////
#import "KOZOtherClass.h"
#implementation KOZOtherClass
- (void) printMessage{
NSLog(#"This method can be called!!");
}
#end
/////////////////////////////////////////////////////////////////////
The same methodology works for all the native Cocoa objects but not for mine.
Can anyone tell me what I'm doing wrong?
Here is the context of why i want to do this:
I am building an app that plays a video using the AVFoundation. I have an animation I want to trigger in an NSView when the playback reaches a particular part in the video (e.g. after 2 seconds). I am adapting Apple's AVSimplePlayer and using the time observer to get the position of the playhead. The time observer executes the code inside a block for every given time interval. In this block I want to send a message to my animation view to trigger the animation when the time is more that 5 seconds for example.
In -init of your objects the Interface Builder connections are not set yet, the loading mechanism can't set those before your object is initialized.
Instead you want to overwrite the -awakeFromNib method like so:
- (void)awakeFromNib {
[[self otherClassInstance] printMessage];
}
-awakeFromNib is guaranteed to be called after the connections have been made. Depending on the exact implementation you may also need to guard against that code being executed twice, for example by having a boolean instance variable didWake that you check/set in that method.

NSMutableArrays are null

I have an NSViewController subclass with:
#property (retain) NSMutableArray* entities;
#property (retain) NSMutableArray* tiles;
In my -init method, both arrays are created with +new, and are given one object each. After that, I call NSLog(#"%#, %#", entities, tiles);, and it gives me just as expected:
2012-12-30 15:07:04.160 Project Land III[2177:303] (
"<RBEntity: 0x100508170>"
), (
"<RBTile: 0x100508470>"
)
I can click a button on the view, though, which calls the same log function, and it spit out this:
2012-12-30 15:07:06.071 Project Land III[2177:303] (null), (null)
I've been stuck on this problem in some form or another for days. Why in the world are the arrays null?
I'm more than happy to post more code, just let me know!
Interface:
#import <Cocoa/Cocoa.h>
#import "RBEntity.h"
#import "RBTile.h"
#interface RBMainViewController : NSViewController {
NSMutableArray* _entities;
NSMutableArray* _tiles;
}
#property (retain) NSMutableArray* entities;
#property (retain) NSMutableArray* tiles;
- (IBAction)log:(id)sender;
#end
My -init method:
- (id)init {
self = [super init];
self.entities = [NSMutableArray new];
self.tiles = [NSMutableArray new];
[self.entities addObject:[RBEntity entityWithLocation:NSMakePoint(4, 5) type:FACEEATER]];
[self.tiles addObject:[RBTile tileWithLocation:NSMakePoint(10, 2) type:GRASS]];
NSLog(#"%#, %#", self.entities, self.tiles);
return self;
}
In my -init method, both arrays are created with +new, and are given one object each. After that, I call NSLog(#"%#, %#", entities, tiles);, and it gives me just as expected:
2012-12-30 15:07:04.160 Project Land III[2177:303] (
"<RBEntity: 0x100508170>"
), (
"<RBTile: 0x100508470>"
)
I can click a button on the view, though, which calls the same log function, and it spit out this:
2012-12-30 15:07:06.071 Project Land III[2177:303] (null), (null)
This is a very common novice mistake.
You have two RBMainViewController objects. One of them, you presumably created in code in one of your other .m files, by saying something like [[RBMainViewController alloc] init]. The other, you created in a nib, probably by dragging it into the nib.
(Note: The nib that you created that VC in is not the VC's nib. That would be circular, to have the VC's nib containing the VC that is loading the nib. The VC that doesn't have its arrays resides in nib A, and each VC will load nib B.)
The VC that you created in a nib is the one whose view appears on the screen. Because that object never received an init message (it was initialized with some other initWith… message instead), you never created its arrays. The view controller you created with init, which does have its arrays, is not visible on the screen (otherwise you would have clicked on its button, rather than the other's, and you'd have seen the arrays in the output).
The solution involves two changes.
The first is to change your implementation of init to be an implementation of initWithNibName:bundle: instead. Like so:
- (instancetype) initWithNibName:(NSString *)nibName
bundle:(NSBundle *)bundle
{
self = [super initWithNibName:nibName bundle:bundle];
if (self != nil) {
<#...#>
}
return self;
}
If you want to continue using init to create your VC in other code, fine, but your implementation of -[RBMainViewController init] should simply send initWithNibName:bundle: to self and return the result.
- (instancetype) init {
return [self initWithNibName:<#nibName#> bundle:<#bundle#>];
}
You also need to delete one of the two view controllers. We'd need to see the code and the nib to know which. If you delete the one you created in code, you may want to create an outlet in that class and connect it in the nib. If you delete the one in the nib, any outlet connections you established in that nib to that VC, you'll need to re-create in code.
Write a custom getter/setter for one of these properties, and put a breakpoint there to see who's resetting the value. If that doesn't catch the problem, and your property is still being reset, you're probably accessing them from an instance of your NSViewController that hasn't been initialised by your init, thus those properties were always uninitialised.
I believe you will need to use self.entites and self.tiles when working with those objects in your class.
EDIT (after interface added)
You will need to have something in your interface like:
#interface RBMainViewController : NSViewController{
NSMutableArray* _entities;
NSMutableArray* _tiles;
}
#property (retain) NSMutableArray* entities;
#property (retain) NSMutableArray* tiles;
- (IBAction)log:(id)sender;
#end
Then you will need to add this to the implementation:
#synthesize entities = _entities;
#synthesize tiles = _tiles;

Calling a method from another 'm' file

The bulk of the code for my app is in a 'm' file called MyViewController. The app implements a custom UIView which contains a UIWebView object. The code for the UIView and UIWebView is kept in a separate 'm' file called CustomUIView.
I have managed to override clicks on URL hyperlinks in the UIWebView object using a delegate. However, I would like to have these clicks launch a method that is stored in my main app code. This method is called "popupView", and takes a single argument, "inputArgument". The inputArgument is the text of the URL the user clicks on. In fact, this method is the very same one that causes my custom UIView to launch.
Anyway, what I'd like to do is have my overridden URL clicks cause the popupView method to launch, thus causing another UIView to open on top of the one that was clicked on.
The problem is that the 'm' file where the URL clicks are detected can't see the 'popupView' method as it is included in the MyViewController 'm' file. How do I call the popupView method from another 'm' file?
Directly
Declare MyViewController's method -popupView: in MyViewController.h.
#import MyViewController.h in CustomUIView.m.
Give CustomUIView a reference to the [one] instance of MyViewController, for example by way of an #property declared in CustomUIView.h.
For (1), the #interface of MyViewController (in MyViewController.h) should look a bit like this
#interface MyViewController : UIViewController
{
//....
}
- (void)popupView:(NSString *)urlText;
//....
#end
For (2), UIViewController.m should have the following somewhere near the top
#import "CustomUIView.h"
#import "MyViewController.h"
For (3), the #interface in CustomUIView.h should look something like
#interface CustomUIView : UIView
{
//....
}
#property (nonatomic, weak) MyViewController *viewController;
#end
This property will need to be set some time after the instance of CustomUIView owned by MyViewController is created. If your CustomUIView is in MyViewController.xib, you can set this property on it by adding the keyword IBOutlet to the property's declaration like this
#property (nonatomic, weak) IBOutlet MyViewController *viewController;
and pointing this property to "File's Owner" in the XIB. If instead, you create the CustomUIView programmatically, you can set this property on it as soon as you have initialized it.
Delegate
This, however, is far from being a best practice. It would be much better to make use of the delegate pattern. To do this, you'll need to
Define a delegate protocol.
Add a "delegate" #property to CustomUIView.
Call the delegate methods on the delegate object at the appropriate times.
Implement the protocol in MyViewController.
Set the "delegate" #property of the instance of CustomUIView owned by the MyViewController instance to be the MyViewController instance.
Let's call our delegate protocol something imaginative like CustomUIViewDelegate. For (1), we'll declare it at the top of CustomUIView.h as follows:
#class CustomUIView;
#protocol CustomUIViewDelegate <NSObject>
- (void)customUIView:(CustomUIView *)customView didSelectURLText:(NSString *)urlText;
#end
Notice that we've had to forward declare our class CustomUIView so that the compiler is able to make sense of the type of the first argument in the protocol method customUIView:didSelectURLText:.
For (2), we'll do something quite similar to (3) above: Your CustomUIView #interface will look something like
#interface CustomUIView : UIView
{
//....
}
#property (nonatomic, weak) id<CustomUIViewDelegate> *delegate;
#end
Again, if we're going to set this property in Interface Builder, we'll need to use the IBOutlet keyword to announce it to IB:
#property (nonatomic, weak) IBOutlet id<CustomUIViewDelegate> *delegate;
For (3), we need to call the delegate method customUIView:didSelectURLText: on our delegate object self.delegate at the appropriate time.
In your question, you wrote
I have managed to override clicks on URL hyperlinks in the UIWebView object using a delegate.
So, let's say that CustomUIView has an instance method
- (void)didSelectURL:(NSURL *)url
{
//....
}
which you call when the user selects a link in the UIWebView. The CustomUIView's delegate needs to be informed of this:
- (void)didSelectURL:(NSURL *)url
{
//...
if ([self.delegate respondsToSelector:#selector(customUIView:didSelectURLText:)]) {
{
[self.delegate customUIView:self didSelectURLText:url.absoluteString];
}
}
Notice that we check first whether the CustomUIView instance's delegate object implements the selector of interest (customUIView:didSelectURLText:) by calling respondsToSelector: on it.
For (4), we'll need first to add <CustomUIViewDelegate> to MyViewController's #interface declaration and be sure to #import CustomUIView.h into the file where we use the symbol CustomUIViewDelegate. Our MyViewController's #interface will look something like this:
#import "CustomUIView.h"
#interface MyViewController : UIViewController <CustomUIViewDelegate>
{
//....
}
//....
#end
More importantly, we need to implement the CustomUIViewDelegate protocol in MyViewController's #implementation; so far we've only declared that MyViewController adopts it.
To do this, since our protocol consists of only one method, we'll need only to add our own implementation of -customUIView:didSelectURLText:. Our MyViewController's #implementation will look something like this:
#import "MyViewController.h"
#implementation MyViewController
//....
- (void)popupView:(NSString *)urlText
{
//....
}
#pragma mark - CustomUIViewDelegate
- (void)customUIView:(CustomUIView *)customView didSelectURLText:(NSString *)urlText
{
[self popupView:urlText];
}
//....
#end
Finally, for (5), we'll need to set the delegate property of the instance of CustomUIView owned by the MyViewController instance. I don't know enough about MyViewController's relationship with its CustomUIView instance to do describe how to do this definitively, but I'll provide an example: I'll assume that you programmatically, in -[MyViewController loadView] add the CustomUIView as a subview of MyViewController's view. So your implementation of -loadView looks a bit like this:
- (void)loadView
{
[super loadView];
//....
CustomUIView *customView = //....
//....
[self.view addSubview:customView];
//....
}
All that remains to do at this point is to set the delegate #property of the local variable customView to self:
customView.delegate = self;
Edit: Updated (5) in light of new information about the relationship between CustomUIView and MyViewController.
In your comment, you write that your CustomUIView is added as a subview of cvc.view where cvc is an instance of CustomUIViewController in CustomUIView's method -[CustomUIView show]. On account of this, you note that writing customView.delegate = self; is the same as writing self.delegate = self, which is clearly not what you want to do.
You want to set the CustomUIView's delegate property to be the instance of MyViewController. Consequently, your method -[CustomUIView show] should look something like
- (void)show
{
//....
[cvc.view addSubview:self];
self.delegate = mvc;
}
where mvc is the instance of MyViewController.
Well, since you are writing the CustomUIView, why not implement a constructor like initWithPopupDelegate:(MyViewController *)delegate and keep a reference to the MyViewController instance that way in an instance variable, then call the method on that.
(Add #class MyViewController; at the top CustomUIView.h, and #import "MyViewController.h" at the top of CustomUIView.m so the compiler knows the class you are using.)
Alternatively, if there is ever only one MyViewController instance, you can define a class method for MyViewController, e.g., + (MyViewController *)instance, and have that return a reference to the one instance (which you store in a class variable and set the first time when you create the instance, see “singleton pattern”). But without knowing the specifics of your code, I would suggest the first solution (delegate) as simpler and more flexible.

Objective-c: Singleton - passing variables

I have a singleton that I'd like to use to manage the onscreen animation of my views. Here's my.
#import <Foundation/Foundation.h>
#interface OAI_AnimationManager : NSObject {
NSMutableDictionary* sectionData;
}
#property (nonatomic, retain) NSMutableDictionary* sectionData;
+(OAI_AnimationManager* )sharedAnimationManager;
- (void) checkToggleStatus : (UIView* ) thisSection;
#end
.m file
#import "OAI_AnimationManager.h"
#implementation OAI_AnimationManager
#synthesize sectionData;
+(OAI_AnimationManager *)sharedAnimationManager {
static OAI_AnimationManager* sharedAnimationManager;
#synchronized(self) {
if (!sharedAnimationManager)
sharedAnimationManager = [[OAI_AnimationManager alloc] init];
return sharedAnimationManager;
}
}
- (void) checkToggleStatus : (UIView* ) thisSection {
//get the section data dictionary
NSLog(#"%#", sectionData);
}
#end
You'll see in the .h file I added a NSMutableDictionary and am using #property/#synthesize for it's getter and setter.
In my ViewController I instantiate the animation manager as well as a series of subclasses of UIView called Section. With each one I store the data (x/y w/h, title, etc.) in a dictionary and pass that to the dictionary delcared in animation manager. In the Section class I also instantiate animation manager and add a UITapGestureRecognizer which calls a method, which passes along which section was tapped to a method (checkToggleStatus) in animation manager.
As you can I see in the method I am just logging sectionData. Problem is I am getting null for the value.
Maybe my understanding of singletons is wrong. My assumption was the class would only be instantiated once, if it was already instantiated then that existing object would be returned.
I do need all the other Section classes data as if one animates others animate in response and I can get around it by passing the tapped Section to the animation manager and doing [[Section superview] subviews] and then looping and getting the data from each that way but it seems redundant since that data is available in the ViewController when they are created.
Am I doing something wrong in trying to transfer that data? Is there a better solution? I am open to suggestions and criticisms.
Thanks
h file
#interface OAI_AnimationManager : NSObject
#property (nonatomic, retain) NSMutableDictionary* sectionData;
+(OAI_AnimationManager* )sharedAnimationManager;
- (void) checkToggleStatus : (UIView* ) thisSection;
#end
m file
static OAI_AnimationManager* _sharedAnimationManager;
#implementation OAI_AnimationManager
#synthesize sectionData = _sectionData;
+(OAI_AnimationManager *)sharedAnimationManager {
#synchronized(self) {
if (!_sharedAnimationManager) {
_sharedAnimationManager = [[OAI_AnimationManager alloc] init];
}
}
return _sharedAnimationManager;
}
- (void) checkToggleStatus : (UIView* ) thisSection {
//get the section data dictionary
NSLog(#"%#", _sectionData);
}
#end
Notice I moved your sectionData variable from the header and moved it to the implementation file. A while back, they changed it to where you can synthesize properties and specify their instance variable names along side it... hence:
sectionData = _sectionData;
I also added and underscore to the instance variable... this is a universal convention for private variables and it also will throw a compile error now if you try to type just sectionData as you did in the return statement of checkToggleStatus:. Now you either have to type self.sectionData or _sectionData.
You didn't include the code that creates an instance of your dictionary but I bet you didn't set it as self.sectionData = [[NSDictionary alloc] init] which means it would not retain the value and you would get null the next time you called it. Classic memory management mistake... I know it well because I learned the hard way hehehe

Objective-C: why a custom object will be a zombie

I'm developing an app in Objective-C using ARC.
My simplified code looks like this:
ClassA (.m)
MyCustomClass *obj = [[MyCustomClass alloc] initWithValue1:#"abc" value2:1000];
MyViewController *vc = [[MyViewController alloc] initWithObject:obj];
// "vc" will become the first item of a UITabBarController
MyViewController (.h)
- (id)initWithObject:(MyCustomClass *)obj {
...
localReferenceToOjbect = obj;
...
}
- (void)viewWillAppear:(BOOL)animated {
// do something with "localRefernceToObject" <---
}
launching the app will result in a call to a zombie: when the ViewController is shown, the "obj" will be already deallocated and so i can't use it anymore.
my workaround is:
ClassA (.h)
#interface ClassA : UIViewController {
MyCustomClass *obj;
}
ClassA (.m)
obj = [[MyCustomClass alloc] initWithValue1:#"abc" value2:1000];
MyViewController *vc = [[MyViewController alloc] initWithObject:obj];
// "vc" will become the first item of a UITabBarController
is this the right way?! i don't think so: why i've to store an istance of an object that is useless for ClassA?
i can't get an explanation on what's actually happening. could you help me?
You're right in the fact that it is not logical to keep around a reference to obj in ClassA.
But if you need to keep around the reference to obj for MyViewController to use it, retain it in MyViewController, not in ClassA, because that's MyViewController that will use it.
The easiest way to do this is to transform your localReferenceToObject you use in MyViewController into a #property(retain) propertyToObject; (or #property(strong) propertyToObject if you use ARC) and access it in your MyViewController.m with self.propertyToObject (instead of localReferenceToObject, to be sure to call the property's setter and thus really retain the object).
This way, the object will be retained and kept around while your MyViewController instance is still alive.
[EDIT] If you want this property to be private, you can declare it in the class extension so that it is not accessible from other classes, as in the below example. See here in Apple's documentation for more details.
In your MyViewController.h header file
#interface MyViewController : UIViewController
// Here you write the public API in the .h / public header
// If you don't want your property to be visible, don't declare it there
#end
In your MyViewController.m file
#interface MyViewController ()
// This is the private API, only visible inside the MyViewController.m file and not from other classes
// Note the "()" to declare the class extension, as explained in Apple doc
#property(nonatomic, retain) MyCustomClass* referenceToObject; // Note: use strong (which is a synonym of retain) if you use ARC
#end
#implementation MyViewController
#synthesize referenceToObject = _referenceToObject; // not even needed with modern ObjC and latest LLVM compiler
- (id)initWithObject:(MyCustomClass *)obj
{
self = [super init];
if (self) {
...
self.referenceToOjbect = obj;
...
}
return self;
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// do something with "self.refernceToObject"
}
// This memory management code is only needed if you don't use ARC
-(void)dealloc
{
self.referenceToObject = nil; // release memory
[super dealloc];
}
Personally, as suggested by Apple in some WWDC sessions, I now really rarely use instance variables and prefer the use of properties instead, either public in the .h or private in the .m.
If you use ARC, you can still use an instance variable instead of a property as ARC will retain it for you, but as long as you make sure your instance variable is declared as strong and not weak.