views:

61

answers:

3

The following process leads to a crash of my app:

  • the user opens a view and a request is send to the server
  • the request is executed in background
  • the user navigates back to the root view
  • the request has been finished

and the following code is executed

// MyDatasource.m  
// e.g. in connectionDidFinishLoading  
[callback loadedDataSource:self];

In the meantime the other models/views has been deallocated and the message is sent to a deallocated instance.

callback is of type id and conforms to the KalDataSourceCallbacks protocoll.

How can I avoid that a message is sent to a deallocated object?

PS: My question is similar to this question

Edit:

I'll try to set callback to nil in the dealloc method (in my datasource class). This doesn't solve the problem, because MyDataSource is always present and the dealloc method should only be called if RootViewController is released (what should happen when the app is closed).

What I've done so far:

Now MyDataSource is retained by a property in my RootViewController:

// RootViewController.h
@property (retain) MyDataSource *dataSource;

// RootViewController.m
@synthesize dataSource;
// ...
self.dataSource = [[[MyDataSource alloc] init] autorelease];
kal.dataSource = dataSource;

- (void)dealloc {
    [dataSource release];
    // ...
}

KalViewController does not retain its datasource. You probably will want to store a reference to the dataSource in an instance variable so that you can release it after the calendar has been destroyed.

I also created a callback property:

// MyDataSource.h
@property (retain) id<KalDataSourceCallbacks> callback;

// MyDataSource.m
@synthesize callback;
// ...
- (void)presentingDatesFrom:(NSDate *)fromDate to:(NSDate *)toDate delegate:(id<KalDataSourceCallbacks>)delegate {
    // ...
    self.callback = delegate;
}
- (void)dealloc {
[callback release];
callback = nil;
   // ...
}

Currently the app is not crashing. I have to test on the devices. Adding callback = nil doesn't do anything here, because the dealloc is never called.

How should I release MyDataSource after the calendar has been destroyed? That doesn't work because I don't know if the calendar has been destroyed. So the dataSource can only live over the whole app runtime.

Edit 2:

Can I check callback for nil to find out if it has been released?

if (callback != nil)
     [callback loadedDataSource:self];

This doesn't help.

Edit 3:

The app was not crashing because I retained the delegate:

@property (retain) id<KalDataSourceCallbacks> callback;

should be

@property (assign) id<KalDataSourceCallbacks> callback;

So how could I avoid that the method loadedDataSource is sent to a deallocated object for my special case?

+1  A: 

when you move out of the current view set the delegate to nil.

xCode
The delegate is in `MyDataSource` class.
testing
A: 

You will need to make sure that you finish the connection and release it and set it to nil. A message sent to a nil object is ignored.

Paul Ardeleanu
The connection can be finished, because my datasource class is created in the `RootViewController` and so always available. (I haven't found another solution for integrating Kal calendar.) So the release of the datasource class is done when `RootViewController` is released.
testing
One possibility would be to set the delegate to `nil` in the `dealloc` method of `KalViewController`. This would be possible through sending a message or with the help of the notification center. But then I would have to modify `KalViewController` which isn't the best solution. In detail, if I want to upgrade to a newer version, each modification has to be done again (perhaps by a person not mine). But this would be the last possibility to get this work.
testing
You've also to be aware of retain cycles. Either I take a weak reference or use the observer pattern ...
testing
A: 

In KalDataSource.h I added the following method to the KalDataSource protocoll:

@protocol KalDataSource <NSObject, UITableViewDataSource>
    // ...
    - (void)destroyCallback;
@end

In KalDataSource.m I added the method to get rid of the warnings:

@implementation SimpleKalDataSource
// ...
- (void)destroyCallback
{
    // do nothing
}
@end

In KalViewController.m I'm calling my before created method when the object is deallocated:

- (void)dealloc
{
    // ...
    [dataSource destroyCallback];
}

In MyDataSource.m I'm implementing the function

- (void)destroyCallback {
    self.callback = nil;
}

and set the delegate to nil.

testing