I came across this kind of syntax a while ago :
[myView setFrame:({
CGRect frame = myView.frame;
frame.size.height = heightValue;
frame;
})];
I can't remember where I saw this, but I was wondering if there are any advantages in using it instead of the classic :
CGRect frame = myView.frame;
frame.size.height = heightValue;
[myView setFrame:frame];
The former syntax simply constrains the scope of frame such that the temporary storage that was used to create it can be reclaimed after the setFrame message send.
In my opinion however, this is one of those things that adds mental effort in reading code for an optimization that a compiler will almost certainly take anyway.
It prevents frame from being scoped outside of the call to setFrame:, but at the expense of readability.
The first syntax is a GNU extension to C language, called Statement Expression. It is incompatible with any of C standards, so you would be better off avoiding altogether in favor of the second syntax, which is a lot more readable.
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
Is C99 designated initializers or the various CGSizeMake, CZRectMake, etc macros more preferable as convention in modern Objective-C?
It almost seems like a personal style preference but the one advantage I see with the C99 style is that the intent of values is clear and explicit.
CGRect rect = CGRectMake(x, y, width, height)
would give you heartache if you mixed up the order of the values where as with:
CGRect rect = (CGRect){.origin.x = x, .origin.y = y, .size.width = width, .size.height = height};
there's no doubt that .height is getting the value you're giving it. What's the reason people would continue using the existing macros?
There's nothing in the Cocoa Coding Convention docs about this and one of the style guide I've come across that notes it is the GitHub style guide: https://github.com/github/objective-c-conventions
From Apple's CGGeometry reference:
All functions described in this reference that take CGRect data structures as inputs implicitly standardize those rectangles before calculating their results. For this reason, your applications should avoid directly reading and writing the data stored in the CGRect data structure. Instead, use the functions described here to manipulate rectangles and to retrieve their characteristics.
So, these CGRect functions (CGRectGetWidth, for example) will ensure height and width are always non-negative values. (CGRect functions that do not "take CGRect data structures as inputs", like CGRectMake, don't standardize the rect's dimensions.)
Additionally, the existing functions could be transitioned in the (admittedly extremely unlikely) event that the data structure ever changed.
Finally, despite the Github style guide you mentioned, the New York Times Objective-C Style Guide suggests using the CGRect functions (for the same reason I mentioned above).
Although it would be possible to use the inline functions when getting values from CGRects, and direct struct member access when creating them, this inconsistency could hurt code readability. Obviously, as you mentioned, it's a style question.
Basically, it doesn't matter, but use the functions.
Is C99 designated initializers or the various CGSizeMake, CZRectMake, etc macros more preferable as convention in modern Objective-C?
Neither style is objectively preferable in general. The formats of the CGRect, CGPoint, and CGSize structures are part of the public documentation, and thus effectively as unchangeable as the order of arguments to CGRectMake, so using a literal is fine. Whether to use literals or the Make functions is a matter of personal taste.
I tend to use CGRectMake instead of a literal when I have all the coordinates easily accessible. I prefer a literal when I'd otherwise have to either use an extra statement or send a message twice. For example, suppose I want to make a rectangle whose origin is (0,0) and whose size is the size of view. I could do that with CGRectMake like this:
CGRect rect = CGRectMake(0, 0, view.bounds.size.width, view.bounds.size.height);
But then I'm sending the bounds message twice. I could send it just once like this:
CGRect rect = view.bounds;
rect.origin = CGPointZero;
Or like this:
CGRect size = view.bounds.size;
CGRect rect = CGRectMake(0, 0, size.width, size.height);
But I prefer to use a literal like this:
CGRect rect = (CGRect){ .origin = CGPointZero, .size = view.bounds.size };
What's the reason people would continue using the existing macros?
Familiarity. Many people aren't familiar with the literal syntax, and they won't learn it by reading Apple's documentation.
A form of safety. If you use the macro, you're forced to provide all of the structure elements. You can't accidentally omit one.
On the other hand, you must put the elements in the correct order. So it's not strictly safer than a literal.
I have been working with Objective C for a couple of months now and this has been my first time seeing this type of syntax in objective c. I think I know the equivalent of it but I wanted to be sure.
Specifically, the code looks like this
float imageHeight = [player texture].contentSize.height;
What is the '[player texture].contentSize.height equivalent to?
I know that the dot and bracket syntax can be the same thing
something.backgroundColor = [UIColor redColor];
[something setBackgroundColor:[UIColor redColor]];
The above two are pretty much the same..
So is this what's happening in my first example? I just have never seen a mix of bracket and dot syntax into one.
Thank's.
float imageHeight = [player texture].contentSize.height;
This is probably the same thing as:
float imageHeight = [[player texture] contentSize].height;
Dot syntax translates to a call of the appropriate property accessor method when it's applied to object pointers. When applied to structures, it's a direct access of one of the fields of that structure.
So, assuming that [player texture] returns a pointer to an object (probably an image), the first use of dot syntax accesses that object's contentSize property. If -contentSize also returns an object pointer, then the next use would also translate to a call of an accessor. More likely, though, the contentSize property is a NSSize, and NSSize is a structure with width and height fields. So the second use of dot syntax most likely retrieves the height field from that structure.
Mixing the two different meanings of dot syntax works fine for getting a value, but you'll get an error if you try to use it to set values. If my assumptions in the previous paragraph are correct, you can't do this:
[player texture].contentSize.height = 50;
Which of the following is best practice in Objective-C?
UITableView* view = (UITableView*) [self view];
[view setSeparatorColor:[UIColor blackColor]];
[view release];
vs.
((UITableView*) self.view).separatorColor = [UIColor blackColor];
Or is there a better way of writing this? self.view is a UIView*.
I'm asking both because I have a weird looking cast (maybe there's a better way?) and because of the following text from the official documentation, which hints that it's more than just a matter of style or personal preference:
A further advantage is that the compiler can signal an error when it detects an attempt to write to a read-only declared property. If you instead use square bracket syntax for accessing variables, the compiler—at best—generates only an undeclared method warning that you invoked a nonexistent setter method, and the code fails at runtime.
Well.... dot notation compiles down to square brackets in the end, but it is down to personal preference. I personally avoid dot notation unless I am setting / accessing a scalar type, it is too easy to look at the following for instance...
view.step = 2.0;
... and not know where step is a scalar property, or has a setter method etc. I prefer to be explicit and would use...
[view setStep:2.0];
But again personal preference I guess.
2 things
You didn't ask that but - I used to love those "One lines" in the beginning, but after some time when you get back to the code it is less readable.
the dot seems more readable to me
I would prefer that -
UITableView* view = (UITableView*)self.view;
view.setSeparatorColor=[UIColor blackColor];
But in the end it is a matter of your own preferences.
You can also cast within the bracket and save yourself a line or two using this syntax:
[(UITableView*) self.view setSeparatorColor:[UIColor redColor]];
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