You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在iOS Swift中基于NSObject实现合规JSON解析安全类并防崩溃?

安全处理iOS JSON解析:基于NSObject的兼容模型类实现

在iOS开发里,JSON解析时模型类和JSON键编码规则不兼容绝对是坑人的常见崩溃原因——尤其是后端返回的JSON突然多了个键、少了个键,或者键的命名规则和你模型的属性不一样的时候。今天我就给你整个基于NSObject的安全模型类方案,不仅能避免运行时崩溃,还能在控制台打印出那些不兼容的键,帮你快速定位问题。

核心实现思路

  • 重写setValue:forKey:和setValue:forUndefinedKey:方法,拦截系统的键值赋值流程
  • 检查模型类是否存在对应属性,不存在就跳过赋值,不抛出异常
  • 自动处理下划线命名(比如user_name)到驼峰式属性(userName)的映射
  • 实时打印缺失或不匹配的键信息,方便调试

完整代码实现

@interface SafeJSONModel : NSObject

@end

@implementation SafeJSONModel

- (void)setValue:(id)value forKey:(NSString *)key {
    // 先检查当前类是否有对应的属性
    BOOL hasMatchingProperty = NO;
    unsigned int propertyCount;
    objc_property_t *properties = class_copyPropertyList([self class], &propertyCount);
    
    for (unsigned int i = 0; i < propertyCount; i++) {
        objc_property_t property = properties[i];
        NSString *propName = [NSString stringWithUTF8String:property_getName(property)];
        
        if ([propName isEqualToString:key]) {
            hasMatchingProperty = YES;
            break;
        }
    }
    free(properties);
    
    if (hasMatchingProperty) {
        // 属性存在,正常调用父类方法赋值
        [super setValue:value forKey:key];
    } else {
        // 属性不存在,打印警告日志
        NSLog(@"⚠️ 警告:模型类 %@ 中不存在与JSON键 '%@' 对应的属性,已跳过赋值", NSStringFromClass([self class]), key);
    }
}

// 处理JSON下划线键到模型驼峰属性的映射逻辑
- (NSString *)camelCaseKeyFromJSONKey:(NSString *)jsonKey {
    NSMutableString *camelCaseKey = [NSMutableString string];
    BOOL needUppercaseNextChar = NO;
    
    for (NSInteger i = 0; i < jsonKey.length; i++) {
        unichar currentChar = [jsonKey characterAtIndex:i];
        if (currentChar == '_') {
            needUppercaseNextChar = YES;
        } else {
            if (needUppercaseNextChar) {
                [camelCaseKey appendString:[[NSString stringWithCharacters:&currentChar length:1] uppercaseString]];
                needUppercaseNextChar = NO;
            } else {
                [camelCaseKey appendString:[NSString stringWithCharacters:&currentChar length:1]];
            }
        }
    }
    return camelCaseKey;
}

// 拦截系统的undefinedKey异常,尝试驼峰匹配后仍失败则打印错误
- (void)setValue:(id)value forUndefinedKey:(NSString *)key {
    NSString *camelCaseKey = [self camelCaseKeyFromJSONKey:key];
    unsigned int propertyCount;
    objc_property_t *properties = class_copyPropertyList([self class], &propertyCount);
    BOOL foundCamelCaseProperty = NO;
    
    for (unsigned int i = 0; i < propertyCount; i++) {
        objc_property_t property = properties[i];
        NSString *propName = [NSString stringWithUTF8String:property_getName(property)];
        
        if ([propName isEqualToString:camelCaseKey]) {
            foundCamelCaseProperty = YES;
            [super setValue:value forKey:camelCaseKey];
            break;
        }
    }
    free(properties);
    
    if (!foundCamelCaseProperty) {
        NSLog(@"❌ 错误:模型类 %@ 中既不存在JSON键 '%@',也不存在对应的驼峰式属性 '%@',已跳过赋值", NSStringFromClass([self class]), key, camelCaseKey);
    }
}

@end

使用示例

比如你需要一个用户模型,直接继承SafeJSONModel即可:

@interface UserModel : SafeJSONModel
@property (nonatomic, copy) NSString *userName;
@property (nonatomic, assign) NSInteger age;
@end

@implementation UserModel
@end

假设后端返回这样的JSON:

{
    "user_name": "John Doe",
    "age": 28,
    "email": "john@example.com"
}

解析时控制台会输出:

⚠️ 警告:模型类 UserModel 中不存在与JSON键 'email' 对应的属性,已跳过赋值

而user_name会自动映射到userName属性,整个过程不会崩溃。

扩展优化建议

  • 如果你的项目用的是其他键编码规则(比如首字母大写的键),可以修改camelCaseKeyFromJSONKey:方法的逻辑来适配
  • 可以把日志输出换成自定义的日志框架,或者把这些不兼容的键信息上报到错误监控系统,方便排查线上问题
  • 如果需要处理复杂类型转换(比如JSON字符串转NSDate),可以在setValue:forKey:中添加对应的类型判断和转换逻辑

内容的提问来源于stack exchange,提问作者iAj

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 05:15:52