I am having a very, very strange error, probably related to memory management (even though I’m using ARC).
I have a my AppDelegate, Foo, and SubFoo (which is a subclass of Foo).
Foo.h
@protocol FooDelegate <NSObject>
- (void)didReceiveDownloadRequest:(NSURLRequest *)downloadRequest;
@end
@interface Foo : NSObject {
__weak id <FooDelegate> delegate;
}
- (void)performRequest;
@property (nonatomic, weak) id <FooDelegate> delegate;
@property (nonatomic, retain) NSString *fileIdentifier;
Foo.m
@implementation Foo
@synthesize delegate, fileIdentifier;
- (id)init {
if ((self = [super init])) {
self.delegate = nil; // I tried leaving this line out, same result.
NSLog(@"I am %p.", self);
}
return self;
}
- (void)performRequest {
// Bah.
}
@end
SubFoo.h
@interface SubFoo : Foo {
WebView *aWebView;
}
SubFoo.m
- (void)performRequest {
if (self.fileIdentifier) {
aWebView = [[WebView alloc] init];
[aWebView setFrameLoadDelegate:self];
[[aWebView mainFrame] loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"theURL"]];
}
}
- (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
NSLog(@"Finished loading.");
// ...
NSLog(@"Class Name: %@", NSStringFromClass([self class]));
NSLog(@"Memory Location of delegate: %p", self.delegate);
// ...
}
Sometimes, the class name on webView:didFinishLoadForFrame: returns a completely different class (instead of SubFoo, it returns random classes, like NSSet, NSArray, it even sometimes returns CFXPreferencesSearchListSource), other times it just crashes there with an EXC_BAD_ACCESS, and when it returns a random class on Class Name: it returns that [randomClassName delegate] is an unrecognized selector.
EDIT: When self gets set to another thing, it gets set RIGHT on webView:didFinishLoadForFrame:, and on performRequest it is ALWAYS SubFoo.
Any help here would be appreciated.
First, even though you are using ARC zeroing weak references in your project (
@property (weak)), other projects and frameworks may not be (and are probably not) using zeroing weak references.In other words, assume that all delegates in frameworks are
__unsafe_unretainedunless:weakin a headerThat said, let’s talk about your example. Your object ownership chart looks something like this:
(Note: I’m not entirely sure which class in your project uses SubFoo. Based on common practice, I’m assuming that you have a class with a strong reference to SubFoo, and that class is also set up to be a SubFooDelegate)
Ultimately, your instance of SubFoo is losing its last strong reference and is deallocating. In a perfect ARC-enabled world, the WebView’s pointer to SubFoo would nil out at this time. However, it’s not a perfect world yet, and WebView’s frameLoadDelegate is
__unsafe_unretained. Due to run loop interaction, the WebView is outliving SubFoo. The web request completes, and a dead pointer is dereferenced.To fix this, you need to call
[aWebView setFrameLoadDelegate:nil];in SubFoo’s dealloc method. You also need to call it when you reassign aWebView, as you are losing track of the old aWebView:SubFoo.m