How to get shared instance of location manger in synchronize - objective-c

Hy, I want to get the background location updates, so I wrote the below code and make it synchronized to keep it safe from multithreading. So I have two questions:
Do I really need to synchronize the piece of code, all I am doing in App is just running background task? I never created any special NSThread type to support multithreading and don't require as such?
Whenever I need to start the location updates, I call like this:
CLLocationManager *locationManager = [LocationTracker sharedLocationManager];
locationManager.delegate = self;
[locationManager startUpdatingLocation];
Is correct way to call?
+ (CLLocationManager *)sharedLocationManager {
static CLLocationManager *_locationManager;
#synchronized(self) {
if (_locationManager == nil) {
_locationManager = [[CLLocationManager alloc] init];
_locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
[_locationManager requestAlwaysAuthorization];
_locationManager.distanceFilter = 10;
if(IS_OS_9_OR_LATER){
_locationManager.allowsBackgroundLocationUpdates = YES;
}
}
}
return _locationManager;
}

Shared instance should be synchronised. You can use #Synchronized or dispatch_once. It is good practice to synchronised the shared instances even you are not using the multiple threads.
+ (instancetype)sharedLocationManager {
static LocationTracker *sharedInstance_ = NULL;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance_ = [[LocationTracker alloc] init];
[sharedInstance_ initialize];
});
return sharedInstance_;
}
- (void)initialize {
_locationManager = [[CLLocationManager alloc] init];
_locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
[_locationManager requestAlwaysAuthorization];
_locationManager.distanceFilter = 10;
if(IS_OS_9_OR_LATER){
_locationManager.allowsBackgroundLocationUpdates = YES;
}
}
You can implement the location manager delegates inside the LocationTracker class. Use NSNotificationObserver to update all the classes, which are all expecting location update.
Else follow the below line.
CLLocationManager *locationManager = [LocationTracker sharedLocationManager].locationManager;
locationManager.delegate = self;
[locationManager startUpdatingLocation];

Related

Save and load a singleton

