简体   繁体   English

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

[英]How to gain assignment access to CGRect elements when the CGRect is an instance variable

@interface some_class : some_parent {

 }
@property (nonatomic, assign) CGRect the_rect;

@end

...

@implementation some_class

@synthesize the_rect;

@end

After creating an instance of some_class:创建 some_class 的实例后:

instance_of_some_class.the_rect.origin.x = 0.0;

Which generates an "Expression not assignable" error.这会产生“表达式不可分配”错误。

Of course this works fine:当然,这很好用:

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

I guess the problem is that the auto-magically created setter only knows about assigning a CGRect to "the_rect".我想问题是自动创建的setter只知道将CGRect分配给“the_rect”。 OK, I get that.好的,我明白了。 Can I tell it to not create a setter so that I can access the member variable directly and make assignments to the struct members without having to assign the entire CGRect?我可以告诉它不要创建一个setter,这样我就可以直接访问成员变量并对结构成员进行赋值,而不必分配整个CGRect?

I suppose that I could break this out into four individual CGFloats for origin.x, origin.y, size.width and size.height...but you'd think there would be a way to do this with CGRects instead.我想我可以将它分解为四个单独的 CGFloats,分别用于 origin.x、origin.y、size.width 和 size.height ......但你会认为有一种方法可以用 CGRects 来代替。

What's confusing is that this, of course, works fine:令人困惑的是,这当然可以正常工作:

CGRect test = instance_of_some_class.the_rect.origin.x;

In other words, the getter knows how to navigate the struct and pull out the value of one of its elements so I can use it.换句话说,getter 知道如何导航结构并提取其中一个元素的值,以便我可以使用它。 The opposite does not seem to be the case as you just can't reach into an ivar that happens to be a struct and make an assignment to one of its elements without having to assign the entire struct.相反的情况似乎并非如此,因为您无法进入恰好是结构的 ivar 并对其元素之一进行分配而不必分配整个结构。

I also tried this:我也试过这个:

@interface some_class : some_parent {

@public

    CGRect the_rect;

 }
@property (nonatomic, assign) CGRect the_rect;

@end

After instantiating in another module:在另一个模块中实例化后:

instance.the_rect->origin.x = some_value;

...and got an error saying that the member being referenced is not a pointer. ...并得到一个错误,指出被引用的成员不是指针。 I tried taking the address of instance.the_rect... but that didn't work either.我尝试获取 instance.the_rect... 的地址,但这也不起作用。

EDIT TO CLARIFY: This is about creating a class that has some ivars that happen to be structs.编辑澄清:这是关于创建一个 class 有一些 ivars 恰好是结构。 Then, after instantiating the class somewhere else, you want to be able to make assignments to the struct ivar elements directly:然后,在其他地方实例化 class 之后,您希望能够直接对 struct ivar 元素进行分配:

class_instance.struct_ivar.element = something;

I am using CGRect as a convenient example of a well-known struct that one might want to use as an ivar.我将 CGRect 用作人们可能想要用作 ivar 的知名结构的一个方便示例。

So you know how to get around your problem, but here's why you have to do so and one of the reasons why dot syntax in Objective-C is an unholy perversion:所以你知道如何解决你的问题,但这就是你必须这样做的原因,也是 Objective-C 中的点语法是邪恶变态的原因之一:

anInstance.property = blah is compiled to the exact same code that would be generated if you had instead typed: [anInstance setProperty:blah] anInstance.property = blah被编译为与您输入的完全相同的代码: [anInstance setProperty:blah]

This works fine if your properties are only object properties.如果您的属性仅为 object 属性,则此方法可以正常工作。 Unfortunately for us, dot syntax already has an unrelated meaning in C, and that's to access members of a struct.对我们来说不幸的是,点语法在 C 中已经有了不相关的含义,那就是访问结构的成员。 ie, you can do:即,你可以这样做:

NSRect foo;
foo.origin.x = 42;

