草庐IT

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

coder 2024-01-14 原文

我一直在使用 Raphael Cruzeiro 的 PDF Annotator 中的代码,并发现了一些内存泄漏(ARC 已关闭,并将保持关闭状态以支持旧设备)。在修补了大部分之后,我只剩下最后一对了,他们让我难住了。所以在一个叫做PDFDocument的类中,他有一个CGPDFPageRefCGPDFDocument和一个自定义注释类@synthesize的属性>'。我不得不用释放来填充他的 dealloc 方法并消除一些悬空指针,除了一个小问题外,它工作得很好:在大约 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 行发送了一条消息!

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

最佳答案

这一点看起来很不对:

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

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

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

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

如您所见,将过度释放 _annotation

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

关于objective-c - 发送到解除分配实例的消息...在@synthesize 期间发送?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9448039/

有关objective-c - 发送到解除分配实例的消息...在@synthesize 期间发送?的更多相关文章

  1. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  2. ruby-on-rails - 如何使用 instance_variable_set 正确设置实例变量? - 2

    我正在查看instance_variable_set的文档并看到给出的示例代码是这样做的:obj.instance_variable_set(:@instnc_var,"valuefortheinstancevariable")然后允许您在类的任何实例方法中以@instnc_var的形式访问该变量。我想知道为什么在@instnc_var之前需要一个冒号:。冒号有什么作用? 最佳答案 我的第一直觉是告诉你不要使用instance_variable_set除非你真的知道你用它做什么。它本质上是一种元编程工具或绕过实例变量可见性的黑客攻击

  3. ruby 正则表达式 - 如何替换字符串中匹配项的第 n 个实例 - 2

    在我的应用程序中,我需要能够找到所有数字子字符串,然后扫描每个子字符串,找到第一个匹配范围(例如5到15之间)的子字符串,并将该实例替换为另一个字符串“X”。我的测试字符串s="1foo100bar10gee1"我的初始模式是1个或多个数字的任何字符串,例如,re=Regexp.new(/\d+/)matches=s.scan(re)给出["1","100","10","1"]如果我想用“X”替换第N个匹配项,并且只替换第N个匹配项,我该怎么做?例如,如果我想替换第三个匹配项“10”(匹配项[2]),我不能只说s[matches[2]]="X"因为它做了两次替换“1fooX0barXg

  4. Ruby Koans about_array_assignment - 非平行与平行分配歧视 - 2

    通过ruby​​koans.com,我在about_array_assignment.rb中遇到了这两段代码你怎么知道第一个是非并行赋值,第二个是一个变量的并行赋值?在我看来,除了命名差异之外,代码几乎完全相同。4deftest_non_parallel_assignment5names=["John","Smith"]6assert_equal["John","Smith"],names7end45deftest_parallel_assignment_with_one_variable46first_name,=["John","Smith"]47assert_equal'John

  5. ruby - 主要 :Object when running build from sublime 的未定义方法 `require_relative' - 2

    我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby​​1.9+ 关于ruby-主要:Objectwhenrun

  6. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

    如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

  7. ruby-on-rails - 如何在 Rails View 上显示错误消息? - 2

    我是rails的新手,想在form字段上应用验证。myviewsnew.html.erb.....模拟.rbclassSimulation{:in=>1..25,:message=>'Therowmustbebetween1and25'}end模拟Controller.rbclassSimulationsController我想检查模型类中row字段的整数范围,如果不在范围内则返回错误信息。我可以检查上面代码的范围,但无法返回错误消息提前致谢 最佳答案 关键是您使用的是模型表单,一种显示ActiveRecord模型实例属性的表单。c

  8. ruby-on-rails - Rails - 从另一个模型中创建一个模型的实例 - 2

    我有一个正在构建的应用程序,我需要一个模型来创建另一个模型的实例。我希望每辆车都有4个轮胎。汽车模型classCar轮胎模型classTire但是,在make_tires内部有一个错误,如果我为Tire尝试它,则没有用于创建或新建的activerecord方法。当我检查轮胎时,它没有这些方法。我该如何补救?错误是这样的:未定义的方法'create'forActiveRecord::AttributeMethods::Serialization::Tire::Module我测试了两个环境:测试和开发,它们都因相同的错误而失败。 最佳答案

  9. ruby-on-rails - RSpec:避免使用允许接收的任何实例 - 2

    我正在处理旧代码的一部分。beforedoallow_any_instance_of(SportRateManager).toreceive(:create).and_return(true)endRubocop错误如下:Avoidstubbingusing'allow_any_instance_of'我读到了RuboCop::RSpec:AnyInstance我试着像下面那样改变它。由此beforedoallow_any_instance_of(SportRateManager).toreceive(:create).and_return(true)end对此:let(:sport_

  10. ruby - 在 Ruby 中重新分配常量时抛出异常? - 2

    我早就知道Ruby中的“常量”(即大写的变量名)不是真正常量。与其他编程语言一样,对对象的引用是唯一存储在变量/常量中的东西。(侧边栏:Ruby确实具有“卡住”引用对象不被修改的功能,据我所知,许多其他语言都没有提供这种功能。)所以这是我的问题:当您将一个值重新分配给常量时,您会收到如下警告:>>FOO='bar'=>"bar">>FOO='baz'(irb):2:warning:alreadyinitializedconstantFOO=>"baz"有没有办法强制Ruby抛出异常而不是打印警告?很难弄清楚为什么有时会发生重新分配。 最佳答案

随机推荐