Please help with understanding how #import works without using inheritance.
So I have created a Class named Person and another Class named Body (both classes are inherited from NSObject).
The Body class has 3 ivars (set with properties)
int hands;
int feet;
int legs;
I then imported and created an instance of the Body class ( *body) into the Person class. My understanding is that I have now made a 'type' from the Body class. This ivar was then set with properties.
In main, I then created an instance from the Person class named person.
Main recognizes person.body.feet but I cannot get and set its value directly. The only way it can be done is passing its value into a new int variable and then print out the new variable.
Obviously I’m a struggling newbe but I really want to understand this problem -
#import <Foundation/Foundation.h>
#import "Person.h"
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
Person *person = [[Person alloc]init];
// this works and NSLog prints out the value is 2
int feet = person.body.feet = 2;
NSLog(#"person.body = %i",feet);
// this does not work - NSLog prints out the value is 0;
[person.body setFeet:2];
NSLog(#"person.body = %i", person.body.feet );
[person release];
[pool drain];
return 0;
}
#import <Foundation/Foundation.h>
#import "Body.h"
#interface Person : NSObject {
Body *body;
}
#property (assign) Body *body;
#end
#import "Person.h"
#implementation Person
#synthesize body;
#end
#import <Foundation/Foundation.h>
#interface Body : NSObject {
int hands;
int feet;
int legs;
}
#property int hands;
#property int feet;
#property int legs;
#end
#import "Body.h"
#implementation Body
#synthesize hands;
#synthesize feet;
#synthesize legs;
#end
My guess is that you haven't assigned a Body object to person.body anywhere.
All objects in Objective-C are allocated on the heap. All those asterisks denote that the variables are pointers, which are mere numbers pointing to memory locations where the objects live. When you declare a property that holds an object, all you are really doing is creating a place to store a pointer to the object. You aren't creating the object itself.
So somewhere (most likely in Person's designated initialiser), you'll want to actually create a Body object and assign it to the body property.
If you don't do this, then the memory location will remain at 0, which is equivalent to nil. When you chain the assignment here:
int feet = person.body.feet = 2;
...you're assigning 2 to both feet and person.body.feet, but since person.body is nil, the setter message to it is ignored. When later you try to log person.body.feet, again, it will be nil and return nil/0.
Edit: Having looked at your update, yes, this is your problem.
This line does not create a Body object:
Body *body;
It creates an instance variable for a pointer to a Body object.
This line does not create a Body object:
#property (assign) Body *body;
It declares a property, which is syntactic sugar for getter and setter method declarations.
This line does not create a Body object:
#synthesize body;
It synthesises your previously declared property by creating the getter and setter methods.
You need to actually create and initialise a Body object and then assign it to your Person object's body property. For instance:
- (id)init {
if (self = [super init]) {
self.body = [[Body alloc] init];
}
return self;
}
Also, as you aren't using ARC, you should almost certainly declare that property to be retain not assign, and release it in your Person's dealloc method.
Related
I do not quite understand the way of declaring instance variable and property. Can someone explain in detail the difference of the two codes below? In the second method, if I use _name for instance variable, is it the same function as the way declaring name in first code? Thanks!
First Code:
// OrderItem.h
#import <Foundation/Foundation.h>
#interface OrderItem : NSObject
{
#public NSString *name;
}
-(id) initWithItemName: (NSString *) itemName;
#end
// OrderItem.m
#import "OrderItem.h"
#implementation OrderItem
-(id) initWithItemName: (NSString *) itemName {
self = [super init];
if (self) {
name = itemName;
NSLog(#"Initializing OrderItem");
}
return self;
}
#end
Second Code:
// OrderItem.h
#import <Foundation/Foundation.h>
#interface OrderItem : NSObject
#property (strong,nonatomic) NSString *name;
-(id) initWithItemName: (NSString *) itemName;
#end
// OrderItem.m
#import "OrderItem.h"
#implementation OrderItem
-(id) initWithItemName: (NSString *) itemName {
self = [super init];
if (self) {
_name = itemName;
NSLog(#"Initializing OrderItem");
}
return self;
}
#end
In the first case you have declared an instance variable (usually called an ivar in Objective-C).
In the second case you have declared a property. A property is a set of two methods, a getter and a setter, usually accessed using dot notation, e.g. self.name. However, an ivar is automatically synthesized for the property with the name _name. That instance variable is what you are accessing in your init.
You can actually change the name of the ivar using #synthesize name = _myName or not have it at all (if you declare the getter and setter manually, no ivar will be synthesized).
Objective-C properties are a rather complicated topic so don't worry if you don't understand it immediately.
Properties are public which means that other classes can read and write them (even classes that aren't subclasses of the class that declares the property). In addition to that, properties provide a getter and a setter method (mutator methods). The getter of a property gets called every time you access the property
NSString *aName = self.name;
Whereas the setter is accessed every time you write or assign to a property.
self.name = #"Some name";
Instance variables (or ivars) are, by default, only visible for the class that declares it and its subclasses (also known as being encapsulated by their class). You can change this default behavior when you add the keyword #public to your ivar declaration though.
I'm having a hard time understanding private instance variables through example. After reading about private instance variables, I went to Xcode and tried to verify how they work.
In the book I'm reading, it states that if you declare an instance variable in the implementation file of a superclass, the instance variable will be private and inaccessible to subclasses.
I tried proving it doing the following without any luck.
/** SuperClass interface file**/
#import <Foundation/Foundation.h>
#interface ClassA : NSObject
-(void) setX;
-(void) printX;
#end
/**SuperClass implementation file **/
#import "ClassA.h"
#implementation ClassA
{
int x;
}
-(void) setX
{
x = 100;
}
-(void) printX
{
NSLog(#" x is equal to %i", x);
}
#end
/** interface file of subclass **/
#import "ClassA.h"
#interface ClassB : ClassA
#end
/**Main file **/
#import "ClassA.h"
#import "ClassB.h"
int main(int argc, const char * argv[])
{
#autoreleasepool
{
ClassA * a;
a = [[ClassA alloc] init];
ClassB * b;
b = [[ClassB alloc] init];
[b setX];
[b printX];
}
return 0;
}
The program prints the following:
x is equal to 100
isn't "x" a private instance variable and inaccessible by object "b", because "x" is declared in the implementation file of superClass "a" while "b" is a subclass?
The books says "instance variables that are to be accessed directly by a subclass must be declared in the interface section and not in the implementation section...Instance variables declared or synthesized in the implementation section are private instance variables and are not directly accessible by subclasses."
Really confused by this.
The methods setX and printX are public and visible and thus can be called on the instance of ClassB. Since they are public they can also be called by the ClassB, like this.
#implementation ClassB
- (void)fig {
[self setX];
}
#end
What can't be done is for ClassB to directly access the value x. Like this:
#implementation ClassB
- (void)foo {
NSLog(#"x is now %i", x);
}
#end
ClassB does not have direct access to x, but it has indirect access to x through the superclass methods. This indirect access is an object oriented programming concept known as encapsulation.
Ivars have #protected attribute by default, means subclasses can access them. To declare ivar as private, use #private attribute before ivar declaration:
#interface ClassA : NSObject
{
#private
int x;
}
If you declare your ivars in #implementation section, the only way for them to be visible to subclasses is to import .m file in your subclass, but your can't use them because they're private.
Or don't use ivars at all, since Objective-C properties now create ivars automatically. If you need a private property, you can declare it via anonymous category in .m file like this:
#interface MyClass ()
#property (nonatomic) NSInteger x;
#end
UPDATE:
I think I understand what's confusing you. Public and protected ivars are inherited by subclasses and can be accessed directly as instance variables of subclass, no need to use accessor methods from a subclass.
I am reading: http://cocoacast.com/?q=node/103
I came across this method in the above page:
-(void)foo
{
self->iVar = 5; //legal because we are referencing a member variable
iVar = r; // illegal because we are referencing a readonly property
}
I then created a project in Xcode.
Test0.h
#import <Foundation/Foundation.h>
#interface Test0 : NSObject
{
#private int iVar;
}
#property (readonly, assign) int iVar;
- (void) foo;
#end
Test0.m
#import "Test0.h"
#implementation Test0
#synthesize iVar;
- (void) foo
{
iVar = 5;
}
#end
main.m
#import <Foundation/Foundation.h>
#import "Test0.h"
int main(int argc, const char * argv[])
{
#autoreleasepool {
Test0 *t1 = [[Test0 alloc] init];
[t1 foo];
NSLog(#"%d", t1.iVar);
}
return 0;
}
The result in Console is 5.
My questions:
The web page mentioned above uses
self->iVar = 5
I have used iVar = 5
What difference does it make?
The web page mentioned above says
iVar = r; // illegal because we are referencing a readonly property
Is iVar = 5 (which I have used) not same as iVar = r ?
How is it not illegal?
how does readonly property work?
The compiler simply does not generate or verify the existence of the setter. It will generate the getter, and that property may be backed by an ivar. As well, the setter is not declared in the class interface.
self->iVar = r;
vs
iVar = r;
What difference does it make?
None. They are identical. They are both direct assignment of the ivar. It's similar to other languages, when adding a superfluous scope resolution (e.g. this->).
The difference is when you attempt to use the setter method (e.g. self.prop = val or [self setProp:val];). In this case, the compiler will emit a warning and the runtime would throw an exception (unless you or a subclass defined the setter yourself).
The web page mentioned above says iVar = r; // illegal because we are referencing a readonly property.
That's wrong. Direct access to the ivar of a readonly property is fine if the ivar exists. You don't see an error in this case because you're accessing the ivar directly, rather than using the setter.
Other issues:
The article incorrectly states atomics are 'mutexed'.
The article incorrectly states atomic properties guarantee thread safety.
I have code such as the following which doesn't work unless the category name is left blank
PrivatePropertyTest.h
#interface PrivatePropertyTest : NSObject
#property (readonly) int readonly;
- (void) testMethod;
#end
PrivatePropertyTest.m
#import "PrivatePropertyTest.h"
#interface PrivatePropertyTest (/*If I place a name in here it doesn't work*/)
#property (readwrite) int readonly;
#end
#implementation PrivatePropertyTest
#synthesize readonly;
- (void) testMethod
{
self.readonly = 2;
}
#end
main.m
#import <Foundation/Foundation.h>
#import "PrivatePropertyTest.h"
int main (int argc, const char * argv[])
{
#autoreleasepool {
PrivatePropertyTest *pPT = [[PrivatePropertyTest alloc] init];
[pPT testMethod];
//pPT.readonly = 1;
// insert code here...
NSLog(#"Hello, World!");
}
return 0;
}
When I give it a name is says the setter Method for the selector doesn't exist. Does this have to do with name mangling? Why does it matter if I name it or not?
If you can declare unnamed categories like this, can more than one unnamed category be declared for the same class?
The problem is, is that isn't an unnamed category. It's a class extension.
Class extensions are a bit like categories in that you can declare methods and properties for the class to implement. But extensions are actually part of the main implementation of the class. This means you can do things like override property access behavior (what you are doing) or add ivars.
Class extensions are required to be compiled with the implementation block, and there can only be one of them.
For my beginner's level independent study of Objective-C, I was asked to add a counter to a class, so that each time a method was used on it, it would ++. However, I misinterpreted this as "Each time the method the method is called, ++." After realizing how to do what was asked of me, I pondered how I could fashion a method that would return a counter in addition to what the method was called to return. If I were to use a static int in addition to variable++; on each call of the method, how can I extract that value of variable in my main program?
Example code from comment:
-(Fraction *) add: (Fraction *) f {
static int fractaddcount;
fractaddcount++;
Fraction *result = [[Fraction alloc] init];
result.numerator = numerator * f.denominator + denominator * f.numerator;
result.denominator = denominator * f.denominator;
return result;
}
Make fractaddcount an instance variable and initialize it to 0 in the init method. Then it can be accessed by other methods in the class.
Additionally if you make it a property other classes will be able to access it. You can even make the property readonly in the .h file and read/write in the .m file (class extension).
Example:
in .h:
#property (non atomic, readonly, assign) int fractaddcount;
in .m:
in class extension:
#Interface TheClassName ()
#property (non atomic, readwrite, assign) int fractaddcount;
#end
in the implementation:
#synthesize fractaddcount;
in init: This is somewhat optional since when the class is instantiated the ivars are cleared to nil (0).
fractaddcount = 0;
in your code:
self.fractaddcount = self.fractaddcount + 1;