我遇到了一个非常非常奇怪的错误,可能与内存管理有关(即使我使用的是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/

10-15 18:05