Scope of an object, objective-c, CLLocationManager - objective-c

I'm still pretty new to programming so I have somewhat of a noob question. When you have an instance variable, in my case of type CLLocationManager, in my appDelegate.m file, I thought I could allocate and initialize my CLLocationManager instance variable in the applicationDidFinishLaunching method. And then I could use a button to startUpdatingLocation in a different method (since I'm calling it from another class). This doesn't seem to work and I'm thinking that I needed to alloc/init in the same method I startUpdatingLocation. Is that true? Do I need to stopUpdatingLocation in the same method? My code is below:
(locationManager is declared as a property)
- (void)stopUpdating {
[locationManager stopUpdatingLocation];
}
- (double)distanceTraveled {
return distanceTraveled;
}
- (void)applicationDidFinishLaunching:(UIApplication *)application {
// Override point for customization after application launch
[window addSubview:rootController.view];
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[window makeKeyAndVisible];
}
- (void)startUpdating {
[locationManager startUpdatingLocation];
}
It seems like I should be doing it more like:
- (void)startUpdating {
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager startUpdatingLocation];
}
If I am supposed to do it this second way, is it because that the scope of the CLLocationManager object is only for the method it is in? I thought having it as an instance variable I would be able to use it in other methods and I could have a separate method for startUpdatingLocation and stopUpdatingLocation. Thanks.

What you originally thought is correct. If you have an instance variable that variable remains available to you throughout the life of the object (in this case your app delegate).
If what you're doing isn't working, it's because of some other issue. you don't need to allocate a new CLLocationManager each time you call startUpdating.

Related

How do UIAlertView or UIActionSheet handle retaining/releasing themselves under ARC?

