How do get reference to another class properly? - objective-c

I have some class initialized in Appdelegate, but when I get this class instance form Appdelegate in another class it has "fresh" state.
I have following in AppDelegate:
Interface:
#property (nonatomic, retain) DataController *dataController;
Implementation:
#synthesize dataController;
- (id)init {
if (self = [super init]) {
DataController *controller = [[DataController alloc] init];
self.dataController = controller;
[controller release];
NSLog(#"items: %d",[self.dataController numberOfItems]);
}
return self;
}
At this point DataControlelr class loads objects form database. Log output show "items: 10".
I have TableViewController where I need to use DataController.
TableViewController header:
#interface TableViewController : UITableViewController {
DataController *dataController;
}
#property (retain) DataController *dataController;
#end
Implementation:
-(id)init{
if (self =[super init]) {
DataController *dc =[(AppDelegate *)[[UIApplication sharedApplication] delegate] dataController];
[dc retain];
dataController = dc;
NSLog(#"items: %d",[self.dataController numberOfItems]);
}
return self;
}
Here it always says that DataController has 0 items. "fresh" state.
The Log output is always
items: 10
items: 0
It seems like assigning that class creates reference to freshly initialised DataController somehow?
How do I reference another class properly?
Thanks.

The first thing to check would be to ensure that the dc variable in the second class isn't nil-- that would cause any method called on it to 'return' 0.
It might also be useful to print out the address of the app delegate from both of those methods-- just in case the -init method is resulting from an incorrectly-allocated second instance of that class somewhere, while the regular version hasn't been initialized in the same way (or was using -initWithCoder:, etc.)
One useful rule of thumb for initialization of objects created or assigned within a nib file is to use -awakeFromNib to perform most of your initialization tasks. A corollary to this is that the app delegate can set up its state in response to the -applicationDidFinishLaunching: method. In this case, if there is a second instance of your AppDelegate class being allocated somewhere, only the one which is really set as the app's delegate will receive -applicationDidFinishLaunching:.
At the end of the day, stepping through in the debugger and looking at the call stack should show you if something isn't happening in quite the way it should.

Could there be an issue with your assignment of dataController = dc in TableViewController? In your log statement you use self.dataController, should your assignment directly above it be self.dataController = dc ?

I found the gotcha. Tanks to Jim!
Moved assignment from -init to -awakefromnib and now DataController is valid.
My mistake is that after putting the code initially in -viewDidLoad and -viewWillAppear which was wrong I thought that in -init is the place for the assignment.

Related

Issue with subclass and superclass using same named properties

This is something very fascinating I observed today. Maybe that's how Objective-C works but I didn't know about this. See the following code below:
// ATableViewController.h
#interface ATableViewController : UITableViewController
#end
// ATableViewController.m
#interface ATableViewController ()
#property (nonatomic) int volly;
#end
#implementation ATableViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.volly = 5;
}
#end
// BTableViewController.h
#interface BTableViewController : ATableViewController
#end
// BTableViewController.m
#interface BTableViewController ()
#property (nonatomic) int volly;
#end
#implementation BTableViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"%d", self.volly); // returns 5
}
#end
I am not sure why the above is valid. I do understand that I passed a message 'volly' to the object 'self' which in turn probably looked at the value from the super class but shouldn't these be initialized? Some explanation would be of great help. Thanks.
EDIT: This is a big problem IMO though. Considering I don't know any of the private properties defined in the super class, my own set of values might end up being different.
For example, a developer may set a boolean flag hasAppeared in viewDidAppear:. This same value will be set for my subclass instance in viewDidAppear: after the [super viewDidAppear:] call. This will be before I actually get to set it myself.
Currently, the solution is I know exactly the variable used by the super class and I can avoid using the same value but I deem this to be a larger issue than it seems.
EDIT 2: The behavior is consistent with binaries (with only headers) as well as with frameworks where implementation is available.
I am answering this without reading all of the comments.
There is no issue here. Both AViewController (AVC) and BViewController (BVC) each have their own private property named volly.
You created an instance of BVC. It can't see the volly property from its parent class (because it is private), just its own.
Now the fun begins.
The viewDidLoad method from BVC is called. It in turn calls [super viewDidLoad]; which of course calls the viewDidLoad from the AVC class. That method, in turn, calls self.volly = 5;.
The confusion seems to be with this line. Remember, self.volly = 5; is really a call to:
[self setVolly:5];
Both AVC and BVC have the (synthesized) setVolly: method. Since self is a pointer to an instance of a BVC object, the call to [self setVolly:5]; results in a call to the setVolly: method in the BVC class despite being called from a method in the AVC class.
Here's the code with some annotations:
The 'BVC' class:
- (void)viewDidLoad
{
[super viewDidLoad]; // calls the method in `AVC`
NSLog(#"%d", self.volly); // returns 5
}
The 'AVC' class:
- (void)viewDidLoad
{
[super viewDidLoad]; // calls the UITableViewController method
// The following is really [self setVolly:5];
// Since "self" is a "BVC", the private "volly" property of
// the "BVC" class is actually set here.
// The private "volly" property of the "AVC" class will still be
// 0 after this call.
self.volly = 5;
}
In the end, the subclass is not using the private property of the parent class. The original premise in the question's title is incorrect.

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.

Property not set in drawRect method - iOS

I have been seeing some strange behavior when I try to access a class variable or a property in my drawRect method..
In my .h file I have the following
#interface DartBoard : UIView
{
Board * board;
int index;
}
#property (readwrite, assign, nonatomic) NSNumber * selectedIndex;
#end
In my .m file I have the following
#implementation DartBoard
#synthesize selectedIndex;
-(id)init
{
self.selectedIndex = [NSNumber numberWithInt:5];
index = 123;
return self;
}
- (void)drawRect:(CGRect)rect {
NSLog(#"selectedIndex: %d",[self.selectedIndex intValue]);
NSLog(#"index: %d",index);
}
#end
the output is
2012-06-12 19:48:42.579 App [3690:707] selectedIndex: 0
2012-06-12 19:48:42.580 App [3690:707] index: 0
I have been trying to find a solution but have had no luck..
I found a similar question but there was no real answer to the issue
See: UIView drawRect; class variables out of scope
I have a feeling drawRect is different that normal methods and is not getting the scope of the class correctly but how do I fix it?
Cheers
Damien
I have a feeling drawRect is different that normal methods and is not getting the scope of the class correctly
No, there is nothing special about -drawRect:.
There are two possibilities:
1. Your -init method is not being called.
You didn't say how this view gets created -- if you are manually calling [[DartBoard alloc] init], or if it is getting unarchived from a nib file.
If it's coming from a nib, UIView's unarchiving doesn't know that your init method should be called. It will call the designated initializer instead, which is -initWithFrame:.
So, you should implement that method instead, and make sure to call super!
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
self.selectedIndex = [NSNumber numberWithInt:5];
index = 123;
}
return self;
}
2. There might be two instances of your view: one that you are manually initing, and another one that comes from somewhere else, probably a nib. The second instance is the one that is being drawn. Since its variables and properties are never set, they show up as zero (the default value).
You could add this line to both your -init and -drawRect: methods, to see what the value of self is. (Or, check it using the debugger.)
NSLog(#"self is %p", self);

Using synthesized mutator vs. assignment

In my program, I am assigning a value to a property in the init method. I later use this property in response to an event.
Given a property named Object; why does the synthesized setter [self setObject:obj] work, but Object = obj give an Invalid Selector exception? Is it because the mutator adds one to the reference count on obj?
Edit: Here is more code, to give context. When I say "work" above, I mean run without errors.
Here is the initialization of the object(A View Controller) which has the property in it:
Note: All properties are declared as (nonatomic, retain).
#synthesize _Kiosk;
....
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil andKiosk: (Kiosk*) kiosk
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
[self set_Kiosk:kiosk]; //This work witout error. _kiosk = kiosk Fails at point below.
}
return self;
}
....
- (IBAction) ActionPressed:(id)sender
{
[_CompanyName setText: [_Kiosk _CompanyName]]; //Failure happens here
}
And here is the method call that initializes this View Controller:
#synthesize _Kiosk;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
[[UIApplication sharedApplication] setStatusBarHidden:true withAnimation:UIStatusBarAnimationFade];
_Kiosk = [[Kiosk alloc] init];
self.window.rootViewController = [_KioskViewController initWithNibName:#"iPadKioskView" bundle:nil andKiosk: _Kiosk];
[_Kiosk release];
[self.window makeKeyAndVisible];
return YES;
}
#property basically just declares two methods: setFoo: and foo. #synthesize will create the method bodies for those methods, which will correctly retain the new value and release the old one.
Assigning directly to the instance variable, however, doesn't call those accessor methods. It just makes the pointer point to a new object. If you're going to manually set the instance variable, then you need to make sure to correctly retain new value and release the old one.
Note that [self setFoo:someFoo] and self.foo = someFoo will both call the accessor method, while foo = someFoo just sets the instance variable directly, bypassing your setter method.
Your posted code gave the answer. You set _Kiosk already, so don't release it. There is no need to assign _Kiosk in the initWithNibName:etc. anymore. Just release the Kiosk in the dealloc method. If you want to change the Kiosk, use class.Kiosk = so the property is invoked, which is bound to retain it and release the previous kiosk.
You definitively have to retain your kiosk parameter in the [init..] method. Otherwise it will be released, and you get an error soon or later.
One way is to do it yourself by calling retain on it, or you can use a synthetized setter, if it is defined as (nonatomic, retain).

