I have a custom init method for my SecondViewController : UIViewController
-(id) initWithFirstViewController:(FirstViewController *)theFirstViewController
{
self = [super init];
fvc = theFirstViewController;
return self;
}
So in my FirstViewController I call this init method with an instance of the FirstViewController as a parameter. Somewhere else in the SecondViewController I use this passed intance:
[fvc setSomething];
The method is executed but I get a warning:
Method -setSomething not found (return type defaults to id)
How to fix this?
In this case, it's a matter of #importing the corresponding .h file so the compiler knows about the method.
Additionally, you should retain theFirstViewController as chances are that it gets released and a different object is created at exactly the same memory location (to which fvc is still pointing). So you should do fvc = [theFirstViewController retain]; as you are "holding on to" the first view controller (you want to make use of it later on). Don't forget to release it in your dealloc.
Related
I wouldn't normally ask questions like this, but I really cant get my head around it. I have a class 'GetTableInfoFromDatabase', which connects to my database and downloads some JSON. This works great from the first screen of my tab-bar application. I can call the getNewData method as much as I want to effectively refresh the data.
My problem arises when I try and create an instance of the 'GetTableInfoFromDatabase' class and call the very same method from another tab. I get the following error:
*** -[GetTableInfoFromDatabase respondsToSelector:]: message sent to deallocated instance 0x1d89e830
The funny thing is, i'm using ARC. The culprit (in my opinion) is ASIHTTPRequest. I have had to enable -fno-objc-arc to get the project to compile. This library is used in the GetTableInfoFromDatabase class.
Here is the class:
- (void) getEventDataWithSender:(id)sender{
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:#"-------.com/getdata.php"]];
[request setDelegate:self];
NSLog(#"Running!");
[request startAsynchronous];
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
self.managedObjectContext = appDelegate.managedObjectContext;
}
And this is how i'm calling it:
GetTableInfoFromDatabase *getInfo = [[GetTableInfoFromDatabase alloc]init];
[getInfo getEventDataWithSender:self];
I've even changed the order of the tabs around, so the first tab to be displayed purely just calls this method, nothing else. Not even before the 'GetTableInfoFromDatabase' has been previously initialised by the class that initialised it first last time. Still crashes.
Has anyone got any ideas? This is so frustrating!
You need to assign that variable to a property if you plan on exposing it to other view controllers. ARC will, and should, immediately deallocate getInfo after this code executes:
GetTableInfoFromDatabase *getInfo = [[GetTableInfoFromDatabase alloc]init];
[getInfo getEventDataWithSender:self];
So if this line is included in say viewDidLoad: and nothing else refers to getInfo in that method, it will be immediately released. Why, because you haven't told the compiler that it should retain it.
So in the view controller that's exposing this class, on whatever tab it might be a child of... you would do something like this:
ViewController.h
#class GetTableInfoFromDatabase; // forward declaration
#interface ViewController : UIViewController
#property (strong, nonatomic) GetTableInfoFromDatabase *getInfo;
ViewController.m
#implementation ViewController
#synthesize getInfo = _getInfo;
- (void) viewDidLoad {
[super viewDidLoad];
self.getInfo = [[GetTableInfoFromDatabase alloc]init]; // assign your value to a property
[self.getInfo getEventDataWithSender:self];
}
So when you declare your property as Strong in your header, it will maintain a strong reference to it. #Synthesize getInfo = _getInfo means that it will create a getter and setter for self.getInfo around an instance variable named _getInfo. If you didn't want to expose it as a property, just an instance variable... you could do this:
ViewController.h
#class GetTableInfoFromDatabase; // forward declaration
#interface ViewController : UIViewController {
GetTableInfoFromDatabase _getInfo;
}
ViewController.m
#implementation ViewController
- (void) viewDidLoad {
[super viewDidLoad];
_getInfo = [[GetTableInfoFromDatabase alloc]init]; // assign your value to a property
[_getInfo getEventDataWithSender:self];
}
By default, the compiler will maintain a strong reference to that instance variable unless otherwise specified. You can have weak references as well, and all of those options are pretty well documented. So with ARC, or plain old memory management in general, you need to make an instance variable or property if you want it to hang around for a while.
Honestly... all ARC is doing for you is keeping you from having to call retain and release. Before ARC, setting that property would look like this:
GetTableInfoFromDatabase getInfo = [[GetTableInfoFromDatabase alloc]init];
self.getInfo = getInfo;
[getInfo release];
Now with ARC, the compiler just writes that code for you ;) Hope this helps!
[self.getInfo getEventDataWithSender:self];
Your GetTableInfoFromDatabase object is being deallocated, almost certainly because nothing is holding a strong reference to it. In your code above, getInfo is a local variable, so I would expect it to be released very shortly after this code, unless you are storing it somewhere.
Almost certainly, your dealloc in GetTableInfoFromDatabase does not clear the request's delegate. You should be holding the request in an ivar of GetTableInfoFromDatabase so that it can remove itself as delegate when it is deallocated.
As a side note, avoid prefacing ObjC methods with get. That has a special meaning in KVC (it means that the first parameter is supposed to be updated by reference). Typically the kind of method you have here would be prefaced with "fetch."
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);
I use the following pattern in my app and am transitioning to ARC. Basically, an object retains an instance of a controller and releases that controller when it is notified through a delegate protocol that it has finished. I don't use an iVar/property b/c startProcess can be called N times to process N things.
Example below:
// start a process in a controller
- (void)startProcess
{
MyController *controller = [[MyController alloc] init];
// set the delegate, the delegate is defined as (nonatomic, assign)
controller.delegate = self;
[controller start];
}
// when the delegate is notified, release the controller
- (void)myControllerDidFinish:(MyController):controller
{
// do something with results
[controller release];
}
When the above implementation is converted to ARC, the controller is no longer retained after startProcess concludes so the processing doesn't occur and the delegate message is never received.
QUESTION: When converting my project to use ARC, how would the above implementation be modified to work correctly w/o creating iVars in the object instantiating the controller? There is a similar example in Apple's documentation to transition to ARC but it involves using blocks. I'd rather not replace the delegate protocol with completion blocks.
EDIT: added comment in code re how delegate is defined
EDIT: clarified first para to explain why an iVar/property to hold the controller won't work
Why not just create an NSMutableArray instance variable, pendingControllers, and adding your controller there? Since arrays retain their members, your code would look like this:
// start a process in a controller
- (void)startProcess
{
MyController *controller = [[MyController alloc] init];
// set the delegate, the delegate is defined as (nonatomic, assign)
controller.delegate = self;
[controller start];
if (pendingControllers == nil) {
pendingControllers = [[NSMutableArray alloc] init];
}
[pendingControllers addObject:controller];
[controller release];
}
// when the delegate is notified, release the controller
- (void)myControllerDidFinish:(MyController):controller
{
// do something with results
[pendingControllers removeObject:controller];
if ([pendingControllers count] == 0) {
// if ARC is enabled, remove the call to -release.
[pendingControllers release], pendingControllers = nil;
}
}
This avoids the problem. Completion blocks are the right answer, and they’re what Apple is using going forward, but this method will work for now.
Usually, it is the controller's responsibility to retain itself while it completes a task. If your controller runs a task on a background thread, then it should automatically be retained by the instance of NSThread. If data is being fetched over the network using NSURLConnection, the controller should be retained as the delegate.
If you are not doing a task like this, you can use synthetic circular retains to retain the controller while the task is being carried out. This can be done by creating an object, I will call it ObjectRetainer, that simply has a __strong id property. When the controller begins its task, it should have a __strong ObjectRetainer instance variable that gets set to a new ObjectRetainer that retains the controller. This way, the controller is retaining an ObjectRetainer that is retaining the controller, thus preventing either one from being deallocated.
When the controller completes its task and has called all necessary delegate methods, it should set the ObjectRetainer instance variable to nil. This will release the ObjectRetainer, that in turn will release the controller.
The ObjectRetainer interface might look something like this:
#interface ObjectRetainer : NSObject {
__strong id object;
}
#property (nonatomic, strong) __strong id object;
#end
You should declare an ivar in the controller's header: __strong ObjectRetainer _retainer. Then, in the controller's start method:
- (void)start {
...
_retainer = [[ObjectRetainer alloc] init];
_retainer.object = self;
}
When the controller is done, simply set _retainer to nil:
- (void)performBackgroundTask {
....
[delegate myControllerDidFinish:self];
_retainer = nil;
}
more simply: make the controller a data member... #synthesized method will do the magic.
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).
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.