I want to create a similar class to UIAlertView which doesn't require a strong ivar.
For example, with UIAlertView, I can do the following in one of my UIViewController's methods:
UIAlertView *alertView = [[UIActionSheet alloc] initWithTitle:nil
message:#"Foo"
delegate:nil
cancelButtonTitle:#"Cancel"
otherButtonTitles:nil];
[alertView show];
... and the actionSheet will not be dealloced until it is no longer visible.
If I were to try to do the same thing:
MYAlertView *myAlertView = [[MYAlertView alloc] initWithMessage:#"Foo"];
[myAlertView show];
... the myAlertView instance will automatically be dealloced at the end of the current method I am in (e.g. right after the [myAlertView show] line).
What is the proper way to prevent this from happening without having to declare myView as a strong property on my UIViewController? (I.e. I want myView to be a local variable, not an instance variable, and I would like the MYAlertView instance to be in charge of its own lifecycle rather than my UIViewController controlling its lifecycle.)
Update: MYAlertView inherits from NSObject, so it cannot be added to the Views hierarchy.
UIAlertView creates a UIWindow, which it retains. The alert view then adds itself as a subview of the window, so the window retains the alert view. Thus it creates a retain cycle which keeps both it and its window alive. UIActionSheet works the same way.
If you need your object to stay around, and nothing else will retain it, it's fine for it to retain itself. You need to make sure you have a well-defined way to make it release itself when it's no longer needed. For example, if it's managing a window, then it should release itself when it takes the window off the screen.
If you add it as a subview of another view it will be retained. When the user selects and action or dismisses it, then it should call self removeFromSuperview as it's last act.
I've done my own AlertView with a little trick.
Just retain the object himself and release it on action. With this, you can call your custom alert vies as native one.
#import "BubbleAlertView.h"
#import <QuartzCore/QuartzCore.h>
#interface BubbleAlertView ()
...
#property (nonatomic, strong) BubbleAlertView *alertView;
...
#end
#implementation BubbleAlertView
...
- (id)initWithTitle:(NSString*)title message:(NSString*)message delegate:(id)delegate cancelButtonTitle:(NSString*)cancelButtonTitle okButtonTitle:(NSString*) okButtonTitle
{
self = [super init];
if (self)
{
// Custom initialization
self.alertView = self; // retain myself
//More init stuff
}
return self;
}
...
//SHOW METHOD
- (void)show
{
// We need to add it to the window, which we can get from the delegate
id appDelegate = [[UIApplication sharedApplication] delegate];
UIWindow *window = [appDelegate window];
[window addSubview:self.view];
// Make sure the alert covers the whole window
self.view.frame = window.frame;
self.view.center = window.center;
}
- (IBAction)btPressed:(id)sender
{
//Actions done
[UIView animateWithDuration:0.4f animations:^{
self.vContent.alpha = 0.f;
} completion:^(BOOL finished) {
[self.view removeFromSuperview];
self.alertView = nil; // deallocate myself
}];
}
You need to retain it somehow until it is released.
I do not really understand why you cannot implement it as subclass of UIView. Then you could use the view hierarchy as the keeper of a strong reference (retain +1). But you will have good reasons for not doing so.
If you don't have such a thing then I would use an NSMutableArray as class varialbe (meaning statc). Just declare it in the #interface block and initialize it with nil:
#interface
static NSMutableArray _allMyAlerts = nil;
provide an accessor.
-(NSMutableArray *) allMyAlerts {
if (_allMyAlerts == nil) {
_allMyAlerts = [[NSMutableArray alloc] init];
}
return _allMyAlerts
}
Within the init method do the following:
- (id) init {
self = [super init];
if (self) {
[[self allMyAlerts] addObject:self];
}
}
You will invode some method when the alert is dismissed.
- (void) dismissAlert {
// Do your stuff here an then remove it from the array.
[[self allMyAlerts] removeObject:self];
}
You may want to add some stuff to make it mutli threading save, which it is not. I just want to give an example that explains the concept.
allMyAlert could be an NSMutableSet as well. No need for an array as far as I can see. Adding the object to an array or set will add 1 to the retain count and removing it will reduce it by 1.

ios CLLocationManager won't get location second time

I have a getLocation method that initializes my CLLocationManager and calls startUpdatingLocation. After I get the user's location, I call stopUpdatingLocation.
Later, when the user presses refresh, I call a method reloadData which calls getLocation so I can get the user's latest location. However, this never calls the locationManager:didUpdateToLocation:fromLocation
method.. so I never get the user's latest location. What could be the issue?
-(void) getLocation {
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
self.locationManager.delegate = self;
[self.locationManager startUpdatingLocation];
}
- (void) locationManager: (CLLocationManager *)manager
didUpdateToLocation: (CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
if (oldLocation!= nil) { // make sure to wait until second latlong value
[self setLatitude:newLocation.coordinate.latitude];
[self setLongitude: newLocation.coordinate.longitude];
[self.locationManager stopUpdatingLocation];
self.locationManager.delegate = nil;
self.locationManager = nil;
[self makeUseOfLocationWithLat:[self getLatitude] andLon:[self getLongitude]];
}
}
-(void) reloadData {
[self getLocation];
}
Is it really necessary to allocate a new CLLocationManager? Try just allocate it once (in your init for example) and just call startUpdatingLocation and stopUpdatingLocation on demand.
For me, this solution works great.
Do you move during testing this? Because I think the callback will only be triggered, when:
you call startUpdatingLocation
your location changes
the location manager gets better results (more detailed)
So I think for your use-case: as you don't move, the callback locationManager:didUpdateToLocation:fromLocation: will only be called once after you hit refresh and as there is no old location, you will not go into the if case.
Another thing: you should definitely have only one CLLocationManager in your class. I only use one per project/application (wrapped in a singleton). This is the preferred way to do! Also I think this will retain the oldLocation value so that your problem may be resolved by changing this.
Best,
Christian
you can try this ,this will help u to update for every time when open the page.Cause if u put startUpdatingLocation in viewdidload ,it will get load only for the first time
-(void)viewWillAppear:(BOOL)animated
{
[locationManager startUpdatingLocation];
}

Releasing Main View Controller (iOS)

I've searched for this on Apple's site and can only seem to find documentation using Storyboards or Navigation Controllers, neither of which I'm using. It's an incredibly straightforward question about memory management.
I created a completely blank application. In my AppDelegate's didFinishLaunchingWithOptions function I'm creating an instance of a View Controller which I've built. My design (which itself could be a problem) is to have a 1:1 relationship between View Controllers and Views.
So the main menu of my application, which is a launching pad for everything is in MenuViewController.h/m.
In .h:
MenuViewController *m;
In .m:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
// Override point for customization after application launch.
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
m = (MenuViewController *)[[MenuViewController alloc] init];
m.window = self.window;
[m doStuff]; // handful of functions, not actually called this
//[m release]; // doesn't make sense to me
return YES;
}
This is where I'm confused. I want this to exist for basically the entirety of the application life cycle. But I'm also under the impression that you should (in the scope of the current function) release anything you allocate. If you need it beyond that, you should retain it elsewhere first. Is this not true?
My fundamental question is...where should I be releasing this View Controller? Is there anything else I've said that seems out of whack?
The initialization is wrong. You don't assign a window to controller, you assign a controller to window:
// window creation code here
...
m = [[MenuViewController alloc] init];
[window setRootViewController:m]; // window does retain for m
[m release]; // so we release it here
[self.window makeKeyAndVisible];
return YES
}
You're right. Generally you should release anything you create in a scope. But in this case you want ownership of the view controller. In this case you need to release the object in the dealloc method of your app delegate:
- (void)dealloc {
[m release];
[super dealloc];
}
Alternatively you could define a #property for your view controller with retain flag and then do this:
MenuViewController *viewController = [[MenuViewController alloc] init];
self.m = viewController;
[viewController release];
Btw, you don't need to cast to MenuViewController in either case.
EDIT: I completely missed that you don't add your view controller to your window. Good point #Eimantas.

