0

Related: Can I use a class method as a delegate callback?

I'm trying to use a static class, MyClass, as a delegate for its own static CLLocationManager member, but the CLLocationManager delegate methods I've implemented aren't being called. I've set the delegate as [myClass class], properly implemented the delegate methods, and included the protocol in MyClass.h.

MyClass.h

@interface iOSSonic : NSObject <CLLocationManagerDelegate>

MyClass.m

locationManager declaration:

@implementation myClasss : NSObject
...
static CLLocationManager *locationManager = nil;

I'm lazily instantiating the static CLLocationManager via the follow method:

+(CLLocationManager*)getLocationManager {
    if (locationManager == nil) {
        locationManager = [[CLLocationManager alloc] init];
        locationManager.delegate = [myClass class]; // we set the delegate of locationManager to self.
        locationManager.desiredAccuracy = kCLLocationAccuracyBest; // setting the accuracy
        locationManager.distanceFilter = 0.5; // get updates for location changes > 0.5 m
        [locationManager requestWhenInUseAuthorization];
    }
    return locationManager;
}

...and then from my ViewController calling the following MyClass method:

+(void)myFunction {
    [self.getLocationManager startUpdatingLocation];
}

Delegate method implementations:

...
+(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
...
}

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

}

ViewController.m

// no initialization needed for static myClass

- (IBAction)onButtonClick:(id)sender {
    [myClass myFunc] // This should trigger the didUpdateLocations delegate method, but it doesn't 

To ensure that this wasn't some problem related to having the delegate be a static (non-instantiable) class and the delegate callbacks be class methods, I also tried with locationManager as a @property rather than a static member, and created an instance of myClass, setting myClass's locationManager's delegate to self. I also replaced getLocationManager with an overridden locationManager getter, and changed the delegate callbacks to instance methods.

MyClass.m

Initialization:

-(id)init {
    if (self = [super init]) {
        // do nothing
    }
    return self;
}

LocationManager declaration and instantiation:

...
@interface MyClass()
@property (strong, nonatomic) CLLocationManager *locationManager;
@end
@implementation
...

// Lazily instantiate locationManager
-(CLLocationManager*)locationManager {
    if (!_locationManager) {
        _locationManager = [[CLLocationManager alloc] init];
        _locationManager.delegate = self; // we set the delegate of locationManager to self.
        _locationManager.desiredAccuracy = kCLLocationAccuracyBest; // setting the accuracy
        _locationManager.distanceFilter = 0.5; // get updates for location changes > 0.5 m
        [_locationManager requestWhenInUseAuthorization];
    }
    return _locationManager;
}

Delegate method implementations:

...
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
...
}

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

}

ViewContoller.h

...
@property (strong, nonatomic) myClass *myClassInstance;
...

ViewController.m

    - (void)viewDidLoad
    {
        [super viewDidLoad];
        ...
        self.myClassInstance = [[myClass alloc] init];

What am I doing wrong?

Community
  • 1
  • 1
acannon828
  • 528
  • 7
  • 22

2 Answers2

0

It was a stupid location services permissions thing. Turns out it had nothing to do with static members, classes vs. instances, etc. This fixed it for me: https://stackoverflow.com/a/25765345/1402368

Of course it was something stupid like this...

Community
  • 1
  • 1
acannon828
  • 528
  • 7
  • 22
-1

Here's my understanding of what's going on:

Instance method calls and class method calls are semantically different in Objective-C, and not interchangeable.

The method declarations:

+(void)someMethod;

and

-(void)someMethod;

Define 2 different kinds of methods. To call them, you have to know if you're calling an instance method or a class method, and code accordingly.

The location manager is written to call INSTANCE methods on it's delegate, not class methods.

Thus, you can't do what you're trying to do (make a CLASS a delegate instead of an instance of a class.)

You might be able to design your own custom class who's objects expect to have a class set as their delegate, but then you would only ever be able to assign a class as the delegate, never an instance of that class.

Duncan C
  • 128,072
  • 22
  • 173
  • 272
  • Although in the docs the delegate methods are declared as instance methods, at least according to [this SO post](http://stackoverflow.com/questions/8883521/can-i-use-a-class-method-as-a-delegate-callback) implementing delegate methods as class methods is totally fine. – acannon828 Dec 03 '14 at 23:37
  • A separate issue is whether or not the CLLocationManager instance will accept as its delegate a class and not an instance. As I explained however, I tested this by re-implementing MyClass as an instantiable class rather than a static class, and replaced the old static MyClass with an instance of the new MyClass as the locationManager's delegate but still had the same problem (the delegate methods--now instance methods--still weren't getting called). – acannon828 Dec 03 '14 at 23:37
  • No, this doesn't make sense. The message dispatch doesn't make this distinction. If you have an `id`, you can send class or instance messages and the appropriate method will run. – jscs Dec 04 '14 at 01:31
  • Josh, you say "the appropriate method will run." Yes, if you send a class message to an ID, it will try to invoke a class method, and if you send an instance message, it will try to run an instance method. That's my point exactly. The location manager expressly wants to send instance messages to an instance method of the delegate. If you only have a class method, there is no instance method so your object does not respond to the message and it gets dropped on the floor. – Duncan C Dec 04 '14 at 02:26
  • No, because you're not sending "instance messages". There's no such thing. You're just sending _a_ message, and the object -- class object or instance -- figures out what to do with it. It would raise an exception if the object didn't respond. See the gist I made. – jscs Dec 04 '14 at 02:43