在objective-C中获取大类的属性类型时出错

时间:2011-09-28 21:36:36

标签: objective-c objective-c-runtime

在Objective-c中,我试图使用以下代码获取包含大约14个属性的某些Object的属性:

-(NSDictionary*) getPropertiesOfClass:(Class) clazz
{
    NSMutableDictionary* dict = [NSMutableDictionary dictionary];
    unsigned int outCount, i;
    objc_property_t *properties = class_copyPropertyList(clazz, &outCount);
    for(i = 0; i < outCount; i++)
    {
        objc_property_t _property = properties[i];
        const char *propName = property_getName(_property);
        if(propName) 
        {
            const char *propType = getPropertyType(_property);
            NSString *propertyName = [NSString stringWithCString:propName encoding:NSUTF8StringEncoding];
            NSString *propertyType = [NSString stringWithCString:propType encoding:NSUTF8StringEncoding];
            [dict setValue:propertyType forKey:propertyName];
        }
    }
    free(properties);
    return dict;
}

static const char *getPropertyType(objc_property_t property) 
{
    const char *attributes = property_getAttributes(property);
    char buffer[1 + strlen(attributes)];
    strcpy(buffer, attributes);
    char *state = buffer, *attribute;
    while ((attribute = strsep(&state, ",")) != NULL) 
    {
        if (attribute[0] == 'T') 
        {
            return (const char *)[[NSData dataWithBytes:(attribute + 3) length:strlen(attribute) - 4] bytes];
        }
    }
    return "@";
}

但是我得到了一个例外(见下文)但是,当我尝试使用相同的代码来获取小对象(具有3或4个属性的对象)的属性列表时,代码无任何例外地工作并获得准确的结果。

那么,如何处理这个错误!! 感谢。

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSConcreteData initWithBytes:length:copy:freeWhenDone:bytesAreVM:]: absurd length: 4294967294, maximum size: 2147483648 bytes'
*** Call stack at first throw:
(
    0   CoreFoundation                      0x0101e5a9 __exceptionPreprocess + 185
    1   libobjc.A.dylib                     0x01172313 objc_exception_throw + 44
    2   CoreFoundation                      0x00fd6ef8 +[NSException raise:format:arguments:] + 136
    3   CoreFoundation                      0x00fd6e6a +[NSException raise:format:] + 58
    4   Foundation                          0x00800b6b -[NSConcreteData initWithBytes:length:copy:freeWhenDone:bytesAreVM:] + 135
    5   Foundation                          0x00811801 -[NSData(NSData) initWithBytes:length:] + 72
    6   solit                               0x00033449 -[ObjectMapper(private) getPropertiesOfClass:] + 489
    7   solit                               0x00031fa5 -[ObjectMapper objectFromDictionary:object:] + 197
    8   solit                               0x000327b3 -[ObjectMapper objectFromDictionary:object:] + 2259
    9   solit                               0x00033913 -[NSDictionary(ObjectMapper) toObject:withTypes:] + 243
    10  solit                               0x00031474 -[JsonWSCaller call:types:error:] + 196
    11  solit                               0x0003f4c2 +[SummaryWSCaller getFastData:] + 354
    12  solit                               0x00006fd9 -[PartSearchResultView tableView:didSelectRowAtIndexPath:] + 233
    13  UIKit                               0x00110b68 -[UITableView _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:] + 1140
    14  UIKit                               0x00106b05 -[UITableView _userSelectRowAtPendingSelectionIndexPath:] + 219
    15  Foundation                          0x0082079e __NSFireDelayedPerform + 441
    16  CoreFoundation                      0x00fff8c3 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 19
    17  CoreFoundation                      0x01000e74 __CFRunLoopDoTimer + 1220
    18  CoreFoundation                      0x00f5d2c9 __CFRunLoopRun + 1817
    19  CoreFoundation                      0x00f5c840 CFRunLoopRunSpecific + 208
    20  CoreFoundation                      0x00f5c761 CFRunLoopRunInMode + 97
    21  GraphicsServices                    0x012561c4 GSEventRunModal + 217
    22  GraphicsServices                    0x01256289 GSEventRun + 115
    23  UIKit                               0x000a7c93 UIApplicationMain + 1160
    24  solit                               0x00002289 main + 121
    25  solit                               0x00002205 start + 53
)

1 个答案:

答案 0 :(得分:4)

这与班级的规模和财产的数量无关。

这可能是因为你的记忆力有问题。当没有太多属性时,用于检索const char*变量中的属性名称和类型的内存仍然存在,但是当您开始有太多属性要循环时,这些内存区域将被新值擦除因此崩溃。

这可能与strsep不可重入的事实有关,因此多次循环和使用它会每次使用相同的内部字符缓冲区。

您应该使getPropertyType方法直接返回NSData对象而不是const char*字节,并使用NSString的{​​{1}}方法从中构建字符串(或者将此initWithData:encoding:调用直接移至initWithData:encoding:,并使其直接返回getPropertyType对象。这样您就可以避免管理指向即将自动释放的内存区域的指针。


但是你的问题可能是在尝试访问此缓冲区中的字节之前忘记检查NSString缓冲区的有效性,尤其是其长度!

特别是您使用的是attribute 而未检查[NSData dataWithBytes:(attribute + 3) length:strlen(attribute) - 4]是否至少有4个字符!

这就是为什么对attribute(内部调用dataWithBytes:length:)的调用因initWithBytes:length:copy:freeWhenDone:bytesAreVM:而崩溃,告诉您将其传递给NSInvalidArgumentException的荒谬长度({ {1}}如果解释为有符号整数)