Setting nested objects? - objective-c

I am just trying to make sure I am getting things right as I move forward with Objective-C, two quick questions if I may:
(1) Am I accessing the Position object correctly from within Rectangle? am I right to access the Position object contained within by the pointer I set in the init, or is there a better way?
(2) In [setPosX: andPosY:] Which of the two ways of setting the Position instance variables is best, or does it really not matter?
// INTERFACE
#interface Position: NSObject {
int posX;
int posY;
}
#property(assign) int posX;
#property(assign) int posY;
#end
#interface Rectangle : NSObject {
Position *coord;
}
-(void) setPosX:(int) inPosX andPosY:(int) inPosY;
// IMPLEMENTATION
#implementation Rectangle
-(id) init {
self = [super init];
if (self) {
NSLog(#"_init: %#", self);
coord = [[Position alloc] init];
// Released in dealloc (not shown)
}
return(self);
}
-(void) setPosX:(int) inPosX andPosY:(int) inPosY {
//[coord setPosX:inPosX];
//[coord setPosY:inPosY];
coord.posX = inPosX;
coord.posY = inPosY;
}
EDIT_01
Do I then call -(id)initWithX:andY: from the Rectangle object when I init it? and if so how do I go about setting posX and posY from within main()? or do I replace the init for rectangle with a further -(id)initWithX:andY: and pass the values through?
#implementation Rectangle
-(id) init {
self = [super init];
if (self) {
NSLog(#"_init: %#", self);
coord = [[Position alloc] initWithX:1234 andY:5678];
}
return(self);
}
...
cheers gary

(1) You're accessing it correctly.
(2) In objective-c 2.0, the assignments have identical affect.
Design wise, you would want to make:
-(void) setPosX:(int) inPosX andPosY:(int) inPosY;
...into a method of Position. This encapsulates both the data and the methods related to into one object. So you could have calls like:
coord = [[Position alloc] initWithX:inPosX andY:inPosY];
or
[coord setPosX:inPosX andPosY:inPosY];
all much cleaner and easier to maintain.
Edit O1
Do I then call -(id)initWithX:andY:
from the Rectangle object when I init
it?
That depends on your design. If the coord property is absolutely vital to the Rectangle instance, then you should call it when you initialize a Rectangle instance. You might even write an initializers for Rectangle that takes a position or x and y as input. eg:
-(id) initWithPosition:(Position *) aPos {
self = [super init];
if (self) {
NSLog(#"_init: %#", self);
coord = aPos;
// Released in dealloc (not shown)
}
return self;
}
You should also write a connivence initializer for the Position class:
-(id) initWithX:(NSInteger) x andY:(NSInteger) y{
self=[super init];
self.posX=x;
self.posY=y;
return self;
}
You would then call like:
Position *aPos=[[Position alloc] initWithX:100 andY:50];
Rectangle *aRec=[[Rectangle alloc] initWithPosition:aPos];
Or you could write another combination initializer for Rectangle:
-(id) initWithXCoordinate:(NSInteger) x andYCoordinate:(NSInteger) y{
self=[super init];
Position *aPos=[[Position alloc] initWithX:x andY:y];
self.coord=aPos;
return self;
}
and call it like:
Rectangle *aRec=[[Rectangle alloc] initWithXCoordinate:100
andYCoordinate:50];
These are rough examples but you get the idea. Objective-c gives you a lot of flexibility in setting up initializer so you can create any initializers you find convenient.
You do generally want to avoid using actual functions instead of methods inside classes.

(1) You also need to release it in -dealloc. Does it really make sense to create a Rectangle with an uninitialised position though? In other words, what's the behaviour of [[Position alloc] init], and is it reasonable that a Rectangle should ever have a Position in that state?
(2) They both do the same thing. The one you've written is clearer than the one you've commented out, because it indicates that you're changing properties rather than getting the object to do something. That's my opinion, anyway. Some people agree, others don't, and as I say the behaviour is the same.

Related

How to get Class Objects to automatically display behaviour

I'm having trouble understanding a finer point of Writing and calling Classes. It's probably
easlier to grasp in Swift but it bothers me to start that study without
getting it right in obj_c first. Currently I do everything in the
ViewControllers with iVars and Globals. With two apps 18 months in the App
store its overdue to put them right.
I've formed a notion that properties are the Object's State, and any methods
within determine the Objects Behaviour but so far no-one is able to tell me.
here be a typical Class header:
#interface Math : NSObject
#property (nonatomic, assign) int a;
#property (nonatomic, assign) int b;
#property (nonatomic, assign) int c;
-(int)mathemagic:(int)a adding:(int)b;
#end
and the corresponding Class implementation:
#implementation Math
#synthesize a = _a;
#synthesize b = _b;
#synthesize c = _c;
- (instancetype)init {
self = [super init];
if (self) {
_a = 0;
_b = 0;
_c = 0;
}
return self;
}
-(int)mathemagic:(int)a adding:(int)b {
_c = (a + b);
return _c;
}
#end
and finally in the appropriate places in my ViewController
#import "Math"
- (void)viewDidLoad {
[super viewDidLoad];
Math *theMath = [Math alloc]; // makes no difference if I init[]
theMath.a = 10;
theMath.b = 20;
NSLog (#" answer is %i",theMath.c);
// but still outputs to:
// answer is 0
}
Now I know can make an iVar and do it this way,
int d = [self.theMath mathemagic:theMath.a adding:theMath.b];
NSLog (#" sum: %i",d);
But i shouldn't have to. Stanford CS193P seems to always make the Class a property of the ViewController, but then everything is again expressed as self.theMath.whatever and the Data Model is no longer encapsulated away from the VC ? Maybe Stanford leaves advanced distractions to Java graduates till later.
Well for this person who's read David Flanagan's "Java in A Nutshell" ,
and Niemeyer-Knudsen's "Learning Java", It's later Now.
I shouldn't have to touch theMath.c, just by assigning values to [ theMath.a ] and [ theMath.b ] should be enough.
Where am I wrong?
I think that is because you are setting a and b = 0 in alloc init . and you are not calling [self mathemagic:a adding:b] anywhere.
I think im Math.m you should change -(instancetype)init to
- (instancetype)initWith:(int)a andb:(int)b {
self = [super init];
if (self) {
_c = [self mathemagic:a adding:b];
}
return self;
}
and in viewDidLoad use
Math *theMath = [[Math alloc]initWith:10 andb:20];
Hope this helps :)
I think you have a misconception of how Objective-C classes work.
First of all, it takes two steps to create an object in Objective-C. You must both:
Dynamically allocate memory for the new object
Initialize the newly allocated memory to appropriate values
So your Math instance initialization should look like this:
Math *theMath = [[Math alloc] init];
Just calling alloc zeroes out all instance variables of the object. Although in your case it makes no difference using [Math alloc] or [[Math alloc] init], it's not good programming style.
Second, if by "automatically display behaviour" you mean logging the result of mathemagic:adding: method, then you should pass it as an argument to NSLog function instead of theMath.c
NSLog(#" should show the sum being %i", [theMath mathemagic:theMath.a adding:theMath.b]);

When overriding `init` method, why it is important to define it again?

I had an exercise to override init method, so I need to create an init method that will set some attributes as well.
My question is: why do I need to define the original init method as well? in case that the new init method won't work?
This is my .h file:
#import <Foundation/Foundation.h>
#import "XYPoint.h"
#interface Rectangle: NSObject
#property float width, height, tx, ty;
-(XYPoint *) origin;
-(void) setOrigin: (XYPoint *) pt;
-(void) translate: (XYPoint *) point;
-(id) initWithWidth:(int) w andHeight:(int) h;
-(id) init;
#end
And .m (only the init methods):
-(id) initWithWidth:(int)w andHeight:(int)h
{
self = [super init];
if (self)
{
[self setWidth:w andHeight:h];
}
return self;
}
-(id) init
{
return [self initWithWidth:0 andHeight:0];
}
I know that it is good this way, but if someone can explain me why is that I would be appreciated.
The idea is to have a central point of initialization for your object instead of sprinkling the initialization of variables throughout each init method.
Your particular example doesn't do much justice to this pattern because you're initializing a Rectangle with 0 width and 0 height, and the default NSObject implementation resets the memory for all instance variables to zero by default, and your initWithWidth:andHeight: method does the same. However, suppose that, you were allocating unit rectangles (width 1, height 1) by default when a Rectangle object was created using,
[[Rectangle alloc] init]
then instead of doing this,
- (id)initWithWidth:(int)width andHeight:(int)height {
self = [super init];
if (self) {
[self setWidth:width andHeight:height];
}
return self;
}
- (id)init {
self = [super init];
if (self) {
[self setWidth:1 andHeight:1];
}
return self.
}
you're centralizing the point of initialization by just doing,
- (id)initWithWidth:(int)width andHeight:(int)height {
self = [super init];
if (self) {
[self setWidth:w andHeight:h];
}
return self;
}
- (id)init {
return [self initWithWidth:1 andHeight:1];
}
This also goes hand in hand with the principle of DRY a.k.a. Don't Repeat Yourself.
This is a trivial example, however, in most real-world objects, you might have a much more complicated setup with notification registration, KVO registration, etc., and then it becomes absolutely crucial that your centralize all that initialization logic.
You don't have do. You generally have one initializer that calls super (self = [super initWithXX]) and the others defer to that one.
In your specific example above its a good idea because initWithWidth:andHeight is acting as the main initializer, so if you call the default what ever code that needs to run in the main initializer will get called.
Btw: Use of the word 'and' in your initializer parameters is a little archaic by modern Objective-C conventions.

Property not set in drawRect method - iOS

I have been seeing some strange behavior when I try to access a class variable or a property in my drawRect method..
In my .h file I have the following
#interface DartBoard : UIView
{
Board * board;
int index;
}
#property (readwrite, assign, nonatomic) NSNumber * selectedIndex;
#end
In my .m file I have the following
#implementation DartBoard
#synthesize selectedIndex;
-(id)init
{
self.selectedIndex = [NSNumber numberWithInt:5];
index = 123;
return self;
}
- (void)drawRect:(CGRect)rect {
NSLog(#"selectedIndex: %d",[self.selectedIndex intValue]);
NSLog(#"index: %d",index);
}
#end
the output is
2012-06-12 19:48:42.579 App [3690:707] selectedIndex: 0
2012-06-12 19:48:42.580 App [3690:707] index: 0
I have been trying to find a solution but have had no luck..
I found a similar question but there was no real answer to the issue
See: UIView drawRect; class variables out of scope
I have a feeling drawRect is different that normal methods and is not getting the scope of the class correctly but how do I fix it?
Cheers
Damien
I have a feeling drawRect is different that normal methods and is not getting the scope of the class correctly
No, there is nothing special about -drawRect:.
There are two possibilities:
1. Your -init method is not being called.
You didn't say how this view gets created -- if you are manually calling [[DartBoard alloc] init], or if it is getting unarchived from a nib file.
If it's coming from a nib, UIView's unarchiving doesn't know that your init method should be called. It will call the designated initializer instead, which is -initWithFrame:.
So, you should implement that method instead, and make sure to call super!
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
self.selectedIndex = [NSNumber numberWithInt:5];
index = 123;
}
return self;
}
2. There might be two instances of your view: one that you are manually initing, and another one that comes from somewhere else, probably a nib. The second instance is the one that is being drawn. Since its variables and properties are never set, they show up as zero (the default value).
You could add this line to both your -init and -drawRect: methods, to see what the value of self is. (Or, check it using the debugger.)
NSLog(#"self is %p", self);

How to initialize a NSMutableArray in Objective C?

I come from a Java background and I am learning Objective C. I am trying to create a class that has a string array and a member function to modify the Array. My code looked like this:
#implementation TAWChapter
#synthesize mSubject;
#synthesize mItems;
- (id) init{
self.mItems = [[NSMutableArray alloc] init];
return self;
}
- (void) setSubject:(NSString *)subject{
self.mSubject = subject;
}
- (void) addItem:(NSString *)item{
[self.mItems addObject:#"asdf"];
}
#end
Which didn't work. I got a "[__NSArrayI addObject:]: unrecognized selector sent to instance " and a "NSInvalidArgumentException". After searching on internet, I changed the single line in the constructor to:
self.mItems = [self.mItems init];
It worked, but why? From a Java developer's point of view the first one makes more sense than the second one. And I have another line it's the same as the first one but it's working(not in a constructor). Can someone explain this to me please?
First of all, you should adhere to Objective-C coding conventions. In Objective-C, classes don't have constructors, they have initialisers. In Objective-C, initialisers invoke the initialiser of the superclass, so it should look like this:
- init
{
self = [super init];
if (!self) return nil;
// set up other stuff here
return self;
}
Second, unless you are using ARC, you might have a memory leak. The first line of your initialiser assigns an object that you own to a property that also likely takes ownership. You should use either:
// property takes care of ownership
self.mItems = [NSMutableArray array];
or:
// assign to instance variable directly with owned object
mItems = [[NSMutableArray alloc] init];
Apple sometimes discourage the use of accessor methods in initialisers because it can fiddle with things like KVO, but consistent use of accessor methods ensures proper object ownership and memory management.
By changing your line in your initialiser to:
self.mItems = [self.mItems init];
does nothing. When your initialiser method is called (which is typically just after it has been allocated), all instance variables are automatically set to nil. So what you are doing is just:
self.mItems = [nil init];
which is just:
self.mItems = nil;
and, don't use init without first allocating an instance, and never use init more than once.
If you do not let the superclass initialise itself, then it may manifest as problems in other areas. Do a Build & Analyze and ensure you have fixed up any issues pointed out by the analyser.
Since objective-c is a superset of c, it's basically c with some "OO" syntax sugar. Before you can create (or use!) an object, you must alloc space for it in the heap. you do this with [Class alloc]. The next step is the initialization of that space. alloc returns a pointer to that space in the heap, which you initialize with init ;)
So you call Class *myObjc = [[Class alloc] init];.
If you use inheritance (which you do since you inherit from NSOBject), you must make sure that your superclass initialized everything properly, thats the call to super. To make sure you don't get a runtime error, check for self != nil, which you do implicitly with if(self)
self.mItems = [self.mItems init]; // doesn't do anything, since you call the getter for mItems with self.mItems and try to init. then you try to set mItmes to itself.
use this code:
#implementation TAWChapter
#synthesize mSubject, mItems;
- (id)init
{
self = [super init];
if (self) {
mItems = [[NSMutableArray alloc] init];
}
return self;
}
- (void) setSubject:(NSString *)subject{
mSubject = subject;
}
- (void) addItem:(NSString *)item{
[mItems addObject:item];
}
#end
You should call super and assign its result to self in your init method:
- (id)init
{
self = [super init];
if (self) {
self.mItems = [[NSMutableArray alloc] init];
}
return self;
}
The another way could be creating NSMutableArray from NSArray:
NSMutableArray *myMutableArray = [NSMutableArray arrayWithArray:myArray];

Objective-C, class identity

I have the following situation, i can't resolve:
#interface Deck : NSObject
#interface MasterDeck : Deck
#interface PlayerDeck : Deck
Inside MasterDeck class, as part of initialization, i call
[self cutDeckImageIntoCards]; // We don't get to execute this method
Call results in an error [PlayerDeck cutDeckImageIntoCards]: unrecognized selector sent to instance
Indeed, PlayerDeck does not have this method .. but why is it being called at all?
After looking at MasterDeck's initialization i added a few debugging statements:
static MasterDeck *gInstance = NULL;
+(MasterDeck *) instance {
#synchronized(self) {
if (gInstance == NULL) {
gInstance = [[self alloc] init];
}
}
return gInstance;
}
-(id) init {
if (gInstance != NULL) {
return gInstance;
}
// MasterDeck
self = [super init];
// PlayerDeck
if (self) {
// Lots of stuff
[self cutDeckImageIntoCards]
// Some more stuff
}
gInstance = self;
return gInstance;
}
Ok, so MasterDeck is PlayerDeck because' Deck thinks it is a PlayerDeck ... Deck confirms
Deck is created as follows:
static Deck *gInstance = NULL;
+(Deck *) instance {
#synchronized(self) {
if (gInstance == NULL) {
gInstance = [[self alloc] init];
}
}
return gInstance;
}
-(id) init {
if (gInstance != NULL) {
return gInstance;
}
self = [super init];
if (self) {
// Do something
}
NSLog(#"Deck thinks it's a %#", [[self class ]description]); // PlayerDeck
gInstance = self;
return gInstance;
}
So, again
#interface Deck : NSObject
Assuming above Singleton Implementation, why would Deck think it's actually a PlayerDeck?
So the way you've written this, if you create the PlayDeck instance first, then the Deck instance is now a PlayDeck.
And then if you go to create the MasterDeck instance, your call to [super init] dutifully returns that previous PlayDeck instance.
So why is Deck a singleton at all? Deck has two subclasses that are singletons, but are you really looking for a singleton Deck also?
At a minimum, you can make this sort of work by not setting gInstance from within each init. Let the class method do that. Just return self from each of the init's. Also, remove the check for gInstance being not null, other Deck's init will always return Deck's instance once you have an instance of Deck.
But beyond that, I would rethink this idea a bit. Hope that helps.
You'll probably want to separate your singleton class from the actual class.
Try implementing it as in this example,
+(id) instance {
static dispatch_once_t pred;
static MasterDeck *sharedInstance = nil;
dispatch_once(&pred, ^{
sharedInstance = [[MasterDeck alloc] init];
});
return sharedInstance;
}
What happens if you replace [[self alloc] init] with [[MasterDeck alloc] init]?
It may be that somehow self is PlayerDeck. To make sure, you could NSLog([self description]) just before calling + alloc.
Edit
I assume that the interesting part of the code you have above is part of the #implementation of MasterDeck. My suggestion would be to try a lot more logging, including determining what super and [self class] are before calling [super init], although these may be misleading...
Also, as a side note, I believe that you should call [self release] in init if you are returning the previously-created instance.
What does the [super init] method look like? Can you step into it, or is it the default initializer?
Edit 2
I think you're doing singletons wrong. If you initialize a PlayerDeck, that would create a singleton in Deck which is an instance of PlayerDeck. Then later, when you initialize a MasterDeck, calling [super init] will return the instance already created by the PlayerDeck.
It looks like you try to be clever, but fact is - often the computer is even smarter. :)
Your deck class caches an instance in gInstance - in fact, it looks like it may store a Deck, a PlayerDeck, or a MasterDeck, depending on what and how you call / instantiate first. After that, this very instance is returned by that init method.
I strongly suggest to get this code clean and readable. I bet there are numerous problems with this code - but your problem is already a good example. Your logic (which should be simple, I guess) can surely be implemented much easier.
Note - I'm not against singletons, but this sort of code stacking is an absolute no-go. It's hard to get more dependency logic into those lines. ;)