Сообщение отправлено нераспределенному экземпляру... Отправлено во время @synthesize?
Я использовал код из 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
Затем я проверил его по Инструментам, чтобы найти объекты зомби, и, конечно же, Инструменты нашли освобожденный объект, отправляющий сообщение в той же строке @synthesize!
У кого-нибудь есть идеи, что происходит и как это исправить?
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 - это автоматические нулевые слабые указатели. Но это действительно не должно быть проблемой, поскольку в Lion / iOS 5 это все равно совершенно новое.