How can I find out when an object is being released? I am listening for kvo changes, but the object get's deallocated before the retain count goes to 0, and I get the following warning:
An instance 0x16562be0 of class MyViewController was deallocated while key value observers were still registered with it. Observation info was leaked, and may even become mistakenly attached to some other object. Set a breakpoint on NSKVODeallocateBreak to stop here in the debugger. Here's the current observation info:
Basically what I'm trying to do is to detect when the model is dismissed. I can't use a Delegate, because the viewControllers being presented are dynamic, and my mainViewController has no knowledge about them other than the fact that they are subclasses of UIViewController.
[anotherViewController addObserver:self forKeyPath:#"retainCount" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionOld | NSKeyValueObservingOptionPrior context:nil];
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
// Here check for the changes and see of the new value is 0 or not
}
I also tried listening for the superView of the viewController being changed to nil
[anotherViewController.view addObserver:self forKeyPath:#"superView" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionOld | NSKeyValueObservingOptionPrior context:nil];
You can only do Key-Value Observing on keys for which the object supports it. What you want to do here is simply not possible — an object's observers are all supposed to be gone by the time it gets to dealloc. You will need to structure your application such that either this object is kept around as long as it is needed or it actively tells interested parties before it goes away.
And looking at an object's retainCount is just never a good idea. As far as it is useful, it is only useful for debugging — and even then there are much better and more reliable tools. The result of retainCount is simply misleading, and it does not work the way most people expect. Watching for it to be 0 is an exercise in futility, because no object can exist with a retain count of 0 — when an object with a retain count of 1 is released, it gets deallocated, and then you are not allowed to message it anymore. (In fact, the framework literally has no way of representing a 0 retain count because it's an unreachable state.)
UPDATE
As of late 2017 (iOS 11, macOS 10.13), when an object is deallocated, it automatically unregisters any remaining observers. From the Foundation release notes for that year:
Relaxed Key-Value Observing Unregistration Requirements
Prior to 10.13, KVO would throw an exception if any observers were
still registered after an autonotifying object's -dealloc finished
running. Additionally, if all observers were removed, but some were
removed from another thread during dealloc, the exception would
incorrectly still be thrown. This requirement has been relaxed in
10.13, subject to two conditions:
The object must be using KVO autonotifying, rather than manually
calling -will and -didChangeValueForKey: (i.e. it should not return NO
from +automaticallyNotifiesObserversForKey:)
The object must not
override the (private) accessors for internal KVO state
If all of these are true, any remaining observers after -dealloc
returns will be cleaned up by KVO; this is also somewhat more
efficient than repeatedly calling -removeObserver methods.
And as of late 2020 (iOS 14, macOS 10.16), KVO is even more careful when an object still has observers during deallocation:
Key-Value Observing
New Features in iOS & iPadOS 14 beta 5
Key-Value Observation removal facilities now employ deterministic
bookkeeping methods. Cases that would have produced hard-to-diagnose
crashes, especially those where KVO signals problems accessing
deallocated observer pointers or observers associated with incorrect
objects, now produce an exception pinpointing which observed object
needs a missed removeObserver(_:) call, and by which observers. This
exception was previously thrown as ‘best effort’ when KVO could detect
the problem; the new deterministic bookkeeping allows it to be thrown
for all cases where removeObserver(_:) is needed.
The improved determinism also allows improved Swift API handling.
Instances of NSKeyValueObservation, produced by the Swift
NSObject.observe(_:changeHandler:) method, take advantage of
integration with this bookkeeping so they now invalidate automatically
when the observed object is released, regardless of how the object
implements its KVO behavior. This applies to all usage of this API in
macOS 11 Big Sur beta, including on processes built with previous
versions of the SDK, and eliminates certain classes of crashes that
sometimes required using the legacy API instead. (65051563)
ORIGINAL
There are a few problems here.
One problem is that you asked the wrong question. You meant to ask “How do I deregister my observer at the right time, before the target is deallocated?” Instead, you mentioned retainCount, which tends to provoke people into berating you about using retainCount instead of helping you do what you're trying to do, which is deregister your observer at the right time.
Another problem is that your view controller doesn't own its model (meaning it doesn't have a strong reference to the model). Usually you want your view controller to own its model, to prevent exactly this sort of problem. While your view controller exists, it needs a model to operate on, so it should own the model. When the view controller is being deallocated, it should stop observing its model and release it. (If you're using ARC, it will release the model automatically at the end of dealloc). You might also choose to deregister in your viewWillDisappear: method, if your view controller goes on and off of the screen repeatedly.
Note that an object can be owned by multiple other objects simultaneously. If you have several view controllers operating on the same model, they should all own the model, meaning that they should all have strong references to the model.
A third problem is that you're (probably) using KVO directly. The built-in KVO API is not very pleasant to use. Take a look at MAKVONotificationCenter. This KVO wrapper automatically unregisters an observer when the observer or the target is deallocated.
if you are interested in getting notified when an object gets deallocated you could send a notification in dealloc, but don't reference the object getting dealloc'ed.
for instance
[[NSNotificationCenter defaultCenter] postNotificationName:#"myclass_dealloced" \
object:[NSValue valueWithPointer:self]];
but you wouldn't ever want to dereference that pointer...
use this only for debugging and testing.
Trying to automatically de-register observers during dealloc is too late.
When dealloc is called, the state of the object graph is undefined. Specifically, order of deallocation is typically not guaranteed and may often change in light of asynchronous processes and/or autorelease.
While the graph the deallocating object strongly references should be coherent, that'll quickly change as the object is deallocated.
The same holds true for the observer of the object being deallocated; as deallocation of an object graph happens, the observed objects state may likely change. As it changes, it may cause observers to fire while the object graph is in the inconsistent, being deallocateed, state.
You really need to concretely separate deallocation from observation logic.
That is, when your controller is dismissed from screen, it should actively dismiss the model layer, including tearing down any observers (or notifying any observers that the model layer is about to go away).
Your observers need to de-register their notifications at the same time they let go of the object.
For example, if your objects are registering notifications on one of their properties, de-register all the notifications before the property is changed or set to nil.
There never should be "hanging" notification registrations to objects that have been simply lost track of. How can you deregister your notifications if you lose track of the object?
just do what KVO says. Observe, act accordingly and signal the Key manually when you need to. That way you can of course know when an object gets deallocated.
When you removeObserver from the Object and it is already deallocated then the method call is acting on nil which does no harm or your observing object holds still a reference and in such case you can still act accordingly.
With ARC this is not a problem and one of the great benefits.
Test it yourself..
// public header.
#interface ObjectToBeObserved : NSObject
#end
// declare in private header
// because you dont want to allow triggering from outside
#interface ObjectToBeObserved : NSObject
// use some artificial property to make it easy signalling manually.
#property (nonatomic) BOOL willDealloc;
#end
#implementation ObjectToBeObserved
-(void)dealloc {
[self willChangeValueForKey:#"willDealloc"];
[self didChangeValueForKey:#"willDealloc"];
}
#end
In your Observer side you just do classic KVO design pattern..
void* objectDeallocatedContext = & objectDeallocatedContext;
#implementation ObservingObject {
// easy to see you could even make a protocol out of the design pattern
// that way you could guarantee your delegate has such property to observe
__weak ObjectToBeObserved *delegate;
}
-(instancetype)initWithObservableDelegate:(ObjectToBeObserved*)observable {
if (!(self=[super init])) return nil;
delegate = observable;
// see i use observe old value here..
if (delegate!=nil)
[delegate addObserver:self forKeyPath:#"willDealloc" options:(NSKeyValueObservingOptionOld) context:objectDeallocatedContext];
return self;
}
-(void)dealloc {
if (delegate!=nil)
[delegate removeObserver:self forKeyPath:#"willDealloc" context: objectDeallocatedContext];
}
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
if (context==objectDeallocatedContext) {
NSLog(#"the observed object deallocated");
// in theory you hold still a weak reference here
// which should be nil after this KVO signal arrived.
// the object in the signal therefore might not be valid anymore,
// which is what you want when observing deallocation.
}
}
#end
KVO is a signal pattern, not a way to know if a signalling object is still valid. But when the object is gone it will not signal anything, those when you can receive the signal you are just fine. Because i choose to watch the NSKeyValueObservingOptionOld value with a void* context, it gets even signaled before the objects artificial "willDealloc" property is set (well, not even set). The KVO can arrive without a valid object but has still a context to compare to. You just need the ping
Related
We have had this weird issue.
When we instantiated an object, we also instantiate a property that belongs to that object:
-(instancetype)init
{
self = [super init];
if (self) {
[self setDocument];
}
return self;
}
-(void)setDocument:
{
_flatGraphicsArrayController = [[NSArrayController alloc] initWithContent:doc.flattenedObjects];
}
...and occasionally a EXC_BAD_ACCESS happens at the setting of _flatGraphicsArrayController
The call stack:
This crash has been determined to be caused by sending that NSKeyValueNotifyObserver message to a deallocated object, an object that appears to be observing changes to flatGraphicsArrayController
To me, this is very confusing because the object that owns this property is just being instantiated, so how could anything possibly be observing changes to the property?
Was somebody registered to observe a specific memory address (if that's how it works), and then the flatGraphicsArrayController somehow took that space in memory, while the observer was deallocated?
Some object (Object1) was added as an observer to another object (Object2). Sometime after that, both Object1 and Object2 were deallocated, but nothing ever removed Object1 as an observer of Object2. The relationship of key-value observers is kept outside of either object (because when KVO was added, no new instance variables could be added to NSObject for binary compatibility reasons, so it has to store its state in a side table).
KVO should complain about this at the time of deallocation of Object1. Check the console log.
Anyway, at some later time, you create your instance of NSArrayController. It happens to occupy the same address as Object2. This means it matches KVO's internal information about the observation relationship between Object1 and Object2. So, effectively, the defunct Object1 is now observing your array controller. When its properties are changed, it sends KVO change notifications to Object1. Of course, Object1 no longer exists. Depending on whether its address has been reused and whether that address is the base address of a new object or points somewhere within it, the result can be a crash or silent.
To fix this, you need to always remove KVO observations before either the observed or observing object is deallocated.
I have a class that retrieves JSON from a URL and returns the data via the protocol/delegate pattern.
MRDelegateClass.h
#import <Foundation/Foundation.h>
#protocol MRDelegateClassProtocol
#optional
- (void)dataRetrieved:(NSDictionary *)json;
- (void)dataFailed:(NSError *)error;
#end
#interface MRDelegateClass : NSObject
#property (strong) id <MRDelegateClassProtocol> delegate;
- (void)getJSONData;
#end
Note that I'm using strong for my delegate property. More about that later...
I am trying to write a 'wrapper' class that implements getJSONData in a block-based format.
MRBlockWrapperClassForDelegate.h
#import <Foundation/Foundation.h>
typedef void(^SuccessBlock)(NSDictionary *json);
typedef void(^ErrorBlock)(NSError *error);
#interface MRBlockWrapperClassForDelegate : NSObject
+ (void)getJSONWithSuccess:(SuccessBlock)success orError:(ErrorBlock)error;
#end
MRBlockWrapperClassForDelegate.m
#import "MRBlockWrapperClassForDelegate.h"
#import "MRDelegateClass.h"
#interface DelegateBlock:NSObject <MRDelegateClassProtocol>
#property (nonatomic, copy) SuccessBlock successBlock;
#property (nonatomic, copy) ErrorBlock errorBlock;
#end
#implementation DelegateBlock
- (id)initWithSuccessBlock:(SuccessBlock)aSuccessBlock andErrorBlock:(ErrorBlock)aErrorBlock {
self = [super init];
if (self) {
_successBlock = aSuccessBlock;
_errorBlock = aErrorBlock;
}
return self;
}
#pragma mark - <MRDelegateClass> protocols
- (void)dataRetrieved:(NSDictionary *)json {
self.successBlock(json);
}
- (void)dataFailed:(NSError *)error {
self.errorBlock(error);
}
#end
// main class
#interface MRBlockWrapperClassForDelegate()
#end
#implementation MRBlockWrapperClassForDelegate
+ (void)getJSONWithSuccess:(SuccessBlock)success orError:(ErrorBlock)error {
MRDelegateClass *delegateClassInstance = [MRDelegateClass new];
DelegateBlock *delegateBlock = [[DelegateBlock alloc] initWithSuccessBlock:success andErrorBlock:error];
delegateClassInstance.delegate = delegateBlock; // set the delegate as the new delegate block
[delegateClassInstance getJSONData];
}
#end
I've come to the objective-c world relatively recently (only lived in ARC times, and still coming to terms with blocks) and admittedly my understanding of memory management is on the slimmer side of things.
This code seems to work fine, but only if I have my delegate as strong. I understand that my delegate should be weak to avoid potential retain-cycles. Looking in instruments, I find that allocations do not continue to grow with continued calls. However, I believe 'best practice' is to have weak delegates.
Questions
Q1) is it ever 'ok' to have strong delegates
Q2) how could I implement the block-based wrapper leaving the delegate of the underlying class as weak delegate (ie. prevent the *delegateBlock from being deallocated before it receives the protocol methods)?
Q1 - Yes. As you point out yourself having delegate properties being weak is a recommendation to help avoid retain cycles. So there is nothing wrong per se with having a strong delegate, but if the clients of your class expect it to be weak you may cause them surprises. The better approach is to keep the delegate weak and for the server side (the class with the delegate property) to keep a strong reference internally for those periods it needs one. As #Scott points out Apple documents doing this for NSURLConnection. Of course that approach doesn't solve your issue - where you want the server to retain the delegate for you...
Q2 - Looked at from the client side the problem is how to keep a delegate alive as long as a server with a weak reference to it requires it. There is a standard solution to this problem called associated objects. In brief the Objective-C runtime essentially allows a key-collection of objects to be associated with another object, along with an association policy which states how long that association should last. To use this mechanism you just need to pick your own unique key, which is of type void * - i.e. an address. The following code outline shows how to use this using NSOpenPanel as an example:
#import <objc/runtime.h> // import associated object functions
static char myUniqueKey; // the address of this variable is going to be unique
NSOpenPanel *panel = [NSOpenPanel openPanel];
MyOpenPanelDelegate *myDelegate = [MyOpenPanelDelegate new];
// associate the delegate with the panel so it lives just as long as the panel itself
objc_setAssociatedObject(panel, &myUniqueKey, myDelegate, OBJC_ASSOCIATION_RETAIN);
// assign as the panel delegate
[panel setDelegate:myDelegate];
The association policy OBJC_ASSOCIATION_RETAIN will retain the passed in object (myDelegate) for as long as the object it is associated with (panel) and then release it.
Adopting this solution avoids making the delegate property itself strong and allows the client to control whether the delegate is retained. If you are also implementing the server you can of course provide a method to do this, maybe associatedDelegate:?, to avoid the client needing to define the key and call objc_setAssociatedObject itself. (Or you can add it to an existing class using a category.)
HTH.
It entirely depends on the architecture of your objects.
When people use weak delegates, it's because the delegate is usually some kind of "parent" object, which retains the thing that has the delegate (let's call the "delegator"). Why does it have to be a parent object? It doesn't have to be; however, in most use cases it turns out to be the most convenient pattern. Since the delegate is a parent object that retains the delegator, the delegator can't retain the delegate or it will have a retain cycle, so it holds a weak reference to the delegate.
However, that is not the only use situation. Take, for example, UIAlertView and UIActionSheet in iOS. The usual way that they are used is: inside a function, create an alert view with a message and add buttons to it, set its delegate, perform any other customization, call -show on it, and then forget it (it is not stored anywhere). It's a kind of "fire and forget" kind of mechanism. Once you show it, you don't need to retain it or anything and it will still be displayed on screen. It's possible in some cases you might want to store the alert view around so you can programmatically dismiss it, but that is rare; in the vast majority of use cases, you simply show and forget it, and just handle any delegate calls.
So in this case, the proper style would be a strong delegate, because 1) the parent object does not retain the alert view, so there is no issue with a retain cycle, and 2) the delegate needs to be kept around, so that when some button is pressed on the alert view, someone will be around to respond to it. Now, a lot of times, #2 isn't a problem because the delegate (parent object) is some kind of view controller or something that is otherwise retained by something else. But this is not always the case. For example, I can simply have a method that is not part of any view controller, which anyone can call to show an alert view, and if the user presses Yes, uploads something to the server. Since it's not part of any controller, it likely is not retained by anything. But it needs to stay around long enough until the alert view is done. So ideally the alert view should have a strong reference to it.
But as I've mentioned before, this is not always what you want for an alert view; sometimes you want to keep it around and dismiss it programmatically. In this case, you want a weak delegate or it will cause a retain cycle. So should an alert view have a strong or weak delegate? Well, the caller should decide! In some situations the caller wants strong; in others the caller wants weak. But how is this possible? The alert view delegate is declared by the alert view class, and must be declared as either strong or weak.
Fortunately, there is a solution that does let the caller decide -- a blocks-based callback. In a blocks-based API, the block essentially becomes the delegate; but the block is not the parent object. Usually the block is created in the calling class and captures self so that it can perform actions on the "parent object". The delegator (alert view in this case) always has a strong reference to the block. However, the block may have a strong or weak reference to the parent object, depending on how the block is written in the calling code (to capture a weak reference to the parent object, don't use self directly in the block, and instead, create a weak version of self outside the block, and let the block use that instead). In this way, the calling code fully controls whether the delegator has a strong or weak reference to it.
You are correct in that delegates are usually weakly referenced. However, there are use cases where a strong reference is preferred, or even necessary. Apple uses this in NSURLConnection:
During a download the connection maintains a strong reference to the delegate. It releases that strong reference when the connection finishes loading, fails, or is canceled.
An NSURLConnection instance can only be used once. After it finishes (either with failure or success), it releases the delegate, and since the delegate is readonly, it can't be (safely) reused.
You can do something similar. In your dataRetrieved and dataFailed methods, set your delegate to nil. You probably don't need to make your delegate readonly if you want to reuse your object, but you will have to assign your delegate again.
As other said it's about architecture. But I'll walk you through it with several examples:
Retry upon failure
Suppose you've made a URLSession, and are waiting for a network call you made through a viewController, sometimes it doesn't matter if it failed, but at other times it does. e.g. you're app is sending a message to another user, then you close that viewcontroller and somehow that network request fails. Do you want it to retry again? If so then that viewController has to remain in memory, so it can resubmit the request again.
Writing to disk
Another case would be when a request succeeds you may want to write something to the disk, so even after the viewcontroller has its UI updated you might still want to sync your local database with the server.
Large background tasks
The original use case for NSURLSession was to power background network task execution, large file downloads and things of that nature. You need something in memory to handle the finalization of those tasks to indicate execution is complete and the OS can sleep the app.
Associating the lifecycle of downloading large files to a certain view is a bad idea…it needs to be tied to some more stable/persistent e.g. the session itself…
Normally if I’m going to use the delegate based system rather than URLSession’s newer block-based API, I have a helper object that encapsulates all the logic necessary to handle failure and success cases that I may require that way, I don’t have to rely on a heavy VC to do the dirty works
This is answer was entirely written thanks to a conversation I had with MattS
When observing a value on an object using addObserver:forKeyPath:options:context:, eventually you'll want to call removeObserver:forKeyPath: on that object to clean up later. Before doing that though, is it possible to check if an object actually is observing that property?
I've tried to ensure in my code that an object is only having an observer removed when it needs to be, but there are some cases where it's possible that the observer may try to remove itself twice. I'm working to prevent this, but just in case, I've just been trying to figure out if there's a way to check first if my code actually is an observer of something.
[...] is it possible to check if an object actually is observing that
property?
No. When dealing with KVO you should always have the following model in mind:
When establishing an observation you are responsible for removing that exact observation. An observation is identified by its context—therefore, the context has to be unique. When receiving notifications (and, in Lion, when removing the observer) you should always test for the context, not the path.
The best practice for handling observed objects is, to remove and establish the observation in the setter of the observed object:
static int fooObservanceContext;
- (void)setFoo:(Foo *)foo
{
[_foo removeObserver:self forKeyPath:#"bar" context:&fooObservanceContext];
_foo = foo; // or whatever ownership handling is needed.
[foo addObserver:self forKeyPath:#"bar" options:0 context:&fooObservanceContext];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if (context == &fooObservanceContext) {
// handle change
} else {
// not my observer callback
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
- (void)dealloc
{
self.foo = nil; // removes observer
}
When using KVO you have to make sure that both objects, observer and observee, are alive as long as the observation is in place.
When adding an observation you have to balance this with exactly one removal of the same observation. Don't assume, you're the only one using KVO. Framework classes might use KVO for their own purposes, so always check for the context in the callback.
One final issue I'd like to point out: The observed property has to be KVO compliant. You can't just observe anything.
Part of the NSKeyValueObserving protocol is this:
- (void *)observationInfo
which should list the observers.
EDIT
Useful for debugging only.
I underestand this an objective-c question. But since lots of people use Swift/objective-c together, I thought I point out the advantage of the Swift4 new API over older versions of KVO:
If you do addObserver multiple times for KVO, then for each change you’ll get the observeValue as many as the current number of times you’ve added yourself as the observer.
And to remove yourself you have to call removeObserver as many times as you added.
Removing it more than you’ve added will result in a crash
The Swift4 observe is far smarter and swiftier!
If you do it multiple times, it doesn’t care. It won’t give multiple callbacks for each change.
And only one invalidate of the token is enough.
invalidating it before beginning to observer or more times that that you’ve done observe will not result in a crash
So to specifically answer your question, if you use the new Swift4 KVO, you don't need to care about it. Just call invalidate and you're good. But if you're using the older API then refer to Nikolai's answer
In a NSManagedObject Sub Class I have the code …
- (void) awakeFromInsert {
[self addObserver:[NSApp delegate] forKeyPath:#"name" options:NSKeyValueObservingOptionNew context:nil];
}
Which adds my App Delegate as an Observer, what I want to do now is from inside my App Delegate, I want to remove itself as an Observer for my NSManagedObject Sub Class.
How would I do this?
Thanks.
I was thinking of adding this to my App Delegate
[JGManagedObject removeObserver:self forKeyPath:#"name"];
but unfortunately removeObserver:forKeyPath: is not a Class Method.
For something like this, it's probably best to rethink the design. The delegate, in this case, would have to have some specific knowledge of the managed object itself in order to do this -- and the delegate would have to have some idea about when in the lifecycle it should (or would want to) stop observing the object.
You have a few choices. Instead of doing this in awake from insert, you could have the delegate start observing it when it creates it and then stop observing it when it gives up ownership. If that is not feasible in your design, you could have the object remove its observer when it is deallocated. If this is a fire-and-forget (basically the delegate only cares once), you could remove the observer after the first change notification. Since, however, you created the observation within the creation lifecycle of this object, it is probably best to remove that observation at the destruction of the object:
- (void)dealloc
{
[self removeObserver:[NSApp delegate] forKeyPath:#"name"];
// other clean-up
[super dealloc];
}
You might also want to do this when the object awakes from fetch and from fault and release the observer when the object will become a fault.
Much the same way you added the observer in the first place, only with fewer options:
// Given some managed object "object"...
[object removeObserver:self forKeyPath:#"name"];
Note that we remove self as the observer, rather than the application delegate as given by [NSApp delegate], since the code will be running within the delegate itself.
How about sending your object the removeObserver:forKeyPath message just before you delete it from the ManagedObjectContext?
From iOS 11 and above, we have automatic registration of KVO. From the Foundation Release Notes for macOS 10.13 and iOS 11
Relaxed Key-Value Observing Unregistration Requirements
Prior to 10.13, KVO would throw an exception if any observers were still registered after an autonotifying object's -dealloc finished running.
Additionally, if all observers were removed, but some were removed
from another thread during dealloc, the exception would incorrectly
still be thrown. This requirement has been relaxed in 10.13, subject
to two conditions:
• The object must be using KVO autonotifying, rather than manually
calling -will and -didChangeValueForKey: (i.e. it should not return NO
from +automaticallyNotifiesObserversForKey:) • The object must not
override the (private) accessors for internal KVO state
If all of these are true, any remaining observers after -dealloc
returns will be cleaned up by KVO; this is also somewhat more
efficient than repeatedly calling -removeObserver methods.
I'm working on an iPhone app that performs some communication with the server to be initialized the first time it's run. I have a class ServerCommunication with methods for each of the basic tasks to be performed (authenticate user, get activation key, download updates, etc.), and a new instance of ServerCommunication is created for each task; I'm not reusing them, although it would be more efficient.
When the user completes the first initialization screen, ServerCommunication gets created four times. I keep track of it with NSLog(#"Initializing ServerCommunication instance %p", self); in its -init method. The second initialization screen also calls ServerCommunication a few times when the user taps the "Next" button, but on its last instantiation, the app hangs with the message -[ServerCommunication insertDataIntoLocalDB:]: message sent to deallocated instance 0xec75b0 in the console. The thing is, 0xec75b0 is the address of the first instance of ServerCommunication I created way back at the first screen.
Why would it be sending messages to that instance? I'm not retaining them anywhere; they're mostly autoreleased. If it helps, all of the methods in that class perform asynchronous downloading of XML data with NSURLConnection and then parse it with NSXMLParser. The parser's delegate method -(void)parserDidEndDocument:(NSXMLParser *)parser then sends off NSNotifications that are received by methods in my view controllers so they know whether to proceed to the next screen or stay there and display an error message.
Help is much appreciated!
The first thing I would do is turn on NSZombies, which should let you break at the point where your zombie is being messaged.
A common cause of problems like this is when you have objects with weak references to each other that are not allocated and deallocated at the same time. So (hypothetically), some other object stores a pointer to your ServerCommunication object as a delegate or owner. When ServerCommunication is deallocated, it doesn't unregister, and then some time down the road the object holding the weak reference tries to message you.
If I had to completely guess (and I do!) I bet you add your ServerCommunication objects as an NSNotification observer, but never remove them. Try making sure that you do this:
[[NSNotificationCenter defaultCenter] removeObserver:self];
sometime before deallocation. (It's also possible that there's a more circuitous path involving NSNotification here -- such as a pointer to the ServerCommunication object being passed as data to the view controller, which is then trying to message it.)