消息发送到解除分配的实例...在@synthesize 期间发送?

Posted

技术标签:

【中文标题】消息发送到解除分配的实例...在@synthesize 期间发送?【英文标题】:Message Sent To Deallocated Instance... Sent During @synthesize? 【发布时间】:2012-03-15 22:10:07 【问题描述】:

我一直在使用来自 Raphael Cruzeiro 的 PDF Annotator 的代码,并发现了一些内存泄漏(ARC 已关闭,将保持关闭以支持旧设备)。在修补了大部分之后,我只剩下最后一对了,他们让我难过。因此,在一个名为PDFDocument 的类中,他具有CGPDFPageRefCGPDFDocument 和自定义注释类@synthesize 的属性。我不得不在他的 dealloc 方法中添加 release 并消除一些悬空指针,除了一个小问题外,这很好用:在大约 3 个完整的保留释放周期后,它在他的注释对象的 @synthesize 行崩溃......我已经由于在@synthesize 期间发送了一个释放的对象,所以从未见过 SIGABRT,所以自然不知道如何修复它。如果我删除了 dealloc 中的发布代码,它会泄漏,但如果我把它留在里面,它就会崩溃。这是 PDFDocument 类的代码:

//.h

#import <Foundation/Foundation.h>

@class Annotation;

@interface PDFDocument : NSObject 
    Annotation *_annotation;


- (id)initWithDocument:(NSString *)documentPath;

- (NSInteger) pageCount;
- (void) loadPage:(NSInteger)number;
- (BOOL)save;

@property (nonatomic, retain) NSString *name;
@property (nonatomic, retain) NSString *hash;
@property (readwrite, nonatomic, assign) CGPDFDocumentRef document;
@property (readwrite, nonatomic, assign) CGPDFPageRef page;

@property (nonatomic, retain) NSString *version;

@property (nonatomic, assign) BOOL dirty;

@property (nonatomic, retain) Annotation *annotation;

@end

//.m
#import "PDFDocument.h"
#import "Annotation.h"
#import "HashExtensions.h"
#import "DocumentDeserializer.h"
#import "DocumentSerializer.h"


@implementation PDFDocument

@synthesize document;
@synthesize page;
@synthesize annotation = _annotation; //after 3rd cycle, it crashes here.
@synthesize name;
@synthesize hash;
@synthesize dirty;
@synthesize version;

- (id)initWithDocument:(NSString *)documentPath

    if((self = [super init]) != NULL) 

        self.name = [documentPath lastPathComponent];
        if ([self.name isEqualToString:@"Musette.pdf"] || [self.name isEqualToString:@"Minore.pdf"] || [self.name isEqualToString:@"Cantata.pdf"] || [self.name isEqualToString:@"Finalé.pdf"]) 
        
        CFURLRef ref = CFBundleCopyResourceURL(CFBundleGetMainBundle(), (CFStringRef)self.name, NULL, NULL);
        self.document = CGPDFDocumentCreateWithURL(ref);
        self.page = CGPDFDocumentGetPage(document, 1);
        self.version = @"1.0";
        DocumentDeserializer *deserializer = [[[DocumentDeserializer alloc] init] autorelease];
        self.annotation = [deserializer readAnnotation:[[(NSURL*)ref absoluteString] stringByDeletingPathExtension]];

        CFRelease(ref);
        

        else   

            CFURLRef pdfURL = (CFURLRef)[[NSURL alloc] initFileURLWithPath:documentPath];
            self.document = CGPDFDocumentCreateWithURL(pdfURL);
            self.page = CGPDFDocumentGetPage(document, 1);
            self.version = @"1.0";
            DocumentDeserializer *deserializer = [[[DocumentDeserializer alloc] init] autorelease];
            self.annotation = [deserializer readAnnotation:[[(NSURL*)pdfURL absoluteString] stringByDeletingPathExtension]];

            CFRelease(pdfURL);
            CGPDFPageRelease(self.page);

        
    

    return self;


- (NSInteger)pageCount

    return CGPDFDocumentGetNumberOfPages(self.document);


- (void)loadPage:(NSInteger)number

    self.page = CGPDFDocumentGetPage(document, number);


- (BOOL)save

    DocumentSerializer *serializer = [[[DocumentSerializer alloc] init] autorelease];
    [serializer serialize:self];

    self.dirty = NO;
    return !self.dirty;


- (void)dealloc

    CGPDFDocumentRelease(self.document);
    if (self.annotation != nil && _annotation != nil) 
        [_annotation release];
        self.annotation = nil;
     //my attempt to prevent the object from being over-released
    self.document = nil;
    self.name = nil;
    [super dealloc];


@end

然后我通过 Instruments 运行它来查找僵尸对象,果然,Instruments 发现了一个释放的对象,它在完全相同的@synthesize 行发送了一条消息!

有谁知道发生了什么以及如何解决它?

【问题讨论】:

只有初代iPhone不兼容ARC,你为什么不用呢? 只是我自己的喜好......再加上 ARC 重构工具现在让我很恼火的事实。唉...我现在就转换。 你说它在“合成期间”崩溃了。实际上,它在 - (Annotation*)annotation- (void)setAnnotation:(Annotation*) 合成方法中崩溃了。可能是 ivar 已经发布的 setter。 【参考方案1】:

这点看起来很不对:

if (self.annotation != nil && _annotation != nil) 
    [_annotation release];
    self.annotation = nil;

首先,您为什么要检查 self.annotation_annotation 是否为空。这实际上是两次执行相同的检查。

其次,您使用直接 ivar 访问来释放 _annotation,然后 annotation 的设置器将再次释放 _annotation 并设置 _annotation = nil。实际上它正在这样做:

if (self.annotation != nil && _annotation != nil) 
    [_annotation release];
    [_annotation release];
    _annotation = [nil retain];

如您所见,将过度发布_annotation

另外,说真的,只需使用 ARC。 ARC(主要)是编译时间,与它运行的设备或操作系统版本无关。在 ios 5 之前的版本中唯一不支持的部分是自动 nil-ed 弱指针。但这真的不应该成为问题,因为无论如何这在 Lion / iOS 5 中都是全新的。

【讨论】:

这是我试图弄清楚 dealloc 是否正在清除对象。我添加了 byte wise &'s 因为只检查属性不起作用......无论哪种方式,我都希望我可以将它转换为 ARC 并完成它。 字节明智 &?这是合乎逻辑的,你已经到了那里。它正在检查从- (Annotation*)annotation 返回的值是否为非零且_annotation 为非零。 - (Annotation*)annotation 将只是一个 return _annotation。你明白了。 ARC 很棒,但无论如何理解下面发生的事情也很好。它也是“按位”的,正如 mattjgalloway 所说,你没有使用它。 很高兴了解发生了什么是的,但没有任何人可以让我不使用 ARC。就像了解内存的工作原理一样,您无需实现自己的malloc

以上是关于消息发送到解除分配的实例...在@synthesize 期间发送?的主要内容,如果未能解决你的问题,请参考以下文章

XCode App 随机崩溃 - 消息发送到解除分配的实例

NSCoding 帮助非常奇怪的错误访问解除分配的实例

CLLocationManger 向分配的实例发送消息

TableViewCell 自动释放错误

如何解除分配 CCLayer

类的实例被解除分配,而键值观察者仍向其注册