How to avoid deallocated objects being accessed in callbacks/etc? - ios

The issue has been discussed here and here, but I wonder if there is a more solid way to solve this whether you have delegates or not - when a function is called after a delay.
At a certain point in a program, at a button push, an object - a CCLayer - is created. That layer creates several objects, some of them at callbacks. That created object layer has a "back" button which destroys it. I am running into a problem when the callbacks, etc are triggered AFTER that object is destructed and try to access objects that don't exist anymore - where the "message sent to deallocated instance 0x258ba480" gives me this good news. How do I avoid that?
1) Is there a way to kill the callbacks (because I obviously don't need them anymore)
2) should/can I test for the existence of these possibly non-existent objects at the callbacks themselves
3) something else?
(My callback is code for checking for an internet connection that I copied from this illustrious website - may it live long and prosper-, using Reachability, and I could solve the problem by simply moving it to the main view and setting a flag on the child view, but I don't want to.)
- (void)testInternetConnection
{
internetReachableFoo = [Reachability reachabilityWithHostname:#"www.google.com"];
// Internet is reachable
internetReachableFoo.reachableBlock = ^(Reachability*reach)
{
// Update the UI on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"Yayyy, we have the interwebs!");
//I do the net stuff here
});
};
// Internet is not reachable
internetReachableFoo.unreachableBlock = ^(Reachability*reach)
{
// Update the UI on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"Someone broke the internet :(");
noNetMessageLabel.visible=true; //<-------this goes kaboom
noNetFlag=true;
});
};
[internetReachableFoo startNotifier];
}

There are basically two ways to avoid deallocated delegates from being messaged:
Hold onto the objects you want to message later. That way they won’t get deallocated. This is the case with block callbacks – if a block references some object, the object gets retained until the block ceases to exist. If you message some objects from a block and hit a deallocated object, you must have screwed up the memory management somewhere.
Clear the delegation link before you release the delegate. Nowadays this is usually done using weak, zeroing properties that are automatically set to nil when the referenced object is deallocated. Very convenient. Not your case.

You might consider several options:
First, you may just check for existence of an object before passing message to it:
if (noNetMessageLabel)
noNetMessageLabel.visible = true;
But personally I consider that as a bad architecture.
More wise decision, from my point of view, would be move the code of displaying any alert regarding internet connectivity to the model.
Create method like this in AppDelegate or in the model:
- (NSError*)presentConnectivityAlert
{
if () //any error condition checking appropriate
[[NSNotificationCenter defaultCenter]
postNotificationName:#"connectivityAlert"
object:self
userInfo:userInfo];
}
Also you may consider moving internet checking code to the model too.
In the ViewControllers of your app implement listening to this notification.
- (void)viewDidLoad {
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(didReceiveRemoteNotification:)
name:#"connectivityAlert"
object:nil];
}
- (void)viewDidUnload {
[[NSNotificationCenter defaultCenter]
removeObserver:self
name:#"connectivityAlert"
object:nil];
}
-(void)didReceiveRemoteNotification:(NSDictionary *)userInfo {
if (self.isViewLoaded && self.view.window) {
//Present the user with alert
}
}
Thus you have more general and quite versatile approach to handle connectivity issues throughout all your application.
Is there a way to kill the callbacks
It's not possible to cancel block (in your case), but it's possible to cancel NSOperation in NSOperationQueue. But that will require to rewrite your implementation of Reachability.

Related

Are message parameters in block retained when given to dispatch_async?

