Tech

UPDATED: Singletons in Objective C – An Example of CLLocationManager with ARC support

In software engineering, the singleton pattern is a design pattern used to implement the mathematical concept of a singleton, by restricting the instantiation of a class to one object.

In Cocoa, singleton objects act as global variables which can be allocated only once and can not be deleted. When it comes to global variables, as developers, we are often told that it is bad design and should be avoid. However, they are essential (see this article by Matt Gallagher for more detail) and helpful especially when exactly one object is needed to coordinate actions across the system.

An Example of CLLocationManager

Using CLLocationManager as a singleton object will be a perfect example if you are designing a location-based application and want to access the user location across the whole system. First we need to create a class called LocationController and set it to be the singleton.
In LocationController.h

//
//  LocationController.h
//
//  Created by Jinru on 12/19/09.
//  Copyright 2009 Arizona State University. All rights reserved.
//

#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>

// protocol for sending location updates to another view controller
@protocol LocationControllerDelegate
@required
- (void)locationUpdate:(CLLocation*)location;
@end

@interface LocationController : NSObject  {

	CLLocationManager* locationManager;
	CLLocation* location;
	__weak id delegate;
}

@property (nonatomic, strong) CLLocationManager* locationManager;
@property (nonatomic, strong) CLLocation* location;
@property (nonatomic, weak) id  delegate;

+ (LocationController*)sharedInstance; // Singleton method

@end

Then in LocationController.m

//
//  LocationController.m
//
//  Created by Jinru on 12/19/09.
//  Copyright 2009 Arizona State University. All rights reserved.
//

#import "LocationController.h"

static LocationController* sharedCLDelegate = nil;

@implementation LocationController
@synthesize locationManager, location, delegate;

- (id)init
{
 	self = [super init];
	if (self != nil) {
		self.locationManager = [[CLLocationManager alloc] init];
		self.locationManager.delegate = self;
		self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
	}
	return self;
}

#pragma mark -
#pragma mark CLLocationManagerDelegate Methods
- (void)locationManager:(CLLocationManager*)manager
	didUpdateToLocation:(CLLocation*)newLocation
		   fromLocation:(CLLocation*)oldLocation
{
	/* ... */
}

- (void)locationManager:(CLLocationManager*)manager
	   didFailWithError:(NSError*)error
{
       /* ... */

}

#pragma mark -
#pragma mark Singleton Object Methods
// THE FOLLOWING CODE IS NO LONGER SUPPORTED IN ARC
+ (LocationController*)sharedInstance {
    @synchronized(self) {
        if (sharedCLDelegate == nil) {
            [[self alloc] init];
        }
    }
    return sharedCLDelegate;
}

+ (id)allocWithZone:(NSZone *)zone {
    @synchronized(self) {
        if (sharedCLDelegate == nil) {
            sharedCLDelegate = [super allocWithZone:zone];
            return sharedCLDelegate;  // assignment and return on first allocation
        }
    }
    return nil; // on subsequent allocation attempts return nil
}

- (id)copyWithZone:(NSZone *)zone
{
    return self;
}

- (id)retain {
    return self;
}

- (unsigned)retainCount {
    return UINT_MAX;  // denotes an object that cannot be released
}

- (void)release {
    //do nothing
}

- (id)autorelease {
    return self;
}
@end

Now your LocationController class is ready to be used as a singleton in your code. To access its instance just use:

[LocationController sharedInstance];

And you are done.

UPDATE

With the new ARC support in iOS 4 and MacOS 10.6, the implementation of a singleton class needs to be updated. Simply because in ARC, you cannot call or override methods such as retain, release, autorelease and retaincount. Here is how you should implement the LocationController as a singleton class now.

#pragma mark - Singleton implementation in ARC
+ (LocationController *)sharedLocationController
{
static LocationController *sharedLocationControllerInstance = nil;
static dispatch_once_t predicate;
dispatch_once(&predicate, ^{
sharedLocationControllerInstance = [[self alloc] init];
});
return sharedLocationControllerInstance;
}

To access your singleton class is the same:

LocationController* locationController = [LocationController sharedLocationController];
About these ads
Standard

