OStack程序员社区-中国程序员成长平台

标题: ios - 使用自定义 getter 和 setter 将消息发送到带有 ARC 的已释放实例 [打印本页]

作者: 菜鸟教程小白    时间: 2022-12-13 08:45
标题: ios - 使用自定义 getter 和 setter 将消息发送到带有 ARC 的已释放实例

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

我已经阅读了所有相关的帖子,在 ARC 之前编写的那些不适用,其他所有内容都没有帮助。我打开了僵尸对象调试器选项。


设置自定义 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;
}

实现自定义 Getter & Sender

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

- (NSMethodSignature *)methodSignatureForSelectorSEL)selector

- (void)forwardInvocationNSInvocation *)invocation

如下图:

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

- (void)forwardInvocationNSInvocation *)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 setObjectbj forKey:key];
    } else {
        id obj = [self.values objectForKey:key];

        [invocation setReturnValue:&obj];
    }
}

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


应用程序崩溃

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

- (void)buttonPressedid)sender {
    HObject *obj = [[HObject alloc] init];

    NSString *name = @"this is a string object";
    obj.date = [NSDate date];
    obj.email = @"[email protected]";
    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 protected]";
    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 protected]";
        obj.firstName = [NSString stringWithFormat"%@", name];

        [objects addObjectbj];
    }


    self.array = [NSArray arrayWithArraybjects];

}

- (void)buttonPressedid)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 也没有。但是,电子邮件的值(value)始终存在。

在研究了 dataTypes 之后,我意识到这是因为 email 是一个无法释放的 string literal。另一方面,firstNamedate 是可以释放的对象。

崩溃日志引用了一个 CFString 属性,我了解到它不使用 ARC。我从未创建过 Core Foundation 对象,所以我开始通过记录 [obj class]:

来发现它是在 setter 中创建的
- (void)forwardInvocationNSInvocation *)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 setObjectbj 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 字符串。

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

这是我所做的一切。我感谢任何和所有的帮助。



Best Answer-推荐答案


问题出在这里:

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

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

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

还请注意,您的 getter 实现不起作用。例如,setFirstName: 使用键“FirstName”将键存储在字典中,但 getter firstName 尝试读取键“firstName”的值。

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

关于ios - 使用自定义 getter 和 setter 将消息发送到带有 ARC 的已释放实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24979501/






欢迎光临 OStack程序员社区-中国程序员成长平台 (https://ostack.cn/) Powered by Discuz! X3.4