我遇到了一个非常非常奇怪的错误,可能与内存管理有关(即使我使用的是ARC)。
我有一个AppDelegate,Foo和SubFoo(这是Foo的子类)。
oo
@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;
富
@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
子库
@interface SubFoo : Foo {
WebView *aWebView;
}
子库
- (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);
// ...
}
有时,webView:didFinishLoadForFrame:上的类名称返回完全不同的类(而不是SubFoo,它返回随机类,例如NSSet,NSArray,它甚至有时返回CFXPreferencesSearchListSource),有时它只是用EXC_BAD_ACCESS崩溃,并且当它崩溃时返回有关“类名”的随机类:它返回[randomClassName委托]是无法识别的选择器。
编辑:当self被设置为另一件事时,它在webView:didFinishLoadForFrame:上被设置为正确,而在performRequest上它始终是SubFoo。
在这里的任何帮助,将不胜感激。
最佳答案
首先,即使您在项目(@property (weak)
)中使用ARC将弱引用归零,其他项目和框架也可能没有(也可能没有)对弱引用归零。
换句话说,假定框架中的所有委托都是__unsafe_unretained
,除非:
委托属性在标头中声明为weak
文档/标题明确指出否则
就是说,让我们谈谈您的例子。您的对象所有权图表如下所示:
(注意:我不能完全确定项目中的哪个类使用SubFoo。根据惯例,我假设您有一个强烈引用SubFoo的类,并且该类也被设置为SubFooDelegate。)
最终,您的SubFoo实例将丢失其最后一个强引用,并且正在取消分配。在一个支持ARC的完美世界中,此时WebView指向SubFoo的指针将消失。但是,这还不是一个完美的世界,WebView的frameLoadDelegate是__unsafe_unretained
。由于运行循环的相互作用,WebView已超越SubFoo。 Web请求完成,并且停用了指针。
要解决此问题,您需要在SubFoo的dealloc方法中调用[aWebView setFrameLoadDelegate:nil];
。重新分配aWebView时,还需要调用它,因为您将无法使用旧的aWebView:
子库
@implementation SubFoo
- (void)dealloc {
[aWebView setFrameLoadDelegate:nil];
// Also nil out any other unsafe-unretained references
}
- (void)performRequest {
if (self.fileIdentifier) {
[aWebView setFrameLoadDelegate:nil]; // Protects us if performRequest is called twice. Is a no-op if aWebView is nil
aWebView = [[WebView alloc] init];
[aWebView setFrameLoadDelegate:self];
[[aWebView mainFrame] loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"theURL"]];
}
}
- (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
// ...
}
关于objective-c - 启用ARC时出现奇怪的内存问题,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/9741697/