initWithNibName: what kind of custom initialization?

edit : ok, what i've learned is you might choose between initWithNibName or initWithCoder, depending if you use a .xib or not. And "init" is just not the constructor method for UIVIewController.
This might seem to be a fairly simple question, but I'm not sure about the answer : I've read that this method "is only used for programatically creating view controllers", and in the doc : "It is loaded the first time the view controller’s view is accessed"
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
// Custom initialization
}
return self;
}
Ok so to understand it a bit more :
What would you write as "custom initialization" into this method?
When should you implement this method, like that in the code, if you can just write it after allocating your viewController (example : MyVC *myvc = [[MyVC alloc] initWithNibName:...bundle...];)
Thanks for your answer
Usually I do this:
// Initialize controller in the code with simple init
MyVC *myVC = [[MyVC alloc] init];
Then do this in my init method:
- (id)init {
self = [super initWithNibName:#"MyVC" bundle:nil];
if (!self) return nil;
// here I initialize instance variables like strings, arrays or dictionaries.
return self;
}
If controller needs some parameters from the initializee, then I write custom initWithFoo:(Foo *)foo method:
- (id)initWithFoo:(Foo *)foo {
self = [super initWithNibName:#"MyVC" bundle:nil];
if (!self) return nil;
_foo = [foo retain];
return self;
}
This allows simplification of initialization as well as extra initializers for your view controller if it can be initialized from different locations with different parameters. Then in initWithFoo: and initWithBar you'd simply call init which calls super and initializes instance variables with default values.
It's an init method, so you initialize everything you need to be initialized when you begin your work in your view controller. Every object ivar will be automatically initialized to nil, but you can initialize a NSMutableArray to work with or an BOOL that you want to be a certain value.
You implement this method every time you have something to initialize, as stated previously. You generally don't initialized things after allocating your view controller, that way you don't need it to do it every time you use your view controller (as you may use it at different place in your application). It's also the best practice.
I usually put in there configuration stuff that I know it wont change.
For example, the ViewController's title:
self.title = #"MyTitle";
Or if this is one of the main ViewController in a TabBar application. That is, it owns one of the tabs, then I configure its TabBarItemlike this:
UITabBarItem *item = [[UITabBarItem alloc] initWithTitle:#"something"
image:[UIImage imageNamed:#"something.png"]
tag:0];
self.tabBarItem = item;
There is all sorts of things you can do there.

Memory managment question

I have a button with IBAction, which shows another window:
-(IBAction)someButtonClick:(id)sender
{
anotherView = [[NSWindowController alloc] initWithWindowNibName:#"AnotherWindow"];
[anotherView showWindow:self];
}
I worry about memory management in here. I allocate an object in this IBAction and don't released it. But how can i do it? If i released this object after showing, window will closing immediately.
The view is stored in an instance variable and you have access to it anywhere in your class. Release it in the code that dismisses the view.
Since anotherView is an instance variable you can release it in your dealloc method. But then you still have a memory leak, since every time your button is clicked a new instance of the window controller is created, but only the last one can be freed. You really should use accessors for this. Here is my suggestion:
- (NSWindowController *) anotherView;
{
if (nil == anotherView) {
anotherView = [[NSWindowController alloc] initWithWindowNibName:#"AnotherWindow"];
}
return anotherView;
}
- (void) setAnotherView: (NSWindowController *) newAnotherView;
{
if (newAnotherView != anotherView) {
[anotherView release];
anotherView = [newAnotherView retain];
}
}
- (void) dealloc;
{
[self setAnotherView: nil];
[super dealloc];
}
- (IBAction) someButtonClick: (id) sender;
{
[[self anotherView] showWindow: self];
}
If you use a Objective-C 2.0 property you don't have to write the setter.
And also you should rename your instance variable, the name should reflect what it is. And a View is not a Window Controller.