当CGRect是实例变量时,如何获得对CGRect元素的赋值访问

时间:2011-05-02 18:14:34

标签: iphone objective-c

@interface some_class : some_parent {

 }
@property (nonatomic, assign) CGRect the_rect;

@end

...

@implementation some_class

@synthesize the_rect;

@end

创建some_class的实例后:

instance_of_some_class.the_rect.origin.x = 0.0;

生成“表达式无法分配”错误。

当然这很好用:

instance_of_some_class.the_rect = CGRectMake(0,0,0,0);

我想问题是自动神奇创建的setter只知道将CGRect分配给“the_rect”。好的,我明白了。我可以告诉它不要创建一个setter,这样我就可以直接访问成员变量并对struct成员进行赋值而不必分配整个CGRect吗?

我想我可以将它分解为四个单独的CGFloats for origin.x,origin.y,size.width和size.height ...但你认为有一种方法可以用CGRects代替它

令人困惑的是,这当然可行:

CGRect test = instance_of_some_class.the_rect.origin.x;

换句话说,getter知道如何导航struct并提取其中一个元素的值,以便我可以使用它。相反的情况似乎并非如此,因为你无法进入一个恰好是结构的ivar,并且无需分配整个结构就可以对其中一个元素进行赋值。

我也试过这个:

@interface some_class : some_parent {

@public

    CGRect the_rect;

 }
@property (nonatomic, assign) CGRect the_rect;

@end

在另一个模块中实例化后:

instance.the_rect->origin.x = some_value;

...并且错误地说被引用的成员不是指针。我尝试使用instance.the_rect的地址......但这也不起作用。

EDIT TO CLARIFY:这是关于创建一个具有一些碰巧结构的ivars的类。然后,在将该类实例化到其他地方后,您希望能够直接对struct ivar元素进行赋值:

class_instance.struct_ivar.element = something;

我正在使用CGRect作为一个众所周知的结构的一个方便示例,人们可能希望将其用作ivar。

4 个答案:

答案 0 :(得分:12)

所以你知道如何解决你的问题,但是这里为什么你必须这样做,这就是为什么Objective-C中的点语法是一个邪恶的原因之一变态:

anInstance.property = blah编译为完全相同的代码,如果您输入的话代码将生成:[anInstance setProperty:blah]

如果您的属性只是对象属性,则此方法可以正常工作。对我们来说不幸的是,点语法在C中已经具有不相关的含义,而这是为了访问结构的成员。即,你可以这样做:

NSRect foo;
foo.origin.x = 42;

如果foo是对象,那么foo.origin.x = 42将与[[foo origin] setX:42]相同(假设origin也是对象)。但是,由于它们只是结构,它只是进行偏移计算并直接在内存中设置值。没有方法调用。

在Objective-C中输入点语法。现在我们有点运算符的两个不同含义(甚至连C ++都不允许这样做,具有讽刺意味的是,它的所有运算符都被重载)。有时您使用它来访问结构成员。有时您正在使用它来调用对象访问器。有时您可以混合使用这些用途。有时你不能。

如果表达式用作右值,则可以混合使用成员与访问者访问。换句话说,你可以这样做:

foo = anObject.frame.origin.x;

但是,如果您尝试将此作为左值,则会失败:

anObject.frame.origin.x = foo;

解决这个问题的方法是:

NSRect frame = anObject.frame;
frame.origin.x = 42;
anObject.frame = frame;

当然,如果你只是忘记了点语法完全存在,那么你就永远不会遇到这个问题,因为尝试使用括号语法提出一种方法是不合情理的。

这是我认为点语法是一个可怕错误的众多原因之一。 永远如果从未添加过,就会对此感到困惑。

答案 1 :(得分:6)

您通常不直接从类外部访问成员变量。如果确实想要,您可以将其声明为@public。我想你可能需要为每个rect的成员创建访问器:

- (void)setTheRectX:(CGFloat)newX {
    the_rect.origin.x = newX;
}

- (void)setTheRectY:(CGFloat)newY {
    the_rect.origin.y = newY;
}

- (void)setTheRectOrigin:(CGPoint)newOrigin {
    the_rect.origin = newOrigin;
}

等等。

问题在于,当你要求实例的the_rect时,你没有获得指向实例所具有的同一个矩形的指针,就像你将ivar作为一个对象指针一样 - 你会得到一个结构的新副本。

围绕SO讨论此问题有几个问题:1 2 3

Public ivar:

@interface RectHolder : NSObject {

@public
    CGRect the_rect;


}

访问:

RectHolder * myRH = [[RectHolder alloc] init];
myRH->the_rect = CGRectMake(0.0, 0.0, 100, 100);

myRH->the_rect.origin.x = 10;

NSLog(@"%@", NSStringFromRect(myRH->the_rect));

答案 2 :(得分:1)

您无法指定struct的元素。这是你必须做的事情:

CGRect rect = object.rect;
rect.origin.x = 3.0f;
rect.size.height = 53.3f;
object.rect = rect;

通过执行此操作,您不会分配到rect的元素,而是更改整个rect值。

答案 3 :(得分:0)

这是你想要一个静态类变量,我现在知道它不是OP想要的。


如果你想要一个类变量,我不认为你是这样做的。我刚测试过,元素之间的价值不存在。不知道为什么它会让你在没有警告的情况下合成空的setter / getters。

对于创建类变量的方法,我认为最好的选择是使用static关键字,就像在C.中一样。

static CGRect the_rect; // in .h file outside the @implementation

然后在+ (void)initalize初始化它。你可能不得不亲自写下设置者和吸气剂。