使用自定义getter和setter将消息发送到使用ARC的解除分配的实例

时间:2014-07-27 09:08:32

标签: ios objective-c iphone automatic-ref-counting core-foundation

我尝试为自定义对象HFObject实现自定义getter和setter,尽管使用了ARC,但我的应用程序因Message sent to deallocated instance错误而崩溃。

我已经阅读过每一篇相关文章,那些在ARC之前写过的文章并不适用,其他一切都没有用。我打开了zombie对象调试器选项。


设置自定义HObject

在HObject.h中,我已声明了这四个属性:

@property (retain) NSString *email;        //Will use custom getter/setter
@property (retain) NSString *firstName;    //Will use custom getter/setter
@property (retain) NSDate *date;           //Will use custom getter/setter
@property (nonatomic, retain) NSMutableDictionary *values;

在HObject的实现中,我删除了email的自动获取和设置。 firstNamedate使用@dynamic就像这样

@dynamic email;
@dynamic firstName;
@dynamic date;

我还在我的HObject init中分配values字典

- (id)init {
    self = [super init];

    if (self) {
        self.values = [NSMutableDictionary dictionary];
    }

    return self;
}

实施Custom Getter&发送方

我的自定义getter / setter。我已经覆盖了

- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector

- (void)forwardInvocation:(NSInvocation *)invocation

如下图所示:

- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
{
    NSString *sel = NSStringFromSelector(selector);
    if ([sel rangeOfString:@"set"].location == 0) {
        return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
    } else {
        return [NSMethodSignature signatureWithObjCTypes:"@@:"];
    }
}

- (void)forwardInvocation:(NSInvocation *)invocation
{
    NSString *key = NSStringFromSelector([invocation selector]);
    if ([key rangeOfString:@"set"].location == 0) { 
        key = [key substringWithRange:NSMakeRange(3, [key length]-4)]; 

        id obj;   
        [invocation getArgument:&obj atIndex:2];   

        [self.values setObject:obj forKey:key];
    } else {
        id obj = [self.values objectForKey:key];

        [invocation setReturnValue:&obj];
    }
}

我在这里尝试做的是将属性的所有值存储到我的values字典中,并从那里检索它们。


App Crashing

在我的视图控制器的实现中,我尝试创建一个HObject并为我的属性设置值,然后我记录值字典以查看我的所有值。

- (void)buttonPressed:(id)sender {
    HObject *obj = [[HObject alloc] init];

    NSString *name = @"this is a string object";
    obj.date = [NSDate date];
    obj.email = @"email@website.com";
    obj.firstName = [NSString stringWithFormat:@"%@", name];


    NSLog(@"Values %@", [obj values]);
}

此时应用程序崩溃,这是我的控制台日志

2014-07-27 04:12:37.899 App[61501:60b] Values {
    Date = "2014-07-27 08:12:37 +0000";
    Email = "email@website.com";
    FirstName = "this is a string object";
}
2014-07-27 04:12:37.901 HeadsUp[61501:60b] *** -[CFString release]: message sent to deallocated instance 0x109473fe0

如果你能从这里帮助我,我将不胜感激。我也包括我的调试过程以防止你


我的调试过程(长,如果你已经可以帮助我跳过)

我最初创建了许多这些对象并将它们存储在一个数组中,当我这样做时,而不是创建一个对象。我的应用程序崩溃了一点。

我的阵列:

@property (nonatomic, strong) NSArray *array;

方法:

- (void)createArray
{
    int i = 1; //number of testobjs

    NSMutableArray *objects = [NSMutableArray arrayWithCapacity:i];

    for (int j = 0; j<i; j++) {
        HFObject *obj = [[User alloc] init];

        NSString *name = @"this is a string object";
        [obj setObject:[NSDate date] forKey:@"Date"];
        obj.email = @"email@website.com";
        obj.firstName = [NSString stringWithFormat:@"%@", name];

        [objects addObject:obj];
    }


    self.array = [NSArray arrayWithArray:objects];

}

- (void)buttonPressed:(id)sender {
    HObject *object = [self.array objectAtIndex:0];

    NSLog(@"Values %@", [object values]);
}

崩溃日志:

2014-07-27 04:34:02.893 App[61623:60b] *** -[CFString isNSString__]: message sent to deallocated instance 0x1094988f0
(lldb) 

现在这个崩溃日志与之前的崩溃日志基本相同,只是这里没有记录[object values]

内的值

调查一下这个问题,我查看了调试器的左侧窗口(不确定它实际上是什么),我看到了这个:

()

(将HFObject视为HObject,将dirtyValues视为values;我将其重命名为演示用途)

您可以看到在@"FirstName"键下没有值。

我做了几个类似的测试,我更改了我设置的属性的值并更改了数据类型。通常,FirstName不仅没有值,Date也没有。但是,电子邮件的价值始终存在。

在研究了dataTypes之后,我意识到这是因为emailstring literal而无法解除分配。另一方面,firstNamedate是对象,可以解除分配。

崩溃日志指的是CFString属性,我了解到它并没有使用ARC。我从未创建过Core Foundation对象,所以我开始通过记录[obj class]来发现它是在setter中创建的:

- (void)forwardInvocation:(NSInvocation *)invocation
{
    NSString *key = NSStringFromSelector([invocation selector]);
    if ([key rangeOfString:@"set"].location == 0) { 
        key = [key substringWithRange:NSMakeRange(3, [key length]-4)]; 

        id obj;   
        [invocation getArgument:&obj atIndex:2];   

        NSLog(@"%@", [obj class]);  //I ADDED THIS LOG

        [self.values setObject:obj forKey:key];
    } else {
        id obj = [self.values objectForKey:key];

        [invocation setReturnValue:&obj];
    }
}

再次崩溃之后,我得到了obj

2014-07-27 04:58:03.893 HeadsUp[61765:60b] __NSDate
2014-07-27 04:58:03.894 HeadsUp[61765:60b] __NSCFConstantString
2014-07-27 04:58:03.894 HeadsUp[61765:60b] __NSCFString
2014-07-27 04:58:03.904 HeadsUp[61765:60b] *** -[__NSDate release]: message sent to deallocated instance 0x109554370
(lldb) 

在这里,您可以看到Date由于某种原因被解除分配,而我的字符串现在是__NSCF字符串。

我尝试使用NSStrings将字符串重置为(__brigde NSString *)obj以及将CF对象桥接到ARC的所有其他可能方式,但这也无效。

以下是我所做的一切。我感谢任何帮助。

1 个答案:

答案 0 :(得分:4)

问题在于:

id obj;   
[invocation getArgument:&obj atIndex:2];

getArgument只需将对象指针复制到obj而不保留它。 但是,由于obj(默认情况下)是__strong变量,因此它将被释放 当前方法的结束。要解决此问题,请使用

__unsafe_unretained id obj;   
[invocation getArgument:&obj atIndex:2];   

另请注意,您的getter实现不起作用。例如,setFirstName: 使用键&#34; FirstName&#34;将密钥存储在字典中,但是getter firstName 尝试读取键&#34; firstName&#34;。

的值

(正如评论中已经提到的,它可能更容易,更不容易出错 而是分别重写三个属性的访问器方法 动态转发。)