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

Cod*_*aFi 2 memory-management core-graphics objective-c dealloc ios

我一直在使用来自Raphael Cruzeiro的PDF Annotator的代码,并且发现了许多内存泄漏(ARC已关闭,并将保留以支持旧设备).在修补了大部分内容之后,我对最后一对来说,他们让我难过.因此,在一个叫做类PDFDocument,他的属性了CGPDFPageRef,CGPDFDocument和一个自定义的注释类@synthesize"d.我不得不用释放来消除他的dealloc方法并消除一些悬空指针,除了一个小问题之外它很有效:在大约3个完整的保留 - 释放周期之后,它在@synthesize行崩溃了他的注释对象......我已经从未见过SIGABRT,因为在@synthesize期间发送了一个解除分配的对象,所以自然不知道如何修复它.如果我删除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
Run Code Online (Sandbox Code Playgroud)

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

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

mat*_*way 8

这一点看起来非常错误:

if (self.annotation != nil && _annotation != nil) {
    [_annotation release];
    self.annotation = nil;
}
Run Code Online (Sandbox Code Playgroud)

首先,你为什么要检查self.annotation_annotation为nil-ness.这实际上是两次相同的检查.

其次,你使用直接ivar访问释放_annotation,然后setter annotation_annotation再次释放和设置_annotation = nil.实际上它是这样做的:

if (self.annotation != nil && _annotation != nil) {
    [_annotation release];
    [_annotation release];
    _annotation = [nil retain];
}
Run Code Online (Sandbox Code Playgroud)

你可以看到,这将过度释放_annotation.

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

  • 字头明智&?这是合乎逻辑的,你已经到了那里.它正在检查从` - (Annotation*)annotation`返回的值是否为非nil且`_annotation`是否为非nil.` - (Annotation*)annotation`将只是一个`return _annotation`.你得到了照片. (2认同)