I have created a singleton like this:
+ (instancetype)sharedDataStore {
static SSDataStore *_sharedDataStore = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedDataStore = [[self alloc] init];
});
return _sharedDataStore;
}
What is the best way of saving and loading it to and from the documents folder?
I tried saving it with NSKeyedArchiver, but I don't know how to load it afterwards. What do I do instead of
_sharedDataStore = [[self alloc] init];
?
To restore the state of your singleton, you need to implement the initWithCoder method from the NSCoding protocol. Something like:
- (id)initWithCoder:(NSCoder *)decoder {
self = [super init];
if (!self) {
return nil;
}
self.property1 = [decoder decodeObjectForKey:#"property1"];
self.property2 = [decoder decodeObjectForKey:#"property2"];
return self;
}
See NSCoding/NSKeyedArchiver on NSHipster for more details.

Can we show user location in offline in MKMapview

Can we get current user location "That blue ball which is animating" when our device is offline.when i tried to get current user location and log it i'm getting 0.00000 for both longitude and latitude here is the code that i used to get current user location.I'm using ipad mini to test it.I have also added CLLocationManagerDelegate in .h file.
- (void)viewDidLoad {
[self getUserLocation];
self.myMapView.showsUserLocation = YES;
}
-(void)getUserLocation{
if ([CLLocationManager locationServicesEnabled]) {
locationManager = [[[CLLocationManager alloc] init] autorelease];
locationManager.delegate = self;
locationManager.distanceFilter = kCLDistanceFilterNone;
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
[locationManager startUpdatingLocation];
}else{
NSLog(#"User location Disabled");
}
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
CLLocation *currentGPSLocation = newLocation;
if (currentGPSLocation != nil) {
currentLocation.latitude = self.myMapView.userLocation.coordinate.latitude;
currentLocation.longitude = self.myMapView.userLocation.coordinate.longitude;
currentLocationWalk.latitude=self.myMapView.userLocation.coordinate.latitude;
currentLocationWalk.longitude=self.myMapView.userLocation.coordinate.longitude;
NSLog(#"didUpdateToLocation: %f,%f", self.myMapView.userLocation.coordinate.latitude,self.myMapView.userLocation.coordinate.longitude);
statusLabel.textColor = [UIColor blackColor];
statusLabel.text = [NSString stringWithFormat:#"%f",self.myMapView.userLocation.coordinate.latitude];
statusLabel1.text = [NSString stringWithFormat:#"%f",self.myMapView.userLocation.coordinate.longitude];
}
}
You are not using ARC (I strongly suggest using it, it avoid most of the memory management errors!).
Therefore it might be that "locationManager" is a instance variable that is not retained (maybe you did not declare it as a retained property).
In this case, it might have been released already when your code returns to the main run loop, where the autorelease pool is drained.
This line
locationManager = [[[CLLocationManager alloc] init] autorelease];
is not a good idea.
define the locationManager as a property,
init the locationManager in viewDidLoad(), but remove the autorelease!
and relase the locationManager only when the viewController is unloaded (there where you release the other properties)

Can i achieve the real singleton in objective-c like java c#

init method is declared in NSObject class hence, the client code can create a new instance of my singleton class, is there any way to achieve the real singleton such that client cannot create a new instance.
Just do this:
static SingletonClass *singleton;
+ (SingletonClass *)sharedInstance
{
#synchronized(self) { //For thread safety
if (singleton == nil) {
[[self alloc] init];
}
return singleton;
}
}
-(id)init
{
if (singleton) { //This way init will always return the same instance
return singleton;
}
self = [super init];
if (self) {
singleton = self;
}
return singleton;
}
This is a proper way to do a singleton in objective c
+ (id)sharedManager
{
static dispatch_once_t onceQueue;
static SingletonObjectClass *singleton = nil;
dispatch_once(&onceQueue, ^{
singleton = [[self alloc] init];
});
return singleton;
}
- (id)init {
self = [super init];
if (self) {
//.....
}
return self;
}
init method is for initialization of instance variables. on its own will not create the object. Alloc, copy methods needs to be overridden to achieve a real single ton.
Hope this should clarify.
+ (id)alloc {
NSLog(#"%#: use +sharedInstance instead of +alloc", [[self class] name]);
return nil;
}
+ (id)new {
return [self alloc];
}
+ (SingletonClass *)sharedInstance {
static SingletonClass *myInstance = nil;
if (!myInstance)
{
myInstance = [[super alloc] init];
}
return myInstance;
}
You can return a static object of the class each time, making it a singleton.
#implementation Singleton
#synthesize testVar;
+ (Singleton*) sharedObject {
static Singleton * myInstance = nil;
if (myInstance == nil) {
myInstance = [[[self class] alloc] init];
testVar = 5;
// Set default values if needed
return myInstance;
}
To access object and its members:
[[Singleton sharedObject] testVar]

didUpdateToLocation called multiple

i have an iOS application witch uses the current location of the user. I am doing like this :
-(void)startGeoloc{
NSLog(#"start geoloc");
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy=kCLLocationAccuracyHundredMeters;
[locationManager startUpdatingLocation];
}
#pragma mark - CLLocationManagerDelegate methods
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
[locationManager stopUpdatingLocation];
AppDelegate *apDelegate =(AppDelegate *)[UIApplication sharedApplication].delegate;
apDelegate.modeGeoloc = YES;
[self callWebService:locationManager.location];
}
The problem of this, is that my method callWebService:locationManager.location is called twice and i would like to call it just one time. how i can i do this ? thanks for your answers
To ensure locationManager only calls the "didUpdate..." method once, use a BOOL to reference if the location has been found yet.
Create the ivar BOOL:
#property BOOL didFindLocation;
Before locationManager startUpdatingLocation, set the new BOOL to NO. That way you can call for a new location update at will.
-(void) startFindingLocation {
self.didFindLocation = NO; // like this
locationManager = [[CLLocationManager alloc] init];
[locationManager setDelegate:self]
[locationManager setDesiredAccuracy:kCLLocationAccuracyHundredMeters];
[locationManager startUpdatingLocation];
}
In "didUpdateLocations", check for it.
- (void) locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
if (!self.didFindLocation) {
self.didFindLocation = YES;
[locationManager stopUpdatingLocation];
// do the rest of your stuff
}
}
If possible, do not set didFindLocation anywhere else in your code to avoid confusion.
locationManager delegate methods can be called very frequently (they didUpdateToLocation all the time, right? :)
One way would be to have your callWebService have state, know whether it is currently executing a request and ignore concurrent requests if one is still going. Another way would be to keep a timestamp and only allow it through if 2 minutes has passed since the previous one.
Had the same problem.
I think the easiest solution is setting the CLLocationManager to null.
locationManager = nil;
after calling
[locationManager stopUpdatingLocation];
locationManager.startUpdatingLocation() fetch location continuously and didUpdateLocations method calls several times,
Just set the value for locationManager.distanceFilter value before calling locationManager.startUpdatingLocation().
As I set 100 meters(you can change as your requirement) working fine, and will work for you.
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = 100
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()

Finding the cause of EXC_BAD_ACCESS

I have a class with the following init method:
- (id)init
{
self = [super init];
if (self) {
// Initialization code here.
StateStack* s = [[StateStack alloc] init];
state = s;
[s push:NONE]; //<--EXC_BAD_ACCESS on load here
[s release];
}
return self;
}
And StateStack has the following init code:
- (id)init {
self = [super init];
if (self) {
NSMutableArray* s = [[NSMutableArray alloc] init];
stack = s;
[s release];
NSLog(#"%d",[stack retainCount]);
}
return self;
}
Oddly, if I remove the NSLog line, the EXC_BAD_ACCESS moves to StateStack's dealloc method:
- (void)dealloc {
[stack release]; //<--EXC_BAD_ACCESS
[super dealloc];
}
Searching around seems to suggest that EXC_BAD_ACCESS is caused by overreleasing, but I can't see how I've overreleased anything. Does anyone know what the cause might be?
In your init function:
StateStack* s = [[StateStack alloc] init];
state = s;
[s push:NONE]; //<--EXC_BAD_ACCESS on load here
[s release];
you are allocating an instance of StateStack; this gets a retain count of 1. Then at the end of the function you call release, retain count goes to 0 and the object is ready to be released. So, when later dealloc is executed, the state ivar is sent another release and that is causing the bad access. You don't need to release s, since you want that state be retained. The same error pattern occurs in the other init method.
This would be correct:
- (id)init
{
self = [super init];
if (self) {
// Initialization code here.
StateStack* s = [[StateStack alloc] init];
state = s;
[s push:NONE]; //<--EXC_BAD_ACCESS on load here
}
return self;
}
- (id)init {
self = [super init];
if (self) {
NSMutableArray* s = [[NSMutableArray alloc] init];
stack = s;
}
return self;
}
NB: I don't want to generate misunderstandings. Using retain count to check for correct memory allocation is useless. This is true. Anyway, reasoning in terms of retain count helps understanding what happens when you allocate/release/autorelease an object. It is the basic mechanism, but it is too difficult to track it usage to check for correctness of memory management.
state = s is not copying the NSMutableArray object, it's just copying the pointer to it. So when you call [s release] the object referred to by both s and state is deallocated. You'll get an EXC_BAD_ACCESS whenever you use either from that point on.
Also, don't use [object retainCount] to debug memory management problems. It lies. Google NSZombies.
- (id)init{
self = [super init];
if (self) {
// Initialization code here.
state = [[StateStack alloc] init];
[state push:NONE];
}
return self;
}
StateStack
- (id)init {
self = [super init];
if (self) {
stack = [[NSMutableArray alloc] init];
}
return self;
}