考虑下面的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是否自动初始化struct?如果它是一个NSObject,我必须alloc init
来获取一个有效的对象,但是对于C struct,我在尝试初始化它之前得到了一个有效的结构。
有人可以解释一下,是不是这样,不是手动初始化它?
答案 0 :(得分:2)
要回答子问题,为什么不能分配从getter返回的结构组件: (作为一种动机,因为我已多次读过这个Q.)
一个。这与Cbjective-C无关。这是C标准中规定的行为。您可以检查它是否有简单的C代码:
NSMakeSize( 1.0, 2.0 ).width = 3.0; // Error
B中。不,它是不是编译器的改进。如果是这样的话,结果就是警告,而不是错误。编译器开发人员无权自行决定错误是什么。 (在某些情况下,他们有自由,但这是明确提到的。)
℃。出现此错误的原因非常简单:
表达式
的赋值NSMakeSize( 1.0, 2.0 ).width
如果该表达式是l值,那么将是合法的。如果结构是l值,则.
运算符的结果是l值:
后缀表达式后跟
.
运算符,标识符指定结构或联合对象的成员。该值是指定成员的值,82)如果第一个表达式是左值,则为左值。
ISO / IEC 9899:TC3,6.5.2.3
因此,如果表达式
,它将是可分配的NSMakeSize( 1.0, 2.0 )
是l值。它不是。原因有点复杂。要了解您必须知道.
,->
和&
之间的链接:
与.
相比,->
始终是l值。
后缀表达式后跟
->
运算符,标识符指定结构或联合对象的成员。该值是第一个表达式指向的对象的指定成员的值,并且是左值。 83)
因此 - 脚注83解释的是 - ->
,&
和.
有一个链接:
如果您可以使用S
运算符计算具有分量C的结构&
的地址,则表达式(&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值,因此可以返回指向结构的指针。使用->
运算符取消引用此指针始终为l值,因此您可以为其赋值:
// obj.aIndex returns a pointer
obj.aIndex->category = 1;
您不需要@public
。 (这真是一个坏主意。)
答案 1 :(得分:0)
该属性的语义是复制struct
,因此不需要像Objective-C对象那样分配和初始化它。给它自己的空间就像原始类型一样。
您需要小心更新它,因为这不会起作用:
obj.aIndex.category = 1;
相反,你需要这样做:
Index index = obj.aIndex;
index.category = 1;
obj.aIndex = index;
这是因为属性getter将返回struct
的副本而不是对它的引用(第一个代码段就像第二个代码段,没有最后一行指定它复制回对象)。
所以你可能最好把它变成第一类对象,具体取决于它的使用方式。