25 thoughts on “UPDATED: Singletons in Objective C – An Example of CLLocationManager with ARC support

  1. Kevin says:

    Hello,

    This is exactly what I was thinking I needed to do for a project I’m working on. However, I’m new to iOS development and am not quite clear on how to use the protocol in order to send location updates to another view controller. I know all interested view controllers will need to adopt the LocationControllerDelegate protocol, but how do you actually announce that a new location has changed from the LocationController?

    Thanks!

    Kevin

    • Hi Kevin,

      This is how you will do it. In LocationController.m, one of the delegation method is called when a new location is updated.
      - (void)locationManager:(CLLocationManager*)manager didUpdateToLocation:(CLLocation*)newLocation fromLocation:(CLLocation*)oldLocation
      {
      /*…some filer method to check if the new location is good …*/
      if (good)
      {
      [self.delegate locationUpdate:newLocation];
      }
      }

      Now in your other view controller that adopts the protocol, you first set it up to be the delegate of LocationController, something like [LocationController sharedInstance].delegate = self; in viewdidload will do.

      Then you implement the protocol method locationUpdate:(CLLocation*)location to utilize the updated location the LocationController has received. eg: NSString* latitudeString = [[NSString alloc] initWithFormat:@”%f”, location.coordinate.latitude];

      Hope it helps.

      • Hi kevin, this singleton is very useful. Do you mean adding this to the viewcontroller?

        - (void)locationUpdate:(CLLocation *)location{
        [[LocationController sharedInstance] setLocation:location];
        }

        Also i am working with ARC and the line:

        [LocationController sharedInstance].delegate = self;

        gives me a warning (passing const___strong to parameter of incompatible type id)

      • locationUpdate:(CLLocation*)location method will be called automatically when a new location is available. You implement this method in your viewcontroller that wants to do something with the new location (not to set the location for the singleton as in your code, singleton already has the new location).

        try [LocationController sharedInstance].delegate = (id)self to get rid of the warning.

  2. Peter says:

    Hello.

    I tried this
    (LocationController*)sharedInstance {
    @synchronized(self) {
    if (sharedCLDelegate == nil) {
    [[self alloc] init];
    }
    }
    return sharedCLDelegate;
    }

    but during code analyze i receive potential memory leak…

    • Thanks for letting me know Peter. Try [[[self alloc] init] autorelease] and see if it gets rid of the warning. Usually, a singleton stays around forever(as long as your application is running), so it is supposed to remain in the memory.

  3. Hi this looks like what I was looking for. However I am new to iphone development and think I am missing something probably very basic.

    I have created the LocationController h and m file.

    What do I then need to do to get the lat long in a different view to both the header file and m file. You say use [LocationController sharedInstance].delegate = self; in viewdidload but what else do I need to do. How would I then set the lat long as a variable to use.

    Sorry if this is very basic but I have been looking into this for days.

    • Hi weirdo1978,

      Since the view controller adopts the LocationControllerDelegate protocol. In its .m file, you need to implement the delegate method locationUpdate:(CLLocation*)location to utilize the updated location the LocationController has received. eg: NSString* latitudeString = [[NSString alloc] initWithFormat:@”%f”, location.coordinate.latitude];

      Let me know if you still have questions.

  4. weirdo1978 says:

    Many thanks for the reply

    I have created a basic view based app and then created the PhoneLocation.h and m file as above.

    If i then add

    - (void)viewDidLoad
    {
    [super viewDidLoad];
    NSString* latitudeString = [[NSString alloc] initWithFormat:@”%f”, location.coordinate.latitude];
    }

    to my viewcontroller.m file it gives an error of unexpected @

    Am I missing something.

    Sorry about this

    • Sorry. Should be @”%f” instead of @”%f”. @”” is an indication of NSString. However, if you want to receive the location updates(lat/long), you still need to implement the locationUpdate:(CLLocation*)location method in your view controller because of the Delegation pattern. Delegation is a key design pattern in Cocoa. I have a post that talks about that.

      Also, read “learn objective-c on the mac” if you want to learn the basics of Objective-C.

      Hope this helps.

  5. capikaw says:

    This is pretty awesome stuff jinru – couple questions.

    the “[[self alloc] init];” line (in ARC) gives the warning: Expression result unused.

    Also, I’ve implemented this in two classes and it seems the first class to call:

    [LocationController sharedInstance].delegate = self;

    is the only one to receive updates. Any ideas?

    • Hi capikaw,

      The singleton code above is written before ARC. In ARC, this will cause warnings/errors simply because you cannot override methods such as retain, release, releasecount, autorelease anymore. I need to update this post, sorry. But basically here is how you will implement the singleton class:
      + (SomeClass *)sharedSomeClass
      {
      static SomeClass *sharedSomeClassInstance = nil;
      static dispatch_once_t predicate;
      dispatch_once(&predicate, ^{
      sharedSomeClassInstance = [[self alloc] init];
      });
      return sharedSomeClassInstance;
      }

      and that is it. This piece of code is supported in ARC and is thread safe:-)

  6. lino says:

    Would you mind posting the full code for the ARC updated version? I think I replaced the code appropriately, but I’m getting a few warnings and errors (mostly around the delegate ivar).

  7. Hello Jinru, first of all thanks for this post! What I am curios about is the case how to solve the initial test if `kCLAuthorizationStatusAuthorized` is authorized? When someone is initially starting an application he/she has to allow current location service. This case seems not to be checked in your example or other way round. The init Method has to check kCLAuthorizationStatusAuthorized first before creating a CLLocationManager instance, right? How do I have to solve this initial case? Any idea?

    Best, dominik

  8. Jeremiah Sandahl says:

    I’ve successfully implemented this approach in my mobile app and it works great. I have also eliminated all of the ARC warnings except one.

            self.locationManager.delegate = self;

    This line gives the warning “Assigning to ‘id’ from incompatible type ‘LocationController *__strong’ ”

    I’m not sure how to change this to make it work with ARC for iOS6. Any suggestions?

  9. Tiago Schmitt says:

    Hello Jinru,

    When I try to call the LocationController* locationController = [LocationController sharedLocationController];

    I receive the message: No know class method for selector ‘sharedLocationController’

    I`m using the ARC. Can you say why this is happen?

    Regards,
    Tiago Schmitt

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s