If foo were an object, then foo.origin.x = 42 would be the same as [[foo origin] setX:42] (assuming origin were also an object).如果foo是 object,那么foo.origin.x = 42将与[[foo origin] setX:42]相同(假设origin也是一个对象)。 However, since they're simply structs, it's just doing an offset calculation and setting the value in memory directly.但是,由于它们只是结构,它只是进行偏移计算并直接在 memory 中设置值。 There's no method invocation going on.没有方法调用正在进行。

Enter dot syntax in Objective-C.在 Objective-C 中输入点语法。 Now we have two different meanings of the dot operator (something that not even C++ allows, ironically enough, with all of its operator overloading).现在我们有了点运算符的两种不同含义(具有讽刺意味的是,即使是 C++ 也不允许,因为它的所有运算符都重载了)。 Sometimes you're using it to access a struct member.有时您使用它来访问结构成员。 Sometimes you're using it to invoke an object accessor.有时您使用它来调用 object 访问器。 Sometimes you can mix-and-match those uses.有时您可以混合搭配这些用途。 Sometimes you can't.有时你不能。

You can mix-and-match member vs accessor access if the expression is being used as an rvalue.如果表达式被用作右值,您可以混合匹配成员与访问器访问。 In other words, you can do:换句话说,你可以这样做:

foo = anObject.frame.origin.x;

However, if you try to do this as an lvalue, it fails:但是,如果您尝试将其作为左值执行,则会失败:

anObject.frame.origin.x = foo;

The way to work around this is to do:解决此问题的方法是:

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

Of course, if you just forgot that dot syntax existed altogether, then you'd never have this problem, because trying to coming up with a way to do this using bracket syntax would be non-sensical.当然,如果您只是忘记了点语法完全存在,那么您将永远不会遇到这个问题,因为试图想出一种使用括号语法来做到这一点的方法是没有意义的。

And this is one of the many reason why I think dot syntax was a terrible mistake.这就是为什么我认为点语法是一个可怕的错误的众多原因之一。 You never would've been confused about this if it had never been added.如果从未添加,您永远不会对此感到困惑。

You don't generally access member variables directly from outside the class.您通常不会直接从 class 外部访问成员变量。 You can declare it as @public if you really want to.如果你真的想要,你可以将它声明为@public I think you may have to create accessors for each of the rect's members:我认为您可能必须为每个矩形成员创建访问器:

- (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;
}

and so on.等等。

The issue is that when you ask for the instance's the_rect , you don't get a pointer to the same rect that the instance has, like you would if the ivar was an object pointer -- you get a new copy of the struct.问题是,当您请求实例的the_rect时,您不会得到指向实例所具有的相同 rect 的指针,就像 ivar 是 object 指针一样——您会得到结构的新副本。

There are a few questions floating around SO that discuss this issue: 1 2 3 SO周围有几个问题讨论这个问题: 1 2 3

Public ivar:公共伊瓦尔:

@interface RectHolder : NSObject {

@public
    CGRect the_rect;


}

Access:使用权:

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));

You can't assign to elements of a struct .您不能分配给struct的元素。 This is what you must do instead:这是你必须做的:

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

By doing this you aren't assigning to the rect's elements, but rather changing the entire rect value instead.通过这样做,您不会分配给 rect 的元素,而是更改整个 rect 值。

This is if you want a static class variable, which I know now is not what the OP wanted.这是如果你想要一个 static class 变量,我现在知道这不是 OP 想要的。


If you want a class variable, I don't think that's how you do it.如果您想要一个 class 变量,我认为您不会这样做。 I just tested, and the value is not sustained between elements.我刚刚测试过,元素之间的价值是不可持续的。 Not sure why it's letting you synthesize empty setters/getters though without a warning.不知道为什么它让你在没有警告的情况下合成空的 setter/getter。

For a method to create class variables, I think the best bet is to use the static keyword, much like in C.对于创建 class 变量的方法,我认为最好的办法是使用static关键字,就像在 C 中一样。 ie IE

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

And then initialize it in + (void)initalize .然后在+ (void)initalize initialize 中初始化它。 You probably have to write the setters and getters yourself though.不过,您可能必须自己编写 setter 和 getter。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM