我一直在使用来自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
然后我通过Instruments运行它来查找僵尸对象,果然,仪器发现一个解除分配的对象在完全相同的@synthesize行发送了一条消息!
有谁知道发生了什么以及如何解决?
答案 0 :(得分:8)
这一点看起来非常错误:
if (self.annotation != nil && _annotation != nil) {
[_annotation release];
self.annotation = nil;
}
首先,为什么要检查self.annotation
和_annotation
是否为nil-ness。这实际上是两次进行相同的检查。
其次,您使用直接ivar访问权限发布_annotation
,然后annotation
的设置者将再次发布_annotation
并设置_annotation = nil
。实际上它是这样做的:
if (self.annotation != nil && _annotation != nil) {
[_annotation release];
[_annotation release];
_annotation = [nil retain];
}
您可以看到,过度释放_annotation
。
另外,说真的,只需使用ARC。 ARC(主要)编译时间与其运行的设备或操作系统版本无关。 iOS 5之前唯一不支持的是自动无效弱指针。但这确实不应该是一个问题,因为无论如何,这在Lion / iOS 5中都是全新的。