iPhone - When to release the application delegate? - iphone

I have an object that is used between all my viewControllers so I have stuck it inside the application delegate. (I assume this is the correct place?).
If an action inside a viewController fires that needs to send something to said object I am performing the following:
- (IBAction)sliderMoved:(id) sender{
MyAppDelegate *delegate = [[UIApplication sharedApplication] delegate];
[[delegate myObject] setSpeed:(int)slider];
// [delegate release];
}
I am a bit concerned I am not releasing the delegate object anywhere, is this ok? If I remove the commented line [delegate release] it just crashes the application.

You don't own the delegate object that you create in this snippet. You haven't created it with alloc, new, or a copy. Nor have you retained it. So, it is not your responsibility to release it.
As for putting an object in the Application delegate just to be able to access it from other parts of your code - that is poor OOP design IMO.
Edited to add
I suppose I had better give an example
Suppose you have a class MyClass that you want to create an object of that you can pass around.
You create it in the Application delegate, which it seems you are already doing:
MyObject *myObject = [[MyObject alloc] init];
Then you create another view controller - which you would normally do, except that this view controller has a property:
#property(retain) MyObject *object;
And then you set this property when you create the view controller:
YourViewController *vc = [[YourViewController alloc] init];
vc.object = myObject;
And, if you pass this object to other view controllers as you require.

Related

ViewDidLoad executes slowly while pushing viewcontroller

