I don't understand the use of ! operator in if (! origin). The author explained that it's testing the instance variable origin to see if it's value is nonzero, but I don't quite understand what that means.
#import <Foundation/Foundation.h>
#interface XYPoint : NSObject
#property int x, y;
#end
#import "XYpoint.h"
#implementation XYPoint
#synthesize x, y;
#end
#import <Foundation/Foundation.h>
#class XYPoint;
#interface Rectangle: NSObject
-(XYPoint *) origin;
-(void) setOrigin: (XYPoint *) pt;
#end
#import "Rectangle.h"
#import "XYpoint.h"
#implementation Rectangle {
XYPoint *origin;
}
-(void) setOrigin:(XYPoint *)pt {
if (! origin)
origin = [[XYpoint alloc] init];
origin.x = pt.x;
origin.y = pt.y
}
-(XYPoint *) origin {
return origin;
}
#end
! is usually used to test for not. Most often you'll find it used when testing a conditional that should evaluate to true or false. The ! operator will then reverse that value.
It's often common for anything other than false, 0, or nil (or null depending on language) to be considered true. For example if I have x = 5 and I test if (x), the if statement's true condition will be executed as x simply has a value other than false or nil.
In this particular case it looks as if origin is a pointer to an object. Therefore, if it is pointing to an object it will have a non nil value of an integer pointer (something like 0x??????). So (! origin) is testing, 'does my variable origin point to another object, or is it nil.' If so, the code determines it is acceptable to assign a new value to origin. However, if origin was previously set, this code will not assign a new value to it.
It is testing if the value is initialized (pointer not equal to 0; operator ! is equivalent to NOT operator in Objective-C). If object origin of class XYPoint is allocated it will point to address of the object instead of 0.
Kind regards,
Bo
In this case, it just checks whether origin has been initialized. Uninitialized pointers are 0x0, which is basically 0, which is equal to false at assembler level.
! means 'not' so "if (!origin)" translates as "if not origin" and is testing whether origin has a value. This operator will vary in meaning between languages but in Objective-C, (!origin) will return true if origin is set to 0. It must have been declared otherwise your code will crash. In dynamic languages you can use this operator to test whether a variable has been declared as well whether it holds a value.
Related
Question:
I understand that origin is an instance variable of the Rectangle class which has the XYPoint type. The origin instance variable has two other instance variables x and y. I don't understand clearly, what the if statement in the rectangle class does?
I believed that the if (! origin) means if the origin is not equal to zero then do the following... Is the origin equal to zero? if yes how is it equal to zero and how is it validated in the if (! origin) statement. In other words what does the if (! origin) statement do?
Is there an instance where the origin is equal to zero? if this occurs, how will my code respond? I know that without the if statement myPoint object will not retain its initial value.
I will also like to know why we used the class directive in rectangle class rather than import. What difference does it make. I also noticed that we didn't import the XYPoint header at the rectangle's implementation. I will be very grateful if anyone is willing to help. Thanks a lot in advance.
NB: Please take a look at my codes below.
*
XYPoint Class
#import <Foundation / Foundation.h>
#interface XYPoint: NSObject
#property int x, y;
-(void) setX: (int) Xval andY: (int) yVal;
#end
#import "XYPoint.h"
#implementation XYPoint
#synthesize x, y;
-(void) setX: (int) Xval andY: (int) yVal
{ x = xVal;
y = yVal;
}
#end
*
Rectangle Class
#import <Foundation/Foundation.h>
#class XYPoint;
#interface Rectangle: NSObject
-(XYPoint *) origin;
-(void) setOrigin: (XYPoint *) pt;
#end
#import "Rectangle.h"
#implementation Rectangle
{
XYPoint *origin
}
-(void) SetOrigin: (XYPoint *) pt
{
if (! origin)
origin = [[XYPoint alloc]init];
origin.x = pt.x;
origin.y = pt.y;
}
-(XYPoint *) origin
{
return origin;
}
#end
Main
#import "Rectangle.h"
#import "XYPoint.h"
int main (int argc, char *argv[])
{
#autoreleasepool {
XYPoint *myPoint = [[XYPoint alloc]init];
[myPoint setX: 100 andY: 200];
myRect.origin = myPoint;
NSLog (#"Origin at (%i, %i)" , myRect.origin.x, myRect.origin.y);
[myPoint setX: 50 andY: 75];
NSLog (#"Origin at (%i, %i)" , myRect.origin.x, myRect.origin.y);
}
#end
Origin at (100, 200)
Origin at (100, 200)
Defining an instance variable creates a pointer that can be used to reference an instance. It doesn't create an instance for you. So, initially there is no origin.
The if statement checks if the origin exists yet, and if not it creates a new one, then the code copies the values from the one passed in the parameter.
Aside: Ideally the point class would be immutable and implement copying so rather than repeatedly creating new instances and updating them you can just copy the passed parameter. Copying would do nothing (return self) in the immutable class and is there only to support the addition of a mutable class in the future.
When being declared an object instance variable is set to nil which is zero in terms of C / Objective-C.
To use an object it must be initialized. That's what the if statement checks and does
if (! origin) // alternative syntax if (origin == nil)
origin = [[XYPoint alloc] init];
means
if the object is nil initialize it. If not skip the line.
The class directive rather than the import statement is used when only the type of the class is mentioned in the code and the header file is not needed.
In your code the import statement is required if the classes are written in separate files.
PS: There are some lowercase / uppercase typos in your code and a semicolon is missing.
Sorry I couldn't be more descriptive in the title. I know C++ and C#, and a bit of Java, and I am doing some Objective C tutorials and can't explain what is going on here.
I will show you an example:
Here is the header/interface of the rectangle class:
#import <Foundation/Foundation.h>
#class XYPoint;
#interface Rectangle : NSObject
#property int width, height;
-(int) area;
-(int) perimeter;
-(void) setW:(int)w andH:(int)h;
-(XYPoint *) origin;
-(void) setOrigin:(XYPoint*)pt;
#end
Here is the implementation:
#import "Rectangle.h"
#implementation Rectangle{
XYPoint *origin;
}
#synthesize width, height;
-(void) setW:(int)w andH:(int)h
{
width = w;
height = h;
}
-(int) area{
return width*height;
}
-(int) perimeter{
return (width+height)*2;
}
-(XYPoint *) origin{
return origin;
}
-(void) setOrigin:(XYPoint*)pt{
origin = pt;
}
#end
And here is my main program:
#import <Foundation/Foundation.h>
#import "Rectangle.h"
#import "XYPoint.h"
int main(int argc, const char * argv[])
{
#autoreleasepool {
Rectangle *r = [[Rectangle alloc]init];
XYPoint *p = [[XYPoint alloc]init];
[p setX:100 andY:300];
[r setW:6 andH:8];
r.origin = p;
NSLog(#"Width and height is %i %i", r.width, r.height);
NSLog(#"Origin is at %i,%i", r.origin.x, r.origin.y);
NSLog(#"Arear and perimeter are %i and %i", [r area], [r perimeter]);
}
return 0;
}
The line r.origin = p; confuses me. Exactly which origin is being referred to here? The getter method as defined in the interface, or the actual member variable which is declared in the implementation?
Note origin is not a property.
To be honest I expected an error. The main program should only see the getter method, but it doesn't seem like a getter method that returns something could be on the left hand side of an assignment operator. Or is the system somehow mapping that syntax to a call to setOrigin?
I had expected the only possible way to set the origin would be something like [r setOrigin: p]
I can see how something like r.width = 5 would work, as width as declared in the header as being a property, but I cannot see how r.origin = p works.
Can someone explain what is going on here?
Thanks.
Your guess is right - that syntax, called dot notation, is translated into a call to [r setOrigin:p] by the compiler. The general idea is that just like most other languages, you have getters and setters for object properties; in Objective-C, these are usually written as:
- (type)variableName;
- (void)setVariableName:(type)aVariable;
So long as your own code conforms to these conventions, you can use dot notation to both get and set variables on your classes, and trust that your methods are called.
There's obviously a lot more going on behind the scenes, so I hope this brief explanation is enough for you for now. If you're interested, though, you can look further into:
Synthesizing properties and what actually happens - methods of the form shown above are generated by the compiler, along with instance variables for actual data storage
Key-value coding, which takes advantage of these principles and conventions
Special cases for some types (for example, BOOL variables' getters are usually written as -isVariableName rather than -variableName), and how you can manage them with #property attributes
Yes, property accessors invoked by the dot notation are automagically traansformed to method calls by the compiler; thus
variable = object.property;
is equivalent to
variable = [object property];
and
object.property = variable;
is equivalent to
[object setProperty:variable];
I'm not sure how to initialize a class property that's a C struct, or pre-iOS5 base type. This is what I'd do if I was dealing with a class, but I don't know what I can check to see if this is the first time the struct has been accessed, since they're undefined at creation:
#interface GraphView : UIView
#property (nonatomic) CGPoint origin;
#end
#implementation GraphView
#synthesize origin = _origin;
- (CGPoint)origin
{
if (WHAT?) {
_origin = CGPointMake(self.bounds.origin.x + self.bounds.size.width/2,
self.bounds.origin.y + self.bounds.size.height/2);
}
return _origin;
}
#end
I realize the primary benefit of lazy initialization is for memory allocation, but if I'm doing this for all the properties that are classes, it seems clearest to use the same style for setting starting values on all my properties.
I can use some other instance variable or property to track whether the self.origin has been accessed, but that seems... not smooth. I could take care to never access self.origin before I've set it, which seems mildly entailed by the fact that structs are undefined at creation.
Is there a "right" way?
All members of a objc class will be initialized to zero on creation (even a struct). I do not get you point about memory allocation. The space is reserved for the struct (you are not storing a pointer to it). It will require the same space whether you have assigned it a value or not.
Generally you have to have an in-band invalid state like nil for pointers. In your case, you could test !CGPointEqualToPoint(_origin, CGPointZero) or !CGPointEqualToPoint(_origin, CGPointMake(-1,-1)) if you know those can never be valid. (_origin will default to CGPointZero or you'll have to set it to (-1, -1) in an init if (0, 0) is valid.)
If all possible in-band values are valid, you're stuck with an out-of-band flag as you mentioned.
The lazy evaluation of the CGPoint could be done in (at least) two different ways:
The clean way would be storing a BOOL, that keeps the information whether your CGPoint has been initialized.
#interface GraphView : UIView {
BOOL _originInitialized;
}
#property (nonatomic) CGPoint origin;
#end
#implementation GraphView
#synthesize origin = _origin;
// extend the init methods
-(id) init {
...
_originInitialized = NO;
...
}
- (CGPoint) origin {
if (_originInitialized) {
_origin = CGPointMake(self.bounds.origin.x + self.bounds.size.width/2,
self.bounds.origin.y + self.bounds.size.height/2);
_originInitialized = YES;
}
return _origin;
}
#end
In case you do not want to waste a BOOL of memory you could do a dirty approach
and use an initial value, to test your _origin against:
#interface GraphView : UIView
#property (nonatomic) CGPoint origin;
#end
#implementation GraphView
#synthesize origin = _origin;
// extend the init methods
-(id) init {
...
_origin = CGPointMake(-23.0, -42.0);
...
}
- (CGPoint) origin {
if (_origin.x == -23.0 && _origin.y == -42.0) {
_origin = CGPointMake(self.bounds.origin.x + self.bounds.size.width/2,
self.bounds.origin.y + self.bounds.size.height/2);
}
return _origin;
}
#end
But I'd recommend not to use the second one.
I have two major classes in objective C using cocos2D, DebugZoneLayer and HeroClass. Using Cocos2D may not be part of the issue.
HeroClass contains a CGPoint and a property. I have an instance of HeroClass in DebugZoneLayer initialized like hero = [[HeroClass alloc] init];
My HeroClass.h shortened to show you how I create a CGPoint vel.
#interface HeroClass : CCLayer {
#public CGPoint _vel;
}
#property(assign) CGPoint vel;
In HeroClass.m I synthesize my property like #synthesize vel = _vel;
In DebugZoneLayer.m, I can reference my hero.vel x or y just fine, but anything that assigns a value to hero.vel x or y returns the error: Lvalue required as left operand of assignment
That's right — you can't do that. A property is just a method call, and methods in Objective-C always return by value, meaning the CGPoint that gets returned is just a temporary CGPoint with the same value as the one in your object. Setting the components of this temporary value isn't allowed. You'll need to either create special setters on your class for the point's X and Y values or set the whole point at a time.
Restating Chuck's entirely correct answer in a different way..
Your problem is that CGPoints are not Objective-c Objects, they are C Structs. Your property *_vel* is not an instance of an Object, like an NSArray, NSArray or DebugZoneLayer.
As a simple and lazy example, using an int instead of a struct and a bit of psuedocode..
#interface HeroClass : CCLayer {
int _numberOfLives;
}
#end
#implementation HeroClass
- (id)init {
[super init];
_numberOfLives = 3;
}
- (int)livesRemaining {
return _numberOfLives;
}
#end
you couldn't set the value of _numberOfLives like this..
foo = [[HeroClass alloc] init];
bar = [foo livesRemaining];
bar = 2;
Changing the value of bar won't change the value of foo's _numberOfLives instance variable because when you called -livesRemaining, bar was set to a copy of the current value of _numberOfLives.
In short, you need to learn you some C.
if i have a class named
#interface myClass : NSObject {
int firstnum;
int secondnum;
}
//an a method that looks like this
-(myClass *) myMethod (myClass *) f;
#end
what does that method return as? im used to seeing something like (int) myMethod knowing that it returns an integer. but when its returning an object such as the classes name here what can it possibly return? if you'd like ill write out the whole project im working/studying on. (some methods have been truncated to keep the question simple. but if you'd like ill post it let me know. thnx
#import <Foundation/Foundation.h>
#interface Fraction : NSObject {
int numerator;
int denominator;
}
#property int numerator, denominator;
-(Fraction *) add: (Fraction *) f;
#end
#import "Fraction.h"
#implementation Fraction
#synthesize numerator, denominator;
-(Fraction *) add: (Fraction *) f
{
Fraction *result = [[Fraction alloc] init];
// To add two fractions:
// a/b + c/d = ((a*d) + (b*c)) / (b * d)
result.numerator = numerator * f.denominator + denominator * f.numerator;
result.denominator = denominator * f.denominator;
[result reduce];
return result;
}
#end
The question might be: what is an object? An object is some memory set aside to hold a bunch of values, and it's associated with some methods to interact with those values. You very rarely want to copy that whole chunk of memory. Instead, you pass around pointers to the memory location. So when you pass in or return an object, you are actually just passing around a pointer to the object's location in memory. Pointers are basically just integer values, so that's what's being returned.
In most languages, you don't even have to think about this, and in fact you rarely have to do it in Objective-C either. Just remember that MyClass* is the type of objects that are of class ´MyClass´ and that they respond to all messages that are declared on that class.
That method returns just what it claims to: a pointer to an instance of myClass. In the case of your add: method, it returns the pointer result that you created.
Incidentally, unless you have Garbage Collection enabled, you should add [result autorelease] to that method before returning; otherwise your result fraction will be around forever and you'll leak memory.
CocoaDevCentral's Objective-C Tutorial will help with that memory management, and also might get you more comfortable with methods returning pointers to objects.
it looks like you're working through a copy of the Programming in Objective-C book. In my copy, there is a section at the end of Chapter 13 (Underlying C Language Features) called "How Things Work". I think you are looking for "Fact #2: An Object Variable is Really a Pointer". In your case, the add method is returning a pointer to an instance of the Fraction class. (The asterisk, *, means pointer, the class name before it says what sort of thing it is pointing to) There is more about pointers earlier in the chapter if you don't mind skipping about.
I'm going to be picky and try and rewrite this a bit
#import <Foundation/Foundation.h>
#interface Fraction : NSObject {
// You should really use the recommended type instead of int
NSInteger numerator;
NSInteger denominator;
}
// Don't try and keep these on one line.
// Also, don't depend on default values for assign, retain, or copy when declaring
#property (assign, nonatomic) numerator;
#property (assign, nonatomic) denominator;
// declare the add: function which takes a pointer to a Fraction as a parameter
// and returns a pointer to the restulting fraction
- (Fraction *)add:(Fraction *)aFraction;
#end
#import "Fraction.h"
#implementation Fraction
// Again, don't try and keep these on one line.
// It's easier to visually note the number of synthesised properties matches
// the number of declared properties.
#synthesize numerator;
#synthesize denominator;
// Assume that there is an init function.
- (Fraction *)add:(Fraction *)aFraction
{
Fraction *result = [[Fraction alloc] init];
// To add two fractions:
// a/b + c/d = ((a*d) + (b*c)) / (b * d)
result.numerator = self.numerator * aFraction.denominator
+ denominator * aFraction.numerator;
result.denominator = self.denominator * aFraction.denominator;
[result reduce]; // I assume this is implemented.
return result; // Assuming garbage collection, otherwise use
// return [result autorelease];
}
#end