Problem with NSObject init method

I have a problem with the init() method of a standard NSObject. I wrote a class (EFAPersistence) which is a subclass of NSObject. EFAPersistance has a attribute called efaDatabase.
EFAPersistence.h
#interface EFAPersistence : NSObject {
FMDatabase * efaDatabase;
}
#property (assign) FMDatabase * efaDatabase;
Everytime an instance of EFAPersistance is created I want to assign efaDatabase a value from my AppDelegate.
EFAPersistence.m
#implementation EFAPersistence
#synthesize efaDatabase;
- (id)init {
if (self = [super init]) {
efaDatabase = [[NSApp delegate] efaDatabase];
}
return self;
}
#end
This way of assigning does not work. But it works if I put the code in a normal method. So I am sure that efaDatabase is correctly instantiated in my AppDelegate. It's just not working in my init() method. That's why I have the feeling that NSApp is not working inside the init() method.
That's how the important AppDelegate code looks like.
AppDelegate.h
#interface AppDelegate : NSObject <NSApplicationDelegate> {
FMDatabase * efaDatabase;
}
AppDelegate.m
- (id)init {
if (self = [super init]) {
NSString * databasePath =
[[NSBundle mainBundle] pathForResource:#"efa" ofType:#"sqlite"];
self.efaDatabase = [FMDatabase databaseWithPath:databasePath];
if (![efaDatabase open]) {
NSLog(#"Couldn't open database: %#", databasePath);
// TODO: Create a database here
}
self.db = [[EFAPersistence alloc] init];
}
return self;
}
As you can see I am calling the init method. I also affirmed this by using NSLog(). init() is called. The attribute I am trying to assign in EFAPersistence is also created before init() is called.
To sum everything up:
How can I make this work within the init() method so I do not have to write boiler plate code in all my methods of EFAPersistence?
It looks to me that your AppDelegate is unset when you try to create the EFAPersistance object the first time. This is on below line in AppDelegate.m
self.db = [[EFAPersistence alloc] init];
I imagine the app delegate is set after the init is done (returned).
This way of assigning does not work. But it works if I put the code in a normal method. So I am sure that efaDatabase is correctly instantiated in my AppDelegate. It's just not working in my init() method. That's why I have the feeling that NSApp is not working inside the init() method.
NSApp works fine.
Quoting epatel:
I imagine the app delegate is set after the init is done (returned).
Correct. The nib loader completely instantiates each object (including the app delegate, if it's in a nib), then sets it as the value of any properties it's connected to. These are two separate operations; it will not set a not-yet-initialized object as the application delegate.
Quoting you (Jens) again:
The question is how to assign efaDatabase in EFAPersistences only once . There are other methods like awakeFromNib and viewDidLoad etc. But those are not available in a plain NSObject subclass.
Incorrect. awakeFromNib is sent to every object in a nib after the object has been initialized.
That said, I'm curious as to why you have EFAPersistence in a nib. From its name, it doesn't sound interface-related. Shouldn't the app delegate own the EFAPersistence, and the EFAPersistence own the database directly?