Message sent to deallocated instance with ARC usin

2020-07-27 07:28发布

问题:

I'm trying to implement a custom getter and setter for my custom object HFObject and my app crashed with a Message sent to deallocated instance error despite using ARC.

I've read every single related post, the ones that were written pre-ARC don't apply, and everything else didn't help. I have the zombie object debugger option turned on.


Setting up the custom HObject

Within HObject.h I have declared these four properties:

@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;

In the implementation of HObject, I have removed the automatic getting and setting of email. firstName, and date by utilizing @dynamic like so

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

I also allocate the values dictionary in my HObject init

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

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

    return self;
}

Implementing Custom Getter & Sender

For my custom getter/setter. I have overridden

- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector

and

- (void)forwardInvocation:(NSInvocation *)invocation

As shown below:

- (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];
    }
}

What I'm trying to do here is store all of the values of the property into my values dictionary and retrieve them from there as well.


App Crashing

In the implementation of my view controller, I try to create a HObject and set values for my properties, and then I log the values dictionary to see all of my values.

- (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]);
}

At that point the app crashes and this is my console log

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

If you can help me from here, I would greatly appreciate it. I am also including my debugging process in case that will help you


My debugging Process (Long, skip if you can already help me)

I originally created many of these objects and stored them in an array, and when I do that, as opposed to creating a single object. my app crashed a bit different.

My array:

@property (nonatomic, strong) NSArray *array;

Methods:

- (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]);
}

Crash log:

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

Now this crash log is almost the same as the one before, except this didn't log the values inside of [object values]

Investigating the issue a bit, I looked at the left window (not sure what it is actually called) of the debugger and I saw this:

(Treat HFObject as HObject and dirtyValues as values; I renamed them for presentational purposes)

You can see that under the key @"FirstName" there is no value.

I did several similar tests where I changed the values of the properties I was setting and changed the data types. More often than not, not only did FirstName not have a value, neither did Date. However, the value of email was always present.

After researching about dataTypes, I realized it was because email was a string literal which can't deallocated. On the other hand firstName and date were objects, which can be deallocated.

The crash log refers to a CFString property, which I learned doesn't use ARC. I never created a Core Foundation object, so I set out to found out it was being created in setter by logging the [obj class]:

- (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];
    }
}

After crashing one more time, I got the obj classes

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) 

Here you can see Date is being deallocated for some reason, and my string are now __NSCF strings.

I tried resetting the strings to NSStrings using (__brigde NSString *)obj and every other possible way you can bridge a CF object to ARC, however that didn't work either.

Here is everything I've done. I appreciate any and all help.

回答1:

The problem is here:

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

getArgument simply copies the object pointer into obj without retaining it. However, since obj is (by default) a __strong variable, it will be released at the end of the current method. To solve the problem, use

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

Note also that your getter implementation does not work. For example, setFirstName: stores the key in the dictionary using the key "FirstName", but the getter firstName tries to read the value for the key "firstName".

(As already mentioned in a comment, it would probably easier and less error-prone to just override the accessor methods for the three properties separately, instead of dynamic forwarding.)