As the question states, I would prefer an immutable version of CGPoint, CGSize, and CGRect, although I would rather use a type which is supported by the iOS framework, to avoid converting between types.
So, is there any supported immutable version of CGPoint? If not, any recommendations?
Thanks!
You can just use constant CGPoints etc.
E.g.:
const CGSize size = (CGSize){6.0f, 4.0f};
Using CGSizeMake won't work as they are not compile time constants and you can't use variables, but this is the closest you'll get to an immutable CGSize. If that is not good enough then you'll need to use an object and convert when needed.
When a CGPoint (or any other struct - CGRect, etc.) is a property of an Objective-C object, the mechanics of getter/setter methods and the value semantics of C structs means that there is some level of protection against indirectly modifying the value of a CGPoint property of an object. For example, an expression like someObject.someCGPointProperty.x = 123 will not actually modify the x value of the CGPoint owned by someObject, because the someCGPointProperty accessor will return the CGPoint by value, which means that you are setting the x value on a copy of the CGPoint. To actually modify the CGPoint owned by that object, you would need to do something like:
CGPoint point = someObject.someCGPointProperty;
point.x = 123;
someObject.someCGPointProperty = point;
Regarding control over the number of instances you create: since CGPoint is a struct, it's passed around by value - effectively it's copied - in function arguments or Objective-C messages. So there's going to be plenty of copies of thees structs moving around anyway. However, these structs are created on the stack, not the heap, and a CGPoint is only 16 bytes (possibly smaller on 32-bit iOS devices?), so it's doubtful you need to worry about any performance implications.
Also, see #hypercrypt's more succinct answer regarding const values, as that may address your intentions well.
You can use the NSValue wrapper class. It can wrap a point, a size, a rect and more. It's immutable, so whatever you store inside it can't be changed (remember that if you store a pointer, the memory area to which points can still be changed).
Example
NSValue* value= [NSValue valueWithCGPoint: CGPointMake(x,y)];
If you try to set value.CGPointValue.x or y you get a compile error. The getter returns just a copy of the CGPoint held in the object, so there's no way to change it's value.
Related
I know that I can't use this:
myView.frame.origin.x = 25.0;
and that I have to use this instead:
CGRect myFrame = myView.frame;
myFrame.origin.x = 25.0;
myView.frame = myFrame;
And I'm doing it all the time, but I don't know why I must do it that way. I would like to fill that gap in my understanding. Can someone explain ?
Nowadays Xcode gives you "Expression not assignable". Some time ago you got a compile error "Lvalue required as left operand of assignment".
There are two distinct dot syntaxes being used here. They look the same, but they do different things depending on what they are operating on and what is being done with it:
The first myView.frame is a shorthand for [myView frame], a method call that returns a CGRect struct by value.
myFrame.origin.x is accessing ordinary struct members in the traditional C fashion.
The second myView.frame is again a shorthand, but because the statement is an assignment it translates to calling a different method, [myView setFrame:myFrame].
In your single-line top example, you get a copy of the rect and set its x, but never copy it back to the view. You have to explicitly differentiate between the method calls, the dot syntax sugar can't magic them into a single call.
The reason this does not work is due to the mixing of two syntaxes.
First you have "." as a shortcut for calling the accessor functions (an Objective-C feature).
So
a.b becomes [a getB];
a.b = 5 becomes [a setB:5];
And then theres "." as direct struct member access (pure C). So
a.b really is a.b;
a.b really is a.b = 5;
Combining this in a set-value-case like this, doesn't work.
Because ...
If you could call
myView.frame.origin.x = 25.0;
The "myView.frame" part equals [myView getFrame] and you get a copied CGRect frame (a C struct)
The "myView.frame.origin" gives you a CGPoint origin (also a struct) of the copied CGRect
The "myView.frame.origin.x = 25.0" gives you a CGFloat x of the origin and now you want to assign something to it and here comes the problem...
You try to set a variable of a struct of a struct, which is ok, but there is no pointer from the UIView to the struct, so it is copied instead. So you copy and then you set and then you expect that the set action is somehow forwarded through the initial get to the UIView, well and this just doesn't work.
Of course you could wonder why Apple hasn't just created a shortcut, so that in the end your copied frame is automatically reinjected into a auto appended setFrame call, I guess you just have to live with how it is.
So remember, it would work if you'd get a pointer to the frame, but you don't, you get a copied struct instead.
So if you expect myView.frame.origin.x = 25.0 to work you indirectly expect your call to be automagically translated into some sort of
[myView setFrame:[myView getFrame:frame].origin.x = 25.0].
Well I guess you can admit that this looks wrong.
Also imagine if you'd get a direct pointer to the CGRect frame and you'd change something through that pointer, how would the UIView know that it's size has changed and that it has to update itself ? If on the other hand a [myView setFrame:newFrame] call is made, then UIView can do all the necessary readjusting itself.
A CGRect is a struct, which is something from standard C. A CGRect is not an Objective C object, so when you assign to one of its members, no setter method is called. Without a setter method being called, UIKit will not be able to know that anything has changed, and so will not be able to update the screen display.
Edit: as has been pointed out, the assignment will be to a copy of the struct.
When you manipulate the data directly, no accessor is called, so the UI cannot update itself - or inform any other component that wants to know about changes.
Edit: As pointed out by walkytalky, you will get a copy of the data, so changing it doesn't have any effect on the original anyway. The following example will show this:
UIView *aView = [[UIView alloc] initWithFrame:CGRectMake(50,50,100,100)];
NSLog(#"%f", aView.frame.origin.x); // will give 50
aView.frame.origin.x = 17; // operates on a copy of the rect only
NSLog(#"%f", aView.frame.origin.x); // will still give 50
I'm working on a project that has C code embedded within Objective-C code. The C code produces some void * pointers that I would like to pass between Objective-C methods, so I'd like to wrap them in Objective-C objects to make an NSSet or something to that effect.
I have looked into NSData, which seems to accept arbitrary data, but wants to know the length of the memory chunk, that I don't have.
Any help is appreciated.
The NSValue class is usually used for this task:
NSValue* value = [NSValue valueWithPointer: myPointer];
and
void* myPointer = [value pointerValue];
Note, though, that the pointer given will not receive any special treatment with respect to memory management. You (and you alone) are responsible for making sure, that the memory it points to remains valid as long as pointers to that memory region exist and are reachable in some part of your program.
Note, though, that using such a value with NSSet or as key in a NSDictionary might or might not yield the intended effect:
NSData is essentially a byte buffer. It actually represents the content of the memory in question. Comparing NSData instances for equality compares at byte level. This is one of the reasons, NSData needs to know about the length of the memory region in question.
NSValue with a pointer value is an entirely different beast. Here, that actual (numeric) pointer value is the essential thing. No consideration is given (when comparing two NSValue instances) to the actual content at the address.
I have a game object which processed in two completely different places. In Contact Listener i check some conditions and if they occur i must save one or more portions of complex data. So i decided to use struct. For example:
struct SomeStruct
{
int value1;
int value2;
CGPoint value3;
b2Vec2 value4;
};
typedef SomeStruct SomeStruct;
In Game Scene i go through all game objects and if its the stack/array not empty, do some stuff and wipe it.
In Contact Listener it repeats from the beginning.
I must use this architecture because of strict order of execution (method must be called after other methods).
I suspect that i need something like vector or NSMutableArray (i think it will not work with struct), so vector may the the only way.
But don't understand how to achieve it. May you help me with some code/pseudocode or link to the book/article where i can found a solution?
Cocoa provides NSValue class for that purpose:
This creates an object that you can add to NSMutableArray:
NSValue *someObj = [NSValue valueWithBytes:&myStruct objCType:#encode(SomeStruct)];
You can use [someObj pointerValue] to access a void* representing the address of the structure that you put in NSValue.
There is a lot of solutions for this problem.
Don't use struct. An obj-c class is practically the same thing as a struct.
Use CFArray (CFArrayCreateMutable) and put it there as a pointer.
Use a C++ class with STL vector.
Use a C array (SomeStruct[]) and increase its length when you need it.
Use a classic implementation of a stack, with a linked list (every struct has a pointer to the next value).
I am learning how to create iPhone apps and I have seen that most of the variables we create store memory addresses (pointers) instead than holding the actual value or object. I also have found out that every time you declare a variable with the pointer char (*) you know that the variable is going to hold the address and whenever you don't use the (*) mark to declare a variable you know that it will hold the value instead than the memory location. But I don't know when to us which. for example I have:
CGFloat someVar = [image1 alpha]; // This variable does not require *
// image 1 is a: IBOutlet UIImageView
and in this other case I have to use a pointer:
UIViewController *someOtherVar = [[UIViewController alloc] init]; // this type of var requires *
It will be nice if I can know when can I use each instead of trying each until project compiles.
The function and method signatures in the headers and documentation will indicate what the type is.
For example, here is how the alpha property is declared for UIView:
#property(nonatomic) CGFloat alpha;
There is no * anywhere, so you know it returns CGFloat and not CGFloat*.
In contrast, the backgroundColor property is declared like this:
#property(nonatomic, copy) UIColor *backgroundColor;
so you know it will return UIColor* (a pointer).
Some things are declared with a type of id, which is always going to be a pointer to an object.
In general, Objective-C objects (types declared with #interface) will always be referenced as pointers, while primitive C types and structs will often (but not always) be passed and returned by value.
Oversimplifying greatly, but it depends on the data being returned.
From your examples, CGFloat is a wrapper for float, which is a primitive C data type. [image1 alpha] returns a CGFloat. UIViewController is an object type. [[UIViewController alloc] init] returns a pointer to this allocated memory, (UIViewController *). Therefore you need to use the pointer.
Pointers can be used in more cases than can be described. Generally speaking, as you are starting out, you typically use pointers for objects. But I encourage you to check the documentation to determine the exact data type. It will provide hints as to the data type returned by a specific method or property.
I know that I can't use this:
myView.frame.origin.x = 25.0;
and that I have to use this instead:
CGRect myFrame = myView.frame;
myFrame.origin.x = 25.0;
myView.frame = myFrame;
And I'm doing it all the time, but I don't know why I must do it that way. I would like to fill that gap in my understanding. Can someone explain ?
Nowadays Xcode gives you "Expression not assignable". Some time ago you got a compile error "Lvalue required as left operand of assignment".
There are two distinct dot syntaxes being used here. They look the same, but they do different things depending on what they are operating on and what is being done with it:
The first myView.frame is a shorthand for [myView frame], a method call that returns a CGRect struct by value.
myFrame.origin.x is accessing ordinary struct members in the traditional C fashion.
The second myView.frame is again a shorthand, but because the statement is an assignment it translates to calling a different method, [myView setFrame:myFrame].
In your single-line top example, you get a copy of the rect and set its x, but never copy it back to the view. You have to explicitly differentiate between the method calls, the dot syntax sugar can't magic them into a single call.
The reason this does not work is due to the mixing of two syntaxes.
First you have "." as a shortcut for calling the accessor functions (an Objective-C feature).
So
a.b becomes [a getB];
a.b = 5 becomes [a setB:5];
And then theres "." as direct struct member access (pure C). So
a.b really is a.b;
a.b really is a.b = 5;
Combining this in a set-value-case like this, doesn't work.
Because ...
If you could call
myView.frame.origin.x = 25.0;
The "myView.frame" part equals [myView getFrame] and you get a copied CGRect frame (a C struct)
The "myView.frame.origin" gives you a CGPoint origin (also a struct) of the copied CGRect
The "myView.frame.origin.x = 25.0" gives you a CGFloat x of the origin and now you want to assign something to it and here comes the problem...
You try to set a variable of a struct of a struct, which is ok, but there is no pointer from the UIView to the struct, so it is copied instead. So you copy and then you set and then you expect that the set action is somehow forwarded through the initial get to the UIView, well and this just doesn't work.
Of course you could wonder why Apple hasn't just created a shortcut, so that in the end your copied frame is automatically reinjected into a auto appended setFrame call, I guess you just have to live with how it is.
So remember, it would work if you'd get a pointer to the frame, but you don't, you get a copied struct instead.
So if you expect myView.frame.origin.x = 25.0 to work you indirectly expect your call to be automagically translated into some sort of
[myView setFrame:[myView getFrame:frame].origin.x = 25.0].
Well I guess you can admit that this looks wrong.
Also imagine if you'd get a direct pointer to the CGRect frame and you'd change something through that pointer, how would the UIView know that it's size has changed and that it has to update itself ? If on the other hand a [myView setFrame:newFrame] call is made, then UIView can do all the necessary readjusting itself.
A CGRect is a struct, which is something from standard C. A CGRect is not an Objective C object, so when you assign to one of its members, no setter method is called. Without a setter method being called, UIKit will not be able to know that anything has changed, and so will not be able to update the screen display.
Edit: as has been pointed out, the assignment will be to a copy of the struct.
When you manipulate the data directly, no accessor is called, so the UI cannot update itself - or inform any other component that wants to know about changes.
Edit: As pointed out by walkytalky, you will get a copy of the data, so changing it doesn't have any effect on the original anyway. The following example will show this:
UIView *aView = [[UIView alloc] initWithFrame:CGRectMake(50,50,100,100)];
NSLog(#"%f", aView.frame.origin.x); // will give 50
aView.frame.origin.x = 17; // operates on a copy of the rect only
NSLog(#"%f", aView.frame.origin.x); // will still give 50