在 Objective-C 中获取大类的属性类型时出错
在 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
这与类的大小或属性的数量无关。
这可能是因为您的内存管理有问题。当没有太多属性时,用于检索 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 theconst char*
bytes, and useNSString
'sinitWithData:encoding:
method to build the string from it (or move thisinitWithData:encoding:
call togetPropertyType
directly and make this return anNSString
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 thatattribute
is at least 4 characters long!That's why the call to
dataWithBytes:length:
(that internally callsinitWithBytes:length:copy:freeWhenDone:bytesAreVM:
) crash with aNSInvalidArgumentException
, telling that you pass it an absurd length of4294967294
(which is-2
if interpreted as signed integer)