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

发布于 2024-12-07 05:47:27 字数 4235 浏览 0 评论 0原文

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

-(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
)

In Objective-c, I am trying to get properties of some Object that contains about 14 property using the following code:

-(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 "@";
}

But I got an exception (see below) However, When I try to use the same code to get the properties list of small objects (objects with 3 or 4 properties), the code works without any exceptions and get accurate results.

So, How to handle this error!!
Thanks.

*** 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
)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

胡渣熟男 2024-12-14 05:47:27

这与类的大小或属性的数量无关。

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

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

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


但是您的问题可能是您忘记检查属性缓冲区的有效性,尤其是它的有效性。 length 在尝试访问此缓冲区中的字节之前!

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

这就是为什么调用 dataWithBytes:length: (内部调用 initWithBytes:length:copy:freeWhenDone: bytesAreVM:) 崩溃并出现 NSInvalidArgumentException,告诉您向其传递了一个荒谬的长度 4294967294(其中如果解释为有符号整数,则为 -2

That's not related to the size of the class nor the number of properties.

This is probably because you have a problem in your memory mgmt. When there are not too much properties, the memory used to retrieve the property name and types in the const char* variables are still there, but when you start having too much properties to loop thru, these memory zones are erased by new values, hence the crash.

This is probably related to the fact that strsep is not reentrant too, so looping and using it multiple times uses the same internal char buffer each time.

You should make your getPropertyType method directly return the NSData object instead of the const char* bytes, and use NSString's initWithData:encoding: method to build the string from it (or move this initWithData:encoding: call to getPropertyType directly and make this return an NSString object directly. This way you will avoid managing pointers to memory zones that are to be autoreleased soon.


But your problem is probably that you forgot to check the validity of the attribute buffer and especially its length before trying to access the bytes in this buffer !

Especially you are using [NSData dataWithBytes:(attribute + 3) length:strlen(attribute) - 4] without checking that attribute is at least 4 characters long!

That's why the call to dataWithBytes:length: (that internally calls initWithBytes:length:copy:freeWhenDone:bytesAreVM:) crash with a NSInvalidArgumentException, telling that you pass it an absurd length of 4294967294 (which is -2 if interpreted as signed integer)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文