你可以用带有位域的C结构创建一个NSValue吗?

时间:2014-01-18 20:51:34

标签: objective-c c cocoa objective-c-runtime bit-fields

我正在尝试执行以下操作,但NSValue的创建方法返回nil。 结构中的C位域是否不受支持?

struct MyThingType {
    BOOL isActive:1;
    uint count:7;
} myThing = {
    .isActive = YES,
    .count = 3,
};

NSValue *value = [NSValue valueWithBytes:&myThing objCType:@encode(struct MyThingType)];
// value is nil here

2 个答案:

答案 0 :(得分:6)

首先,claptrap在他的评论中提出了一个非常好的观点:为什么要使用位域说明符(主要用于 进行微优化或在需要的地方手动添加填充位) ,然后将它全部包装在NSValue的实例中 这就像买一座城堡,然后住在厨房里,没有把地毯弄出来......

我认为不是,快速通过苹果开发人员came up with this ......在涉及位字段时确实需要考虑几个问题。

我刚刚找到了this,其中解释了为什么位字段+ NSValue并没有真正发挥得很好。
特别是在sizeof结构导致NSValue读取...中的数据的情况下,我们应该说不稳定的方式:
您创建的结构填充为8位。现在这些位可以读作2 int或1 long或者其他东西......从我在链接页面上看到的内容来看,这不是不可能发生的事情。
因此,基本上,当您使用位字段时,NSValue无法确定实际类型。如果存在歧义,则假定为int(大多数情况下为宽度为4),并且发生在/溢出下,并且您手上有一团糟。

由于编译器仍然具有某些自由关于实际存储成员的位置,因此传递字符串化的typedef类型(objCType: @encode(struct YourStruct)是不够的,因为那里由于编译器优化等原因,你很有可能无法理解实际的结构本身......

我建议你简单地删除位字段说明符,因为结构应该支持...至少,上次我尝试过,一个简单原始类型的结构工作得很好。

答案 1 :(得分:0)

你可以用工会来解决这个问题。只需将结构放入联合,其中另一个成员具有NSValue支持的类型,并且其大小大于您的结构。在你的情况下,long显而易见。

union _bitfield_word_union
{
  yourstructuretype bitfield;
  long plain;
};

通过使用在编译时计算大小的数组,可以使结构大小更加健壮。 (请记住,sizeof()也是编译时运算符。)

char plain[(sizeof(yourstructuretype)/sizeof(char)];

然后你可以将带有位域的结构存储到union中并读出plain成员。

union converter = { .bitfield = yourstructuretypevalue };
long plain = converter.plain;

将此值用于NSValue实例创建。读出来你必须采取相反的方式。

我很确定通过C99的技术更正,这成为标准符合(称为类型惩罚),因为您可以期望通过另一个成员值({{1)读出成员的值(bitfield)如果被读取的成员至少与正在写入的成员一样大,则定义并将其存储回来。 (plain中可能存在未定义的位9-31 / 63,但您不必关心它。)但它符合现实世界。

肮脏的黑客?也许。有人可能称之为C99。但是,将位域与plain结合使用听起来就像使用脏黑客一样。