Ошибка при получении типов свойств большого класса в target-C - PullRequest
1 голос
/ 29 сентября 2011

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

1 Ответ

4 голосов
/ 29 сентября 2011

Это не связано ни с размером класса, ни с количеством свойств.

Вероятно, это связано с тем, что у вас проблема с памятью. Когда свойств не слишком много, память, используемая для получения имени и типа свойства в переменных const char*, все еще там, но когда вы начинаете иметь слишком много свойств для циклического выполнения, эти зоны памяти стираются новыми значениями, поэтому авария.

Это, вероятно, связано с тем, что strsep также не является реентерабельным, поэтому циклическое использование и его многократное использование каждый раз использует один и тот же внутренний буфер символов.

Вы должны заставить ваш метод getPropertyType напрямую возвращать объект NSData вместо const char* байтов и использовать метод NSString initWithData:encoding: для построения строки из него (или переместить этот вызов initWithData:encoding: в getPropertyType напрямую и сделайте так, чтобы он возвращал объект NSString напрямую. Таким образом, вы избежите управления указателями на зоны памяти, которые должны быть автоматически освобождены в ближайшее время.


Но Ваша проблема, вероятно, в том, что вы забыли проверить правильность буфера attribute и особенно его длину, прежде чем пытаться получить доступ к байтам в этом буфере!

Особенно вы используете [NSData dataWithBytes:(attribute + 3) length:strlen(attribute) - 4] , не проверяя, что attribute имеет длину не менее 4 символов!

Вот почему вызов dataWithBytes:length: (который внутренне вызывает initWithBytes:length:copy:freeWhenDone:bytesAreVM:) завершается с NSInvalidArgumentException, сообщая, что вы передаете ему абсурдную длину 4294967294 (которая равна -2, если интерпретируется как целое число со знаком)

...