Why does assignment to -CLLocationManager.delegate sometimes copy its value? - ios

I have a unit test that makes sure that the object assigned to a CLLocationManager is a specific object:
CLLocationManager *loc = weakColl.lm;
XCTAssertTrue(
loc.delegate == (id)weakColl,
#"The collection should be the location manager delegate"
);
However, this test is failing on some devices (CDMA iPhone 6/iOS 8). I put some NSLog() calls in the code that does the assignment to try to figure out what's happening:
id lm = [CLLocationManager new];
[lm setDelegate:(id)self];
NSLog(#"SELF: %p", self);
NSLog(#"Delegate: %p", [lm delegate]);
The address is the same on most platforms, but on the failing devices, it's not:
SELF: 0x15647af0
Delegate: 0x155589a0
What the hell? I had a look at the header file; it's supposed to be an assign property:
#property(assign, nonatomic) id<CLLocationManagerDelegate> delegate;
So I don't understand. Why on earth would the references be different in this case?

Related

EXC_BAD_ACCESS when setting a CLBeacon to nil

The following will perform a crash when setting CLBeacon to nil.
CLBeacon *beacon = [[CLBeacon alloc] init];
beacon = nil; // crash
Is it not possible to deallocate an initialized CLBeacon?
This can be reproduced by simply adding the code above to a fresh project inside the App Delegate's didFinishLaunchingWithOptions
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
CLBeacon *beacon = [[CLBeacon alloc] init];
beacon = nil; // crash
return YES;
}
The apple documentation for CLBeacon states:
You do not create instances of this class directly. The location manager object reports encountered beacons to its associated delegate object.
The reason it crashes is an implementation detail that doesn't really matter, but it is due to the fact that CLBeacons are not properly initialized when you just call init. When it deallocates, CLBeacon dereferences it's _internal ivar and crashes if it is NULL.
You can see this by looking at the value of the CLBeacon->_internal ivar in the debugger. If you create the beacon using init then the ivar is NULL, but if you create it with [[CLBeacon alloc] initWithCoder:nil] it will have a value and it doesn't crash when you set the beacon to nil.
Ran into this problem while using a mocked subclass. My tests would crash every time a mocked subclass was dealloced by ARC.
Solution is to call the correct init method on CLBeacon.
Looking here we see that there is an addition init method. Declare it in a category in your code.
#interface CLBeacon (PRXInternal)
- (id)initWithProximityUUID:(id)arg1 major:(id)arg2 minor:(id)arg3 proximity:(long long)arg4 accuracy:(double)arg5 rssi:(long long)arg6 ;
#end
Call this initializer if you need an instance of the class. Do not include in production code.

How to get information printed in Xcode's console onto screen?

I started using Xcode a couple days ago and I'm completely lost. I'm trying to get a GPS locator app running found here.
Basically, the app prints any updated GPS information using NSLog, which as far as my understanding goes, prints to Xcode's console. However, I'd like to get this info printed onto the screen.
Here's the code from CFAStartViewController.m that successfully prints to the screen:
-(void)viewDidAppear:(BOOL)animated{
CFAAppDelegate *appDelegate=(CFAAppDelegate *)[UIApplication sharedApplication].delegate;
CLLocation *currentLocation=appDelegate.locationManager.location;
self.labelLocationInformation.text=[NSString stringWithFormat:#"latitude: %+.6f\nlongitude: %+.6f\naccuracy: %f",
currentLocation.coordinate.latitude,
currentLocation.coordinate.longitude,
currentLocation.horizontalAccuracy];
}
And here's the code in CFAAppDelegate.m that successfully prints to the console:
-(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation{
NSDate* eventDate = newLocation.timestamp;
NSTimeInterval howRecent = [eventDate timeIntervalSinceNow];
if (abs(howRecent) < 15.0)
{
//Location timestamp is within the last 15.0 seconds, let's use it!
if(newLocation.horizontalAccuracy<35.0){
//Location seems pretty accurate, let's use it!
NSLog(#"latitude %+.6f, longitude %+.6f\n",
newLocation.coordinate.latitude,
newLocation.coordinate.longitude);
NSLog(#"Horizontal Accuracy:%f", newLocation.horizontalAccuracy);
//Optional: turn off location services once we've gotten a good location
//[manager stopUpdatingLocation];
}
}
}
I tried changing the calls to NSLog to self.labelLocationInformation.text, with a similar format to that of CFAStartViewController, but it doesn't seem to do anything. I've read through the basic tutorials of Xcode, but I feel there's some knowledge lacking (obviously) on what to do overall.
If the best you could do is post a link that helps me solve this problem, that would be great.
I think the problem is due to scope - the didUpdateToLocation: method is sent to the location manager delegate, ie your app delegate, which doesn't have the label in scope. Try changing your delegate for the location manager to be your CFAStartViewController, and move the associated location manager delegate code from App Delegate to CFAStartViewController. Then the label will be in scope when didUpdateToLocation: is called.
Based on your clarifying response to my comment.... If you want the data from CFAAppDelegate to be presented in your CFAStartViewController then you need to send that data to CFAStartViewController (or CFAStartViewController needs to get it). To send it 1) provide a storyboard id for CFAStartViewController such as "CFAVC". 2) define or expose properties in CFAStartViewController to contain your data. 3) instantiate CFAStartViewController via the following from within CFAAppDelegate:
// in CFAStartViewController.h define property such as:
#property (strong, nonatomic) CLLocation *incomingLocation;
// in CFAAppDelegate.m
// get a pointer to your VC
CFAStartViewController *destinationVC = [self.storyboard instantiateViewControllerWithIdentifier:#"CFAVC"];
// set properties on VC
destinationVC.incomingLocation = newLocation;
// show CFAStartViewController (or use whatever method you are currently using)
[self.navigationController pushViewController:destinationVC animated:YES];

iOS kontakt.io beacons search

I have two kontakt.io Beacons. I'm able to find it using the default Kontakt.io app available in the AppStore. But when I use the SDK and try to find it in my custom app, the app requests Bluetooth, which means it does something with it, but no beacons are found.
According to the documentation I must only create an object of KTKBeaconManager class, assign a KTKBeaconManagerDelegate and call startFindingDevices method. After that the delegate should receive callbacks whenever devices in range changes. I extended the KTKBeaconManager with a class called BeaconManager. Here's its code (Yes I have imported everything and code compiles. I didn't put it here to save space.).
BeaconManager.h
#interface BeaconManager : KTKBeaconManager <KTKBluetoothManagerDelegate>
#end
BeaconManager.m
#implementation BeaconManager
- (instancetype)init
{
self = [super init];
if (self) {
//Setting the delegate to self
self.delegate = self;
}
return self;
}
- (void)bluetoothManager:(KTKBluetoothManager *)bluetoothManager didChangeDevices:(NSSet *)devices {
NSLog(#"Entered didChangeDevices. Devices size: %d", devices.count);
}
#end
Starting the search.
BeaconManager *beaconManager = [BeaconManager new];
[beaconManager startFindingDevices];
[beaconManager reloadDevices]; //Tells the manager to forget all devices and start searching again.
This is actually a sample code from the documentation, but it's not working. Anybody's going through something similar and has got a clue what to do?
Your beaconManager is most probably deallocated just after it's created. You have to move it to an instance variable.
It's not written that directly but you should know what's the scope of life for object - it should be a property if you want to have it working all the time etc.

Multiple sensors in iOS

I have two different POC, one for the accelerometer and one for GPS. However, I am not comprehending the architecture to put marry both of the applications. I need to initialize both the accel and GPS when the application loads. I have the main view tied to the accel, but also need to the the location of the device.
My current architecture is
Projects in workspace
Main App
Utility App
Services App
Domain App
The main app ViewController inherits
: UIViewController
That all wires up correctly the the accel works as expected.
In the Utility CoreLocationUtility class, I have it inheriting the CLLocationManagerDelegate.
The question is, how do I register a delegate from the same view that is of type AccelDelegate?
If you want to make your ViewController act as delegate for both accelerometer and GPS, state in its header file that it obeys both delegate protocols:
#interface ViewController : UIViewController <CLLocationManagerDelegate, UIAccelerometerDelegate> {
CLLocationManager *myLocationManager; // an instance variable
}
// ... your definitions
#end
then somewhere in your ViewController
[UIAccelerometer sharedAccelerometer].delegate = self;
myLocationManager = [[CLLocationManager alloc] init];
myLocationManager.delegate = self;
then somewhere else in your ViewController, put the delegate methods for both protocols
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
// ... your code
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
// ... your code
}
and that should work, although there may be typos, I have not compiled this. Plus, in my experience, location manager code tends to get quite big. You may be much better off putting it in a class of its own, to be instantiated by the ViewController.
Can anyone explain why the only method in the UIAccelerometerDelegate protocol is deprecated?

IOS allocated objects is not referenced later in this execution path retain count +1

In my appDelegate.h file I do this:
CLLocationManager *locationManager;
and
#property (nonatomic, retain) CLLocationManager *locationManager;
Then later in the .m file:
...
#synthesize locationManager;
...
if ([CLLocationManager locationServicesEnabled])
{
[myGizmoClass setLocationManagerDisabled:FALSE];
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
[self.locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
[self.locationManager setDistanceFilter:kCLDistanceFilterNone];
[self.locationManager startUpdatingLocation];
...
Yet I am getting the following in XCode 4.5 (see image attached)
(Object leaked: allocated object is not referenced later in this code execution path)
What the heck? I reference it right after that line.
I am not seeing the issue. PS: No crash, or anything. Let me be clear. This is working as it is. I just hate the error. I am QUITE sure that I am missing something silly. Can anyone help?
Please do not post anything with regards to "You don't have to do #property anymore", etc. This code was written back for xcode 3.5-4~ish and I prefer being specific because I hate having to flip back and forth between the shorthand that XCode 4.5 allows and what older projects require (and still have in their source code). So I still use the full definitions in the .h file. I figured that the major change to programming style would come with the next major update of the app. (thanks for understanding)
Problem
If this is non ARC (which I assume it is), then think about how this is working:
self.locationManager = [[CLLocationManager alloc] init];
^ ^
Retains on setting |
Retains when allocating
Why is this retaining when I'm setting the property?
#property (nonatomic, retain) CLLocationManager *locationManager;
^
This is why
When you synthesize a property, you are generating a getter and setter (in most circumstances). The nonatomic and retain keywords are providing hints to the synthesize; nonatomic wraps the setting and getting within #synchronized(self) to make sure only one thread is acting on it at a time, and retain is telling the setter to retain whatever value you're putting in to it. It's important to note (for older versions of Xcode anyway, not 4.5), that if you don't synthesize, then these won't take effect
In your case, you are retaining something twice. Hence if there's no release anywhere, then the memory will be leaked. It's simple to fix, simply use:
self.locationManager = [[[CLLocationManager alloc] init] autorelease];
Why does it act like this?
If it didn't, then autoreleased objects returned from methods wouldn't be retained correctly!
Alternative Solution
If you don't like adding autorelease, simply assign to the underlying instance variable instead.
locationManager = [[CLLocationManager alloc] init];
In all cases...
Make sure you release what you have at the most appropriate time, these will not automatically release. For retained properties, self.locationManager = nil will suffice. For the alternative solution, you will need to perform [locationManager release];
The #property is defined to retain. Therefore the following line:
self.locationManager = ...
Which is semantically equivalent to:
[self setLocationManager:...]
retains whatever is on the right hand side. But what you've supplied on the right hand side is an owning reference. So:
[[CLLocationManager alloc] init] // gives an owning reference
self.locationManager = ... // retains your owning reference; you've now
// incremented the reference count twice
Your location manager will be leaked.
Check with this code:
CLLocationManager *location = [[CLLocationManager alloc] init];
self.locationManager = location;
[location release];
or you need to do like:
self.locationManager = [[[CLLocationManager alloc] init] autorelease];
[[CLLocationManager alloc] init] makes retainCount to 1.
self.locationManager makes the retainCount to increment by 1.
In the #property, I see that you've indicated that you want locationManager to retain. So assigning something to self.locationManager will bump the retain count to one. However, since you also call alloc, that bumps the retain count to two now (which will cause a leak).
Solution: Remove self from the alloc statement:
locationManager = [[CLLocationManager alloc] init];

Resources