2015-05-11 36 views
1

考虑下面的结构:Objective-C如何将C struct初始化为属性?

typedef struct _Index { 
    NSInteger category; 
    NSInteger item; 
} Index; 

如果我用这个结构作为一个属性:

@property (nonatomic, assign) Index aIndex;

当我视图控制器alloc init后立即访问它没有任何初始化,LLDB打印为:

(lldb) po vc.aIndex 
(category = 0, item = 0) 
(lldb) po &_aIndex 
0x000000014e2bcf70 

我有点困惑,结构已经有了有效的内存地址,甚至在我想分配一个之前。 Objective-C是否会自动初始化结构体?如果它是一个NSObject,我必须做alloc init来获得一个有效的对象,但是对于C结构,甚至在我尝试初始化它之前,我会得到一个有效的结构体。

有人可以解释,而且它可以这样,而不是手动初始化它?

回答

0

该属性的语义是复制struct,所以它不需要像Objective-C对象那样分配和初始化。它被赋予了自己的空间,就像一个原始类型。

你需要小心更新它,因为这将无法正常工作:

obj.aIndex.category = 1; 

相反,你将需要这样做:

Index index = obj.aIndex; 
index.category = 1; 
obj.aIndex = index; 

这是因为属性的getter将返回一个拷贝struct而不是对它的引用(第一个片段就像第二个片段,没有最后一行将拷贝分配回对象)。

因此,根据使用方法的不同,将它变成第一类对象可能会更好。

+0

我不遵循,为什么我不能只使用'obj.aIndex.category = 1;' – Wingzero

+0

因为它会在对象的副本上设置新的类别' aIndex'(这样行就像我的第二个例子的前两行)。 – trojanfoe

+1

我试过了,它会引发编译器错误,说'表达式不可分配'。看起来与你说的不一样? – Wingzero

2

要接听subquestion,为什么你不能指定从getter返回一个结构组件: (作为一个动机,这是因为我看了这个问答几次。)

A.这有什么好用Cb-C做。这是C标准中规定的行为。你可以检查它的简单的C代码:

NSMakeSize(1.0, 2.0).width = 3.0; // Error 

B.不,这是不编译器的改进。如果是这样,警告将是结果,而不是错误。编译器开发人员不能自由决定错误是什么。 (有一些情况下,他们有自由,但这种明确提到。)

下这样做的原因错误是很容易的:

赋值表达式

NSMakeSize(1.0, 2.0).width 
如果该表达式是l值,则

将是合法的。甲.操作者的结果是L值,如果该结构是L值:

A postfix expression followed by the . operator and an identifier designates a member of a structure or union object. The value is that of the named member,82) and is an lvalue if the first expression is an lvalue.

ISO/IEC 9899:TC3,6.5.2.3

因此这将是可分配的,如果表达式

NSMakeSize(1.0, 2.0) 

是一个l值。不是这样。原因有点复杂。要明白,你必须知道.->&之间的联系:

相反.->始终是一个L值。

A postfix expression followed by the -> operator and an identifier designates a member of a structure or union object. The value is that of the named member of the object to which the first expression points, and is an lvalue. 83)

因此 - 这就是脚注83解释 - ->&,并且.有一个链接:

如果你能计算具有与&运营商C成分的结构S的地址,表达式(&S)->C相当于S.C。这要求您可以计算地址S。但你永远无法做到这一点与返回值,即使它是一个简单的整数...

int f(void) 
{ 
    return 1; 
} 

f()=5; // Error 

...或...指针

int *f(void) 
{ 
    return NULL; 
} 

f()=NULL; // Error 

你总是得到同样的错误:它是不可转让。因为它是一个r值。这是显而易见的,因为目前还不清楚,

a)编译器是否返回值的方式,无论他是否在地址空间中执行它。

b)中时的时间的返回值的使用寿命超过

再回到这意味着返回值是一个r值的结构。因此.运算符的结果是一个r值。您不能为r值赋值。

D.解决方案

有一种解决方案分配给“返回的结构”。有人可能会决定,不管它是否好。由于->总是一个l值,所以可以返回一个指向该结构的指针。取消引用此指针与->运营商一贯的左值的结果,所以你可以赋值给它:

// obj.aIndex returns a pointer 
obj.aIndex->category = 1; 

你不需要@public了点。 (真的是个坏主意)