利用Runtime清空单例属性

- (void)qy_clearPropertyValue {
        // 1.存储属性的个数
        unsigned int propertyCount = 0;
        // 2.通过运行时获取当前类的所有属性
        objc_property_t *properties = class_copyPropertyList([self class], &propertyCount);
        // 3.遍历属性
        for (int i = 0; i < propertyCount; i ++) {
            objc_property_t property = properties[i];
            NSString *propertyName = [NSString stringWithFormat:@"%s", property_getName(property)];
            // 4.利用KVC 设置属性nil
            [self setValue:nil forKey:propertyName];
        }
        // 释放指针
        free(properties);
}

但是上面代码有一个重大bug:

当对象里面的属性不是对象属性时(例如NSInteger CGFloat等),程序会崩溃,控制台报错:could not set nil as the value for the key

解决办法:

  • 第一种:重写setNilValueForKey:方法,逐个属性判断

  • 第二种:异常捕捉,统一处理

- (void)setNilValueForKey:(NSString *)key {
    if ([key isEqualToString:@"属性名1"]) {
        _属性1 = 0;
    }  else if ([key isEqualToString:@"属性名2"]) {
        _属性1 = 0;
    }
    ...
    ...
    ...
  else {
        [super setNilValueForKey:key];
    }
}

最优雅的写法应该是:

- (void)qy_clearPropertyValue {
        // 1.存储属性的个数
        unsigned int propertyCount = 0;
        // 2.通过运行时获取当前类的所有属性
        objc_property_t *properties = class_copyPropertyList([self class], &propertyCount);
        // 3.遍历属性
        for (int i = 0; i < propertyCount; i ++) {
            objc_property_t property = properties[i];
            NSString *propertyName = [NSString stringWithFormat:@"%s", property_getName(property)];
            // 4.利用KVC 设置属性nil
            @try {
                [self setValue:nil forKey:propertyName];
            } @catch (NSException *exception) {
                [self setValue:@0 forKey:propertyName];
            } @finally {
                //
            }
            
        }
        // 释放指针
        free(properties);
}

PS:诡异的死锁Bug:

#pragma mark - 退出登录
- (void)loginOut {
    UIAlertController *alertVc = [UIAlertController alertControllerWithTitle:nil message:@"您真的要退出吗?" preferredStyle:UIAlertControllerStyleAlert];
    [alertVc addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleDefault handler:nil]];
    [alertVc addAction:[UIAlertAction actionWithTitle:@"确认" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            
            //  下面 这一行执行会有死锁的bug, app会卡主,处于假死状态
            [[QYMyInfoModel sharedMyInfoModel] qy_clearPropertyValue];

    }]];
  
    [self presentViewController:alertVc animated:YES completion:nil];    
}

但是,把那句代码放到异步线程执行,就不会死锁,不知道什么原因,如果有人知道,麻烦告知一下

#pragma mark - 退出登录
- (void)loginOut {
    UIAlertController *alertVc = [UIAlertController alertControllerWithTitle:nil message:@"您真的要退出吗?" preferredStyle:UIAlertControllerStyleAlert];
    [alertVc addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleDefault handler:nil]];
    [alertVc addAction:[UIAlertAction actionWithTitle:@"确认" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            
            //  下面 这一行执行会有死锁的bug, app会卡主,处于假死状态,所以在异步线程执行
                  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            [[QYMyInfoModel sharedMyInfoModel] qy_clearPropertyValue];
        });

    }]];
  
    [self presentViewController:alertVc animated:YES completion:nil];    
}

你可能感兴趣的:(利用Runtime清空单例属性)