Using LocationManager from another class than Main - iphone

i'm new here,
i'm new in iphone development,
i'm new in Objective-c
and i'm new in engligh speaking, so don't hate me :P
I'm creating an application that will use the location manager to display the distance between the own position and the positions of a list of shops, like AroundME.
Now, i give the locationManager property to my main class and in the .m of that class, precisely in the viewDidLoad method, i make: [locationManager startUpdatingLocation].
It all works fine, but the problem is:
i need to know my position in other classes than the main, but i don't think that is a good idea making [locationManager startUpdatingLocation] every time i need it, is not time overhead?
The view of my main class is always below the others, so can i retrieve the updated location in my other classes? In what way?
Creating another instance of my main class is not useful, i think, but the locationManager, once started, doesn't stop until my main class is released, isn't true? So i think that it is accessible in some way.
Thanks for your attention and sorry for my awful english : )

I'm going to assume that you set your main class as the CLLocationManagerDelegate in this case. Your delegate will receive locationManager:didUpdateToLocation:fromLocation: messages when the location is updated and in that method you can use NSNotificationCenter to post messages which instances of other classes can add themselves as observers for. See the documentation for NSNotificationCenter for more info on adding observers and posting notifications.
To get the initial location in other classes besides main, you'll probably have to have a reference to the main class and retrieve it using the location property of CLLocationManager because your observers will only receive notifications when the location has changed.

Related

How do I send messages to my view controller instance?

I want to be able to send a message from my mkmapview delegate class to one of my view controllers. I normally send messages by allocating/initing a new instance of that class, but this time I'm trying to send a message to an instance that's already there.
What are the various ways I can do this? Is there a preferred way?
Edit:
I found my solution. Not sure if it's the best way, but it's certainly the easiest as far as I can see and perfect for what I need.
What I did was have a static myViewController variable inside my viewController, then have it be set to self in my viewDidLoad. Last, I added a static method to get the static variable.
Either I don't understand what you are asking, or it is quite simple. You should have a reference to the instance (say it is called myInstance), and then you can simply send a message like:
[myInstance myMessage: param1 bla: param2];
Of course you should only send messages the receiver (myInstance) can understand. Which they are, depends on the class of the receiver, and on any categories that are defined for its class and in scope.
I think you should use NSNotification and NSNotificationCenter to pass messages. Just see the iPhone SDK documentation and you will get how to do that.

How to call a specific class in any app from notification?

in my app, i have generated a local notification. when the notification will appear it starts the app from beginning, but i want to go a specific class, not in app delegate and view controller classes. MAy i code in the methods like application will enter foreground delegates in app delegate class. or somewhere else.
Thanks in advance.
Well, there is no "app delegate class". It is a protocol that any class may agree to honor. Oftentimes it is a subclass of UIApplication, but you can make it any class you like, so long as you agree to support the ApplicationDelegateProtocol.
Nevertheless, your localNotifications will be delivered there, either via the front door on activation, or through the didReceiveLocalNotification: method. From there you can call any method in any object that you like. If you need specifics as to where to dispatch it, you can include a dictionary in the notification's userInfo property.
edit: My mistake. The name of the protocol is UIApplicationDelegate.

best way to use CoreLocation across multiple views