consider the following method snippet.
- (void) closeSocket {
...
dispatch_async(dispatch_get_main_queue(), ^{
// last message before actual disconnection:
[self.connectionListenerDelegate connectionDisconnected:self];
}
self.connectionListenerDelegate = nil;
...
}
This method of my "socket" implementation class can be called by external object, in some arbitrary thread (main, or other). I wish to only notify my delegate once, on the main thread, and remove the delegate so that other background events and possible incoming data won't reach it.
In other words, I want to make sure connectionDisconnected: is the last call from the socket to the delegate.
I know code blocks capture their environment's variables etc. But will the block capture and retain the self.connectionListenerDelegate when created?
If closeSocket is being called on some background thread, and dispatches the connectionDisconnected: asynchronously on the main thread, and I nullify my weak reference to my delegate right away - maybe the block will have a nil object and won't send its message?
What is the right way to go about this?
I guess I could use the old
[self.connectionListenerDelegate performSelectorOnMainThread:#selector(connectionDisconnected:) withObject:self waitUntilDone:NO];
which retains both the receiver and parameter object (self), but I prefer GCD dispatch_async and I'd like to better understand blocks.
If closeSocket is being called on some background thread, and dispatches the connectionDisconnected: asynchronously on the main thread, and I nullify my weak reference to my delegate right away - maybe the block will have a nil object and won't send its message?
I think that after self.connectionListenerDelegate = nil; runs, all methods in the dispatch method will get the nil reference when accessing the connectionListenerDelegate.
So, the best way to go about this would be to transfer the delegate reference to a temporary object for use inside the block:
id<ConnectionListenerDelegate> *tempDelegateRef = self.connectionListenerDelegate;
dispatch_async(dispatch_get_main_queue(), ^{
// last message before actual disconnection:
[tempDelegateRef connectionDisconnected:self];
}
self.connectionListenerDelegate = nil;
I'm not sure if you need strong/weak references or something like that.

Problems using notifications as a callback?

I am trying to update a view when something happens in another class, and after some looking, it appeared that the most common way to do this was to use either delegates or blocks to create a callback. However, I was able to accomplish this task using notifications. What I want to know is: Is there a problem using notifications to trigger methods calls? Are there any risks I'm not aware of? Is there a reason I'd want to use blocks/delegates over notifications?
I'm new to Objective-C, so I'm not sure if the approach I'm taking is correct.
As an example, I'm trying to set the battery level of a BLE device on the ViewController. I have a BluetoothLEManager, which discovers the peripheral, its services/characteristics, etc. But to do this, I need to initiate the "connection" in the detailViewController, then update the battery level once I find it.
Here is some example code of what I'm doing:
DetailViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
NSLog(#"Selected tag UUID: %#", [selectedTag.tagUUID UUIDString]);
tagName.text = selectedTag.mtagName;
if(selectedTag.batteryLevel != nil){
batteryLife.text = selectedTag.batteryLevel;
}
uuidLabel.text = [selectedTag.tagUUID UUIDString];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(setBatteryLevel:) name:#"SetBatteryLevel" object:nil];
}
...
-(void)setBatteryLevel:(NSNotification*)notif{
NSMutableString* batLevel = [[NSMutableString alloc]initWithString:[NSString stringWithFormat:#"%#", selectedTag.batteryLevel]];
[batLevel appendString:#" %"];
selectedTag.batteryLevel = batLevel;
batteryLife.text = selectedTag.batteryLevel;
}
BluetoothLEManager.m:
...
-(void) getBatteryLevel:(CBCharacteristic *)characteristic error:(NSError *)error fetchTag:(FetchTag *)fetchTag
{
NSLog(#"Getting battery Level...");
NSData* data = characteristic.value;
const uint8_t* reportData = [data bytes];
uint16_t batteryLevel = reportData[0];
selectedTag.batteryLevel = [NSString stringWithFormat:#"%i", batteryLevel];
NSLog(#"Battery Level is %#", [NSString stringWithFormat:#"%i", batteryLevel]);
[[NSNotificationCenter defaultCenter] postNotificationName:#"SetBatteryLevel" object:nil];
}
...
Let me know if you need any other code, but this is the basics of it all.
Each approach has different strengths and weaknesses.
Delegates and protocols require a defined interface between the object and it's delegate, a one-to-one relationship, and that the object have specific knowledge of the delegate object it's going to call.
Methods with completion blocks involve a similar one-to-one relationship between an object and the object that invokes the method. However since blocks inherit the scope in which they're defined, you have more flexibility as to the context that's available in the completion block. Blocks also allow the caller to define the completion code in same place that the call takes place, making you code more self-documenting.
In both cases, the object that is notifying the delegate or invoking the completion block has to know who it's talking to, or what code is being executed.
A delegate call is like an auto shop calling you back to let you know your car is done. The service manager has to have your phone number and know that you want a call.
A block is more like a recipe you give to a chef. Give the chef a different recipe and he/she performs a different task for you.
Notifications are much less tightly coupled. It's like a town crier, yelling announcements in a crowded public square. The crier doesn't need to know who's listening, or how many people are listening.
Likewise, when you send a notification, you don't know who, if anybody, is listening, or how many listeners there are. You don't need to know. If 10 objects care about the message you are broadcasting, they can all listen for it, and they'll all be notified. The message sender doesn't have to know or care who's listening.
Sometimes you want tighter coupling, and sometimes you want looser coupling. It depends on the problem you're trying to solve.

Memory Management in a Block Using Notifications

According to the Xcode instruments, my code has a memory leak (at #3). But I get the feeling I'm missing something in my mental model of what's going on, so I have a few questions about the following logic:
__block MyType *blockObject = object; //1
dispatch_async(dispatch_get_main_queue(), ^{
if ([self.selectedObjects containsObject:blockObject]) { //2
[self.selectedObjects removeObject:blockObject];
[[NSNotificationCenter defaultCenter] postNotificationName:ObjectDeselectionNotification object:blockObject]; //3
} else {
[self.selectedObjects addObject:blockCart];
[[NSNotificationCenter defaultCenter] postNotificationName:ObjectSelectionNotification object:blockCart];
}
});
1) I'm using a __block reference because I'm executing this code async and don't want a reference to this variable copied to the heap. Is this a valid usage of __block even though I'm not mutating the variable?
2) Calling self.selectedObjects will create a retain on self. Does the block automatically release this after it has exited?
3) I apparently have a leak at this point, but I'm not exactly sure why. Is NotificationCenter retaining my __block object that is supposed to be disposed of after my block exits?
From the code you've shown, I don't see any problems...
1) Your object would not be "copied" onto the heap - it is already on the heap being an alloc'd object. Rather, it's reference count would be incremented by 1 as it is now owned by the block. You do not need the __block reference as you are not assigning anything to the pointer. In fact, you do not need blockObject at all and can just pass object.
2.) self should be released once the block is done. However, post a notification is synchronous (this block will not finish until all the objects responding to the notification are done).
3.) I'm not sure what the exact implementation that NSNotificationCenter uses, but it doesn't really matter because the posting is synchronous. It will call every observer of your notification and the selectors they want to receive your notification on
It seems as though you are running all this code within another block - can you paste the full method?
Please remove this answer if incorrect (you've already accepted) but I'm not sure you accepted because the answer worked for you.
I don't think you should be referencing self in that block - you will be creating a retain cycle.
__weak YourClass *weakSelf = self;
use weakSelf instead and cut the tie between the creator and the block floating on the dispatch queue?

Best approach to doing something once an Internet connection becomes available

I've seen plenty of examples here on how to check an internet connection is available but nobody seems to explain the best practice way of doing something once/ if it becomes available.
I'm using Tony Million's Reachability class and have a 'connection available' block that sets a boolean (Online) to true once the connection is available. The Reachability class is initialized in my app delegate didFinishLaunchingWithOptions but by the time my code checks the status of Online Reachability still hasn't finished figuring out if there's a connection, and so my app always sees itself as being offline when it first starts.
Now, I could put my connection-requiring code into the 'connection available' block but there's more than one place my app needs the internet so that's obviously not flexible enough for my needs.
The 'best' idea I had so far is to populate an array with methods that need the internet to do their thing and then have Reachability execute whatever is in that array once it knows there's a connection... but am I over-complicating matters here? Is there a better way of going about this?
This is roughly based on your "best idea". Tony Million's Reachability also posts notifications via NSNotificationCenter when the internet connection changes. All your classes that need to do something when an internet connection becomes available, should register for this notification.
On the GitHub page there is an example for that: https://github.com/tonymillion/Reachability#another-simple-example
You would initialize the Reachability class in your app delegate, like you do right now. And then your other classes register for the kReachabilityChangedNotification notification with the NSNotificationCenter in their initializer. They also have to deregister from the NSNotificationCenter in their dealloc method.
Here is some code you can use as starting point:
- (void)registerForReachabilityNotification
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(reachabilityChanged:)
name:kReachabilityChangedNotification
object:nil];
}
- (void)deregisterFromNotifications
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)reachabilityChanged:(NSNotification *)notification
{
Reachability *reachability = notification.object;
switch ([reachability currentReachabilityStatus]) {
case NotReachable:
// No connection
break;
case ReachableViaWiFi:
case ReachableViaWWAN:
// Connection available
break;
}
}

Reachability Classes crashing program - not sure why

I have an 'internet aware' base class for objects which require networking in my app. All the objects which need to be internet aware inherit from it. As you can imagine I allocate and deallocate a lot of these objects.
The internet aware base class has the following code to interact with the Reachability classes used to check for internet status.
#import "Reachability.h"
- (id) init {
...
self.internetReachable = [Reachability reachabilityForInternetConnection];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(checkNetworkStatus) name:kReachabilityChangedNotification object:nil];
[self.internetReachable startNotifier];
...
}
- (void) dealloc
{
[self.internetReachable stopNotifier];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
As soon as there is a change in internet status in my app, the app crashes with the following error:
*** -[Reachability isKindOfClass:]: message sent to deallocated
instance 0x1e249a30
Ive turned on zombies and tracked the problem down to the following line of code within Reachability.m
NSCAssert([(NSObject*) info isKindOfClass: [Reachability class]], #"info was wrong class in ReachabilityCallback");
Unfortunately, beyond stopping listening for NSNotifcations and stopping the notifier, im not sure what else my objects can do to avoid this error.
Any help or suggestions would be great.
Thanks
Vb
Edit:
OK so following the advice of the answer below I ran it in instruments with allocations
and this was the retain count history.
It is as I suspected, its an object that I have deallocated being called by Foundation (ie. NSNotifcationCenter) and not myself.
My internet objects have a strong pointer to a Reachability object. When they are deallocated, so is the Reachability object. The zombie is the Reachability object.
In the dealloc of my internet object I have called removeObserver, but foundation is still calling the deallocated object. I can't understand why...
The reason that Foundation was still sending the deallocated Reachability the NSNotifcations was because the Reachability object was being deallocated on a different thread to the one it was being created on ie. Reachability isn't thread safe. Using dispatch_async back to the same queue that the Reachability object was being created on has solved the problem.
This happens when the object you made that instantiates Reachability, and hence holds a reference to a Reachability instance, is deallocate without (or before) you call stopNotifier on it!
Solving this is very simple. You must call stopNotifier before your object gets removed from the stack tearing down your Reachability instance with it. You can do this in the dealloc method, or if it's a viewController, you could call it in one of the lifecycle methods such as viewDidDisappear, etc.
There should be no need of messing with threads here. Consider, when you call startNotifier on Reachability this thing starts on a background thread by design of Reachability. So, when you call stopNotifier it takes care of threading for you.
The reason you're having to mess with threads has to do with the fact that your object holding the reference to Reachability got deallocated but was still a registered listener for network changes, which happened with startNotifier. When the network does change, guess what, your object, although still registered to receive notifications, is nowhere to be found! Crashy crash. stopNotifier unregisters it before it dies and everything is good.
- (void)dealloc
{ // self.hostReachability is my property holding my Reachability instance
if (self.hostReachability) {
[self.hostReachability stopNotifier];
}
}
The NSCAssert line is where you first access the deallocated object, but if you want to know more about the object's life cycle you should use Instruments. Use Xcode's Profile tool to run your program in the simulator with the Allocations (but not leaks!) tool. In the allocations tool's launch configuration turn on Enable NSZombie Detection and Record Reference Counts. When you hit the NSCAssert line Instruments should detect the attempt to message the zombie info object and make a note of it. If you look at the detailed information for the zombie info object Instruments will show you its history of reference counts and you should be able to see when it was deallocated.

Resources