I was trying to push a viewcontroller B into navigation controller from A and then assigning some properties of B in A.
In this case, the assigning of properties was done and then viewDidLoad of viewcontroller A was executed.
Here, assigning properties in A should be done only after viewDidLoad of A has done.
For example,
[b.navController pushViewController:a animated:YES];
a.status = #"loaded";
Here, status was assigned first and then viewDidLoad of A was executed.
This happens only in iOS 7 whereas in iOS6 it works fine.
Can anyone please let me know where the problem is?
UPDATE: For me in some cases in iOS7, Push view is not working. How cna I debug and fix it?
Just access the viewcontroller.view (set any thing immediately after the alloc) property after the alloc init;
Which will loadview/viewdidload.
See Apple Documentation
In my experience, a UIViewController view is loaded lazily, no matter which iOS version you're working on. If you want to trigger a view load, and therefore its UIViewController viewDidLoad, you should access the view after the UIViewController is allocated. For example:
UIViewController *aViewController = [[CustomViewController alloc] init];
[aViewController view];
Make sure you don't code it as
aViewController.view
since that would generate a compiler warning.
So, in your case it would have to be something like this:
...
CustomViewController *a = [[CustomViewController alloc] init];
[b.navController pushViewController:a animated:YES];
[a view];
a.status = #"loaded";
Let me know if you have further problems with it.
You can know when a View Controller has been pushed onto the stack by implementing the UINavigationControllerDelegate and setting yourself as the delegate self.navigationController.delegate = self; then you will get this callback after every push
navigationController:didShowViewController:animated:
So you can check if the shown viewController is the one your interested in, then set your a.status.
I would suggest you call a delegate method once the view is loaded.
Set the delegate to be controller B.
and after viewDidLoad finishes (in controller A) call the delegate method. You can even pass parameters as you wish to the delegate.
Here's some example code:
Controller B:
a.delegate = self;
[b.navigationController pushViewController:a animated:YES];
Implement the delegate method:
- (void)controllerIsLoaded:(ControllerA *)controllerA status:(NSString *)status
{
a.status = status;
}
Controller A .h file:
#class ControllerA;
#protocol ControllerADelegate <NSObject>
- (void)controllerIsLoaded:(ControllerA *)controllerA status:(NSString *)status;
#end
#interface ControllerA : UIViewController
#property (nonatomic, weak) id <ControllerADelegate> delegate;
Controller A .m file:
- (void)viewDidLoad:(BOOL)animated
{
[super viewDidLoad:animated];
/* your viewDidLoad code here... */
if ([_delegate respondsToSelector:#selector(controllerIsLoaded:status)])
[_delegate controllerIsLoaded:self status:#"Loaded"];
}
Turn off animation for ios7, in my case its work
[b.navController pushViewController:a animated:NO];
a.status = #"loaded";
No documentation provides enough information to know exactly when viewDidLoad would be called.
UIViewController's documentation just says this
This method is called after the view controller has loaded its view hierarchy into memory
I would suggest that you create a custom initializer like this
- (id)initWithStatus:(NSString *)status {
}
But, if you are trying to use this variable to check if the viewController's view has 'loaded' or not, it may not be possible to do that because the pushViewController or presentViewController methods are not guaranteed to be synchronous.
Even in iOS 6, there was no explicit guarantee that the view would be loaded as soon as that method returned.
Please write the code in viewWillAppear method instead of viewDidLoad in next class i.e. where you are pushing the object to
-(void)viewWillAppear:(BOOL)animated
{
}
I'm understand of your question like this:
B *b = [[B alloc] init];
b.status = #"loaded";
[self.navigationController pushViewController:b animated:Yes];
If you want to pass a value from one controller to another means, you have to assign a value before using pushViewController method.

Objective-c / xcode - Access button state in different class

So I tried this in many different ways but I can't get it to work. Im trying to change the state of a UIbutton in a different class.
class1.h
#property (strong, nonatomic) IBOutlet UIButton *monthly;
class2.m
- (void)viewDidLoad
{
ViewController *vc = [[ViewController alloc] init];
vc.monthly.enabled = NO;
}
Whatever I try and where ever I put the code, the button state is not changing. When I log the state in class2.m:
NSLog(vc.monthly.enabled ? #"Yes" : #"No");
It always returns No, even if I just stated it as YES in my class2.m. Long story short: My button property is not updating from a different class. Please tell me if you need to see any more code and i'll update asap.
i think problem is with class instance. the following line create new instance
ViewController *vc = [[ViewController alloc] init];
that's why your button state is not changing you have to get reference of your previously created intstace no need to create new instance.
for this you can use AppDelegate file to declare property of class1.
see following code
AppDelegate.h
#Property(nonatomic, ratain) ViewController *vc;
AppDelegate.m
#synthesize vc;
now alloc & initialize vc whenever you need it like following.
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication
sharedApplication] delegate];
appDelegate.vc=[[ViewController alloc] init];
also don't forgot to import AppDelegate.h file where you write above code.
now using appDelegate.vc you can use all property of View Controller in all classes of you project.
The main problem is you are creating new instance each time when you are going to check the button state. But the button state is for the button which you have created in class1.h. So you have need that you should create button in Appdelegate class and fetch the instance from Appdelegate in the class where you are checking the status of button and check the status of button. I think it will help.
Access the button using the object of the ViewController class that is already in the stack. No need to creat a new object like ViewController *vc = [[ViewController alloc] init];. When you are doing this it creates a new object so you are not getting the write thing.

load data into uipickerview with NSMutableArray located in app delegate

basically i have parsed some data from XML into a NSMutableArray that is shared in the appDelegate.
in my secondViewController i have a uiPickerView that i am wanting to load the details of the array into it.
My question is... how?
i have briefly worked with uiPickerView's before and had to load the data in first to assign to the uiPickerView like so:
titleDB = [[NSMutableArray alloc] init];
[titleDB addObject:#"MR"];
[titleDB addObject:#"Mrs"];
[titleDB addObject:#"Ms"];
[titleDB addObject:#"Miss"];
[titleDB addObject:#"DR"];
[titlePickerView selectRow:1 inComponent:0 animated:YES];
but since the data is coming from the appDelegate i don't know how i should load it into the uiPickerview, is it something to do with the datasource?
I'm asking to throw code at me I'm just asking for the best way to do it.
Any help on this would be great
Thanks
Jonn4y
This is a common pattern. You will want to access UIApplication's sharedApplication instance. So assuming your appDelegate class is named YourAppDelegate, the array ivar in YourAppDelegate and viewController is titleDB then you could do this in your viewController's viewDidLoad method
YourAppDelegate *appDelegate=(YourAppDelegate *)[[UIApplication sharedApplication] delegate];
// assuming you are using #property and #synthesize for your ivars
self.titleDB=appDelegate.titleDB;
Good luck
Basically, you want to access the appDelegate object from random places in code. This is not an unusual request. Remember that Objective C is a superset of the C language. And as such, you can use a global variable. What more natural variable would there be in a Cocoa program than the app delegate, for the reasons stated above. So, in your appDelegate .h file, add:
<MyAppDelegateClass> * appDelegate;
Substitute MyAppDelegateClass for the name of your appDelegate class name. Then just include your appDelegate's .h file anywhere you want to use the appDelegate variable, and just use (in your example):
[appDelegate titleDB]
or create a local iVar:
NSMutableArray * titleDB = [appDelegate titleDBData];
Then in your app delegate method didFinishLaunchingWithOptions, add the following line:
appDelegate = self;

How to implement custom delegate methods for my view controllers

In my app there are two tabbars. In tab-0 parent class is "FirstViewController" and in tab-1 parent class is "SecondViewController". In "SecondViewController" i have declared protocol and custom delegate method. i want to pass the information in "FirstViewController"(FVC). So FVC has to assigned as a delegate.
Now my doubt is, right now i am in "SVC". How can i assign "FVC" as a delegate of "SVC"?
In "SVC"
[[self delegate] sendCoordinates:self];
Definition of method is in "FVC". To execute this method, first i need to assign "FVC" as a delegate.
I hope I am clear in explaining my problem.
Thanks in advance.
You need to set the delegate. Let me demonstrate:
`SVC.h`
#interface SVC
{
id _delegate;
}
- (void)setDelegate:(id)delegate; //sets the delegate
- (id)delegate; //gets the delegate
#end
#protocol SVCDelegate
- (void)sendCoordinates:(SVC *)svc;
#end
Then, in SVC.m, you call the delegate in exactly the same way as you showed in your question, so [[self delegate] sendCoordinates:self];
Now, in FVC, you'll need to #import SVC.h and initialise the object.
SVC*svcObject = [[SVC alloc] init];
[svcObject setDelegate:self]; //self now refers to FVC - I think you're missing this one
//work with the object and when you're done, get rid of it in dealloc:
[svcObject setDelegate:nil];
[svcObject release];
In the same class, implement - (void)sendCoordinates:(SVC *)svc and it will be called after you set the delegate.
I think you're missing the setDelegate: stage, which is why it doesn't work.
Hope it helps!
Note: In SVC, remember to retain the delegate, or it will become nil and no delegate methods will never be called. Don't forget to release that delegate once you're done.

Objective-C – Sending data from NSObject subclass to an UIViewController

I have an UIViewController which has a UITableView inside. In this view controller I want to display some data that I have downloaded from the internet. So for this I have created a helper class called OfficesParser which is supposed to do the following:
Download the data from the internet with ASIHTTPRequest
Process the data with a JSON parser
When finished, send the data back to my view controller
In my view controller I'm allocing and initing my helper class in -viewDidLoad like so:
self.officesParser = [[[OfficesParser alloc] init] autorelease]; //officesParser is a retained property
Then in -viewWillAppear: I call the the method for the officesParser object that will start the download process like so:
[self.officesParser download];
In my helper class OfficesParser ASIHTTPRequest has a delegate method that tells you when a queue has finished downloading. So from this method I want send the data to my view controller. I would think this would work, but it didn't:
- (void)queueFinished:(ASINetworkQueue *)queue {
NSArray *offices = [self offices];
OfficesViewController *ovc = [[OfficesViewController alloc] init];
[ovc setOffices:offices];
}
With this code in mind, how would you achieve what I'm trying to do with proper code?
You need to take a look at delegates and protocols. They're exactly what you're looking for, as they let classes communicate without having to persist a reference. Here is another explanation on them.
Your code:
OfficesViewController *ovc = [[OfficesViewController alloc] init];
Creates new instance property of OfficesViewController. Since it's a new instance it does not have a connection to the OfficesViewController you triggered after downloading and parsing process. To be able to comunicate b/w OfficesViewController and OfficesParser create a modified init method for OfficesParser that allows week pointer to the OfficesViewController.
#interface OfficesParser ()
#property(nonatomic,assign)OfficesViewController *ovc;
#end
#implementation OfficesParser
#synthesize ovc;
-(id)initWithDelegate:(OfficesViewController*)delegate{
ovc = delegate;
return [self init];
}
Now you can access your ovc delegate.
- (void)queueFinished:(ASINetworkQueue *)queue {
NSArray *offices = [self offices];
[ovc setOffices:offices];
}
Finally create your OfficesParser like that
self.officesParser = [[OfficesParser alloc] initWithDelegate: self];