I have two views in my app, one is a general view where CoreLocation works away calculating the users location while the user is doing other stuff in the view. The second view is accessed by the user when they touch a button allowing them to locate themselves more accurately using a mapview and MapKit, i would like the mapview in this view to show the location that CoreLocation has already identified in the first view AND to continue displaying this location based on updates from CoreLocation in the other view.
Is the best way here to create a singleton that encapsulates the CoreLocation stuff and have this referenced in the view with the map, or to use notifications ? or to use some other better practice for my scenario ?
Thanks
I have a couple of apps that use CoreLocation in multiple places. From what I've read, you definitely want there to be just one instance of CLLocationManager. It's worked great as a singleton for me.
Hope this helps!
If I were you, I would do it this way:
Decide which view is going to be always loaded.
I assume, you want CalculatingView is loaded all the time, and MapView will be loaded/unloaded based on the user action.
Allocate and initialize a pointer to CLLocationManager inside CalculatingView. This will provide location property and also call delegate messages. Since the CalculatingView is loaded and retained, this pointer is always working too.
Set CLLocationManager's delegate to be CalculatingView, which might also be called self, if this view has allocated and initialized CLLocationManager pointer.
Implement delegate methods of CLLocationManager, in CalculatingView
If you like to, you can have MapView to be allocated and initialized within CalculatingView. But it's ok to have it in other places, as long as you can send message to MapView. Make sure they are valid by checking if it's not nil or if it respondsToSelector.
When the CLLocationManager's delegate, which is CalculatingView receives messages, send a message to MapView.
It's like relaying messages, but the messages that MapView should respond to don't have to be the same messages sent to CalculatingView like delegate method calls from CLLocationManager
By checking if MapView is valid, meaning if it's loaded to be displayed, you can decide to send messages to MapView or not
The essence is to decide which view is loaded consitently, to use delegate methods for sending(or relaying) messages to other pointers(in this cases, MapView pointer).
The singleton is good, but unless you are going to use CLLocationManager from multiple places, like more than 3~4 places, it's not that necessary, I think
Hope I didn't confuse you. Based on what you posted, it seems like this way can be simple solution for your goal. If I didn't catch your true intention, please let me know.
I am not sure this is the best way, but I've been setting up my main controller (the one that is loaded first) as a location manager delegate. When the location updates it fires off a notification with the new location as the notification object. Any controllers listening can then use that data however they need it.
As an aside, Apple's LocateMe app instantiates the location manager three times. So, by their example, having multiple LocationManagers might not be a problem.
From what I've read, best practice for this is to add CLLocationManager to your App Delegate as you can access it from any view.
Short sample code to put in your view where you need the CLLocationManager
....imports....
#implementation YourViewController
- (void)viewDidLoad {
self.myLocationManager = [[UIApplication sharedApplication] delegate].yourLocationManagerVarName;
}
#end
Hop that helps.
Maybe you should consider a MVC oriented approach. From your description your are missing a model layer representation of your user. Defining a simple User class with a basic CLLocation property would be a first step.
#interface User {}
#property (nonatomic, retain) CLLocation *location;
#end
#implementation User
#synthesize location;
- (void)dealloc {
self.location = nil;
[super dealloc];
}
#end
The same instance of the User will be passed to your view controller. It may be created in the app delegate.
Next create location services object for your app. It will start the CLLocationManager, and give the location to your user. You may have to set the GPS accuracy, ignore frames you don't want, and implement basic LBS logic here.
At this point, you have a feature full app, without any UI. This is a good design in the way it can be reused and tested.
Now stack your UI on top of that. Give your root controller a pointer to the User instance in your app delegate. Your view controller pass this pointer to modals / navigations view controllers it creates.
This controller start observing User's location changes in their viewDidLoad and react accordingly.
- (void)viewDidLoad {
[self observeValueForKeyPath:#"location" ofObject:self.user change:0 context:NULL];
}
Your view controller would also register for notification raised by your location services objects to display an alert to the user.
Based on other answers:
there is no real penalty to create multiple CLLocationManager instances in your code. The only side effect is that the api is asynchronous, thus you have to wait to get a valid location in your view controller. You can try to get the current location from the location manager on your viewDidLoad using locationManager.location API.
don't share stuff from your app delegate. This prevent code reuse. What if you reuse your views and you app delegate don't have a location manager ?
if you need more code, please ask.

Releasing a delegating object in its delegate callback method

I'm trying to figure out what the recommended practice is for the following situation. Certain objects, such as CLLocationManager or MKReverseGeocoder, send their results asynchronously to a delegate callback method. Is it OK to release that CLLocationManager or MKReverseGeocoder instance (or whatever class it may be) in the callback method? The point is that you no longer need that object around, so you tell it to stop sending updates, set its delegate to nil, and release the object.
Pseudo code:
#interface SomeClass <CLLocationManagerDelegate>
...
#end
#implementation SomeClass
...
- (void)someMethod
{
CLLocationManager* locManager = [[CLLocationManager alloc] init];
locManager.delegate = self;
[locManager startUpdatingLocation];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
// Do something with the location
// ...
[manager stopUpdatingLocation];
manager.delegate = nil;
[manager release];
}
#end
I am wondering if this usage pattern is considered to be always OK, if it's considered to be never OK, or if it depends on the class?
There is an obvious case where releasing the delegating object would go wrong and that is if it needs to do stuff after it has notified the delegate. If the delegate releases the object, its memory may get overwritten and the application crashes. (That appears to be what happens in my app with CLLocationManager in a particular circumstance, both only on the simulator. I'm trying to figure out if it is a simulator bug or if what I am doing is fundamentally flawed.)
I have been searching and I cannot find a conclusive answer to this. Does anyone have an authoritative source that can answer this question?
No. That pattern is always considered to be wrong. It breaks the Cocoa Memory Management Rules. The manager object was passed in as a parameter. You did not obtain it by new, alloc or copy, nor did you retain it. You therefore must not release it.
This is a very good question, I waited few hours in hope someone would give a sufficient answer, but because no one even replied, I'll give it a try. First I'll comment on your approach, then I try to suggest how I would go around this.
It's definitely a very bad idea to release - thus deallocate an object from its delegate. Just think about how objects (like a CLLocationManager) do call their delegates - they just call them in the middle of some method. When call to delegate is finished, code's execution comes back to a method of an object that has already been deallocated. BAM!
Let's forget for a moment about the fact that this is a bad idea. I see two options how to fix that easily. First, autorelease instead of release gives an object a little longer time spam - it'd at least survive returning from delegate. That should be sufficient for most of the cases, at least if author of API did her job well and encapsulated logic behind the main API class (in case of CLLocationManager it might be waiting for GPS to turn off...). Second option would be to delay releasing (performSelector:withObject:afterDelay: comes to mind), but that's more of a workaround for badly implemented APIs.
So if releasing it is not a good idea, then what is ?
Well, what do you really gain by releasing a CLLocationManager ? Freeing those few bytes of memory is not going to save your app from terminating when system is out of memory. Anyway, is it really only once that you need the current user's location ?
I suggest that you encapsulate tasks related to CLLocationManager into a separate class, probably even a singleton - that class would become its delegate, and it would take care of communicating with CLLocationManager and informing your application about results (probably by sending NSNotification). CLLocationManager would be released from that class's dealloc, and never as a result of a delegate callback. stopUpdatingLocation should suffice, freeing few bytes of memory - well, you can do it when your app is entering background, but as long as your app runs, freeing those few bytes do not make any significant improvement in memory consumption.
** Addition **
It's natural, and correct, for a delegate to have ownership of an object for which it acts as delegate. But the delegate should not release the object as a result of a callback. There's one exception to this though, and it is the callback telling you processing is over. As an example for this is NSURLConnection's connectionDidFinishLoading: which states in documentation "The delegate will receive no further messages". You can have a class downloading bunch of files, each with a different NSURLConnection (having your class as delegate), allocating AND releasing them as download of files progress.
CLLocationManager's behavior is different. You should have only one instance of CLLocationManager in your program. That instance is managed by some code, probably a singleton - one that could be released when application goes into background, reinitialized when it wakes up. CLLocationManager's lifespan would be the same as of its managing class, which acts as delegate as well.
Just like Michal said there is absolutely no good reason to release the manager object for memeory savings.
Also, just likeJeremyP said, it would be completely incorrect pattern wise and design wise to release an object inside another function that only receives that object. It goes against all the rules.
However, what would be correct is to simply stop the updates and set the managers delegate to nil as you are already doing. So the only thing you need to remove is the [manager release] line.
My guess is that you are creating a manager in a local scope and therefore are trying to work out how to make sure the the manager gets released. The correct thing to do here would be to create a manager as an instance variable of your class and then just release it in the class dealloc.
Another reason why your pattern is a bad idea, is that for something like a CLLocationManager you generally want to tell it to stop receiving updates if the screen goes to sleep - you can only do that if you maintain a reference somewhere that you can tell to start/stop. And if you are maintaining a reference, then you might as well fully manage it.

More than 1 appDelegate object?

While fixing third-party code I've discovered a really brilliant idea) Guy was using 2 appDelegate objects in project xibs. I assume he thought that this would be some kind of singletone or such. But after some rethinking of that piece of code, I found that there is no technical restrictions on it.
Here is my example: simple project with navController and 2 views. Each with it's viewController. When app launched, first view is on screen. When user taps button, second view is pushed to navController. For now there is appDelegate object in MainWindow.xib. Now, if you'll add just the same appDelegate object to second view's xib. Now right when second view is pushed, you can see that one more instance of appDelegate is created and destroyed (if you'll override init and dealloc methods and insert log there).
Here I'm very surprised. Does it mean that only one appDelegte instance can be created? If yes, then why? appDelegate is just a NSObject subclass implementing UIApplicationDelegate protocol.
The appDelegate object created by xCode on every iphone project is the entry and exit point of an application. It does not make sence to have more than one instance of this class, if you do (besides perhaps some application settings being lost) which class does the applicaiton delegate to? Why can you only make one? Most probably this is because the class is implementing a Singleton patterns under covers so ensure only one instance of the app delegate is made, i bet that even when you try to alloc another one of these, the original app delegate is the only one kept. You can probably dig around the docs and find more info on apples site at http://developer.apple.com/iphone
UIApplicationDelegate is a protocol and doesn't have state in itself, thus there's nothing that prevents you to have several of them. Contrast this with UIApplication that has state and
provides sharedApplication singleton accessor
It should be totally possible to replace UIApplication's delegate property on the fly. I don't see much of the benefit, though.
I think what's happening here is that there is an instance of the AppDelegate class in the second Nib, but no other objects are retaining it. Therefore it gets created and immediately released. If you added a retained property to the view controller that connected to the AppDelegate, then it wouldn't get released immediately.
You can have multiple objects that implement the UIApplicationDelegate protocol, but it's not usually done because 90% of the behavior would be identical in all cases.
I think you could do something along these lines:
Note the old UIApplication.delegate.
Create your instance of UIApplicationDelegate with the old delegate as parameter.
Make sure to call the old delegate in each method you implement.
Make it return the old delegate in the - (id)forwardingTargetForSelector:(SEL)aSelector method.
Replace the [UIApplication sharedApplication].delegate with yours.
It replaces the original app delegate with your, making sure that the old delegate will still be called, especially if you did not override each and every method the UIApplicationDelegate protocol defines.