CLLocation getDistanceFrom: vs distanceFromLocation:
In Backwards Compatibility if Apple Starts Polishing, Oliver Drobnik explains how he solved the getDistanceFrom: vs distanceFromLocation: problem. His solution involves a new method (distanceBetweenLocation:andLocation:) and eight lines of code for calling getDistanceFrom: through a NSInvocation.
Wouldn't it be nice if instead you could use [aLocation distanceFromLocation:anotherLocation] everywhere in your code, while still retaining backward compatibility? Enter the Objective-C runtime! In your main.m file, first #import <objc/runtime.h> and at the very beginning of your main function, add this:
Method getDistanceFrom = class_getInstanceMethod([CLLocation class], @selector(getDistanceFrom:));
class_addMethod([CLLocation class], @selector(distanceFromLocation:), method_getImplementation(getDistanceFrom), method_getTypeEncoding(getDistanceFrom));
Here you go, -[CLLocation distanceFromLocation:] available at runtime in any iOS version with only two lines of code. Note that on iOS 3, class_addMethod will add the distanceFromLocation: method to the CLLocation class using getDistanceFrom: implementation. On iOS 4, class_addMethod will do nothing as the method already exists.
12 comments:
thumbs up for a simple and effective solution
I'm getting the following errors:
error: initializer element is not constant
error: expected declaration specifiers or '...' before '[' token
error: expected declaration specifiers or '...' before 'selector'
error: expected declaration specifiers or '...' before 'method_getImplementation'
error: expected declaration specifiers or '...' before 'method_getTypeEncoding'
warning: data definition has no type or storage class
warning: type defaults to 'int' in declaration of 'class_addMethod'
error: conflicting types for 'class_addMethod'
Any thoughts ?
If your main file is main.c, rename it to main.m and make sure you #import both <objc/runtime.h> and <CoreLocation/CLLocation.h>
Cédric,
Sorry, I did't read your instructions correctly initially. I've made sure my main file is main.m and I have included both #import statements, but still getting the errors listed.
Stephen
At the very beginning of your main function means just after int main (int argc, const char * argv[]) {
Ah yes, that solved it, thanks very much.
Excellent stuff, very nice indeed!
Shouldn't this be placed inside the pool?
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
How likely/unlikely will this cause problems in iOS 5.0 or 6.0 etc?
No, there is no need to place it inside the pool. These two lines will never trigger an autorelease message to be sent.
This will not cause any problem on any future iOS that already has the distanceFromLocation: method.
Hello Cédric,
I'm only getting around to testing this now. I've implemented your code as below:
main.m
#import
#import
#import
int main(int argc, char *argv[])
{
Method getDistanceFrom = class_getInstanceMethod([CLLocation class], @selector(getDistanceFrom:));
class_addMethod([CLLocation class], @selector(distanceFromLocation:), method_getImplementation(getDistanceFrom), method_getTypeEncoding(getDistanceFrom));
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, nil);
[pool release];
return retVal;
}
and in my ViewController I have the following line:
CLLocationDistance meters = [newLocation distanceFromLocation: oldLocation];
but this gives me an error 'incompatible types in initialization'. Any thoughts, am I missing something ?
Regards,
Stephen
Post a Comment