EXC_BAD_ACCESS when setting a CLBeacon to nil - ios

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.

Related

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

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?

[UINavigationController retain]: message sent to deallocated instance

My application crashes when simulating Memory warning in simulator with error:
[UINavigationController retain]: message sent to deallocated instance
I'm using ARC.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UIWindow *window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
_window = window;
[self startWithFlash];
return YES;
}
- (void)startWithFlash
{
[self.window.subviews makeObjectsPerformSelector:#selector(removeFromSuperview)];
__weak typeof (self) weakSelf = self;
WPRSplashViewController *splashViewController = [[WPRSplashViewController alloc] initWithNibName:#"WPRSplashView" bundle:nil doneCallback:^{
[weakSelf startApplication];
}];
self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:splashViewController];
[self.window makeKeyAndVisible];
}
- (void)startApplication
{
WPRMainViewController *mainViewController = [[WPRMainViewController alloc] init];
UINavigationController * controller = [[UINavigationController alloc] initWithRootViewController:mainViewController];
self.menuController = [[PHMenuViewController alloc] initWithRootViewController:controller
atIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
self.window.rootViewController = self.menuController;
[self.window makeKeyAndVisible];
}
This happens when somewhere in the app I call:
[((WPRAppDelegate*) [UIApplication sharedApplication].delegate) startWithFlash];
And right after that simulating memory warning.
Running Profile Tools with NSZombie enabled I get the following trace:
This is not the only place with such crash. In every place where I use UINavigationController as wrapper for view controller and I present it as modal view, after simulating memory warning I get this crash.
I had very similar issue in other place for which I've posted here another question but did not find a reasonable solution: Similar issue
Finally after days of investigations I've found a reason to all these crashes including the one described in "iOS memory warning sent to deallocated UIViewController"
The problem came out from PHAirViewController project. I still did not find out a real reason, but simply commenting out - (void)dealloc function in PHAirViewController.m file did the magic.
The main headache I got when was running Instruments to detect NSZombies. All traces were pointing to system classes like UINavigationController, UIImagePickerViewController etc... Due to this I started disabling ARC for parent controllers. In some places it helped but in some it didn't.
After a lot of voodoo I found out that every class (including system classes) was implementing UIViewCOntroller(PHAirViewController) Category and though - (void)dealloc function was always called on dismissing them.
Now the only thing left is to understand why this function is generating NSZombies. The interesting thing is that just commenting its content (which have only one line: self.phSwipeHandler = nil) does not have the same effect.
Quickfix: insert assert([NSThread isMainThread]); to various places in your code where you access appDelegate.window.rootViewController. This should be done for write- and for read-accesses to the property! This will reveal the culprit. appDelegate.window.rootViewController must not be accessed from any other thread than the main thread.
Generally, there are these reasons why this may happen:
You are using __unsafe_unretained variables.
You are using an unsafe_unretained property.
You are using non-ARC
You are accessing the same variable from different threads at the same time
You are accessing the same nonatomic, non-weak property from different threads at the same time
The fix for 1 and 2 is simple: Just don't use unsafe_unretained anymore.
The fix for 3 is: use ARC instead.
The fix for 4 and 5: use atomic properties instead, or synchronize access to your iVars. (Note that you must not access iVars from atomic properties directly as this breaks the atomicity.) Alternatively, use the property only from one thread, e.g. only from the main thread.
In your example, I assume that issue #5 applies. The culprit should be some non-main-thread accessing rootViewController from UIWindow.
It is likely you are using an assign or __unsafe_unretained property somewhere in your code. Delegates should always be of type weak, so that the reference to the delegate object is nil'ed out on deallocation.
Also, calling:
[((WPRAppDelegate*) [UIApplication sharedApplication].delegate) startWithFlash];
... from within another class in your app is a bit of a smell. One that I've had many times. It means you have circular dependencies. Your app delegate is dependent on the class using this code (transitively, if not directly), and this class is dependent on your app delegate. Looking at your Instruments trace, it looks like you have adopted the delegate pattern else where, so you have some experience with decoupling. I would suggest bubbling that message up through a delegate chain, notification, or block.

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.

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];

Why system call UIApplicationDelegate's dealloc method?

I have next code:
// create new delegate
MyCustomApplicationDelegate *redelegate = [[MyCustomApplicationDelegate alloc] init];
redelegate.delegate = [(NSObject<UIApplicationDelegate42> *)[UIApplication sharedApplication].delegate retain];
// replace delegate
[UIApplication sharedApplication].delegate = redelegate;
...
[UIApplication sharedApplication].delegate = redelegate.delegate;
[redelegate.delegate release];
after last line the system called dealloc method of base UIApplicationDelegate class.
So, why? I read Apple documentation about UIApplication, about delegate property:
#property(nonatomic, assign) id delegate
Discussion The delegate must adopt the UIApplicationDelegate formal
protocol. UIApplication assigns and does not retain the delegate.
It clearly says that UIApplication assigns and does not retain the delegate. So, why it destroy my base delegate?
UIApplication has some unusual behavior. The first time that you set the delegate property on UIApplication, the old delegate is released. Every time after that, when you set the delegate property the old delegate is not released.
The declaration for the delegate property in UIApplication.h is:
#property(nonatomic,assign) id<UIApplicationDelegate> delegate;
This implies that the shared UIApplication will never call retain or release on the delegate. This is normal for the delegate pattern: A class normally doesn't retain its delegate because this results in a retain loop.
But in this case, there's an unusual problem: Who owns the app's first delegate? In main.m the call to UIAplicationMain() implicitly allocs and inits the first delegate, which leaves that delegate with a retain count of 1. Someone has to release that but there's no classes around to own it. To solve this, whenever you set a new delegate on UIApplication for the first time, it releases that first delegate. The new delegate was alloced and initted by some class in your app, so you already own a reference to the new delegate. UIApplication doesn't retain or release the new delegate.
I don't think you're supposed to change the UIApplication sharedApplication delegate like this. The standard delegate of your App is automatically the [UIApplication sharedApplication] delegate. So perhaps you should just put all your custom code in the normal delegate?
I have an idea that old delegate after this:
[UIApplication sharedApplication].delegate = redelegate.delegate;
will be released.
But when i see this
#property(nonatomic, assign) id<UIApplicationDelegate> delegate
i'm thinking that it should not (because assign)
Do you agree with me?

Resources