Accessing an object outside scope in a controller class - objective-c

In my controller class, I initialize two instances of a model class (whose header is properly imported into controller class) with an NSButton. The model is really simple, just 4 members and one method - attack(). Making a silly text game!
- (IBAction)startGame:(id)sender {
Combatant *hero = [[Combatant alloc] init];
Combatant *enemy = [[Combatant alloc] init];
[console insertText:#"You have created a hero! An enemy approaches...\n"];
}
So now I have these two objects sitting there. Or do I? Because this other button, the one that's supposed to make them fight, has no idea what hero and enemy are, or that they have a class method that makes em' fight!
- (IBAction)attack:(id)sender{
[hero attack:enemy]; //Use of undeclared identifier, blah blah.
[console insertText:#"You attack the enemy! Woah!\n"];}
I get that if I initialized those objects in the attack method, then I could use them, so I gather this is something to do with scope. But I don't like the idea of sending model objects to controller methods, that seems silly.
Let me apologize: yes, this is a stupid, high-level question about the structure of Cocoa. Sorry. But I figure one of you will know exactly what I am not doing and tell me to do it!
In short, what is the Cocoa way of doing things in this situation? Thanks in advance.
-Alec

When you declare a variable in a method, it is a local variable, which means it only exists in that method. The same goes for variables you declare in functions.
If you want the variable to exist in all instance methods in the class, you need to make it an instance variable, which you do by declaring it in that { … } section in the class's #interface.
Note that any objects you store in instance variables, the instance should own. This means three things:
You'll need to either retain the object (and thereby own it) or make a copy (which you will then own) before assigning it to the instance variable.
Since you own it, you'll need to release it in the instance's dealloc method.
If you decide to replace it with a different object, you'll need to release the former object (since you still own it) and retain or copy the new object (in order to own it).
See the Objective-C Programming Language and the Memory Management Programming Guide for more information.

Related

objective C underscore property vs self

I'm was playing around with the standard sample split view that gets created when you select a split view application in Xcode, and after adding a few fields i needed to add a few fields to display them in the detail view.
and something interesting happend
in the original sample, the master view sets a "detailItem" property in the detail view and the detail view displays it.
- (void)setDetailItem:(id) newDetailItem
{
if (_detailItem != newDetailItem) {
_detailItem = newDetailItem;
// Update the view.
[self configureView];
}
i understand what that does and all, so while i was playing around with it. i thought it would be the same if instead of _detailItem i used self.detailItem, since it's a property of the class.
however, when i used
self.detailItem != newDetailItem
i actually got stuck in a loop where this method is constantly called and i cant do anything else in the simulator.
my question is, whats the actual difference between the underscore variables(ivar?) and the properties?
i read some posts here it seems to be just some objective C convention, but it actually made some difference.
_property means you are directly accessing the property.
self.property means you are using accessors.
In your case, in the setter method you are calling it, creating a recursive call.
In the course of your experiment, you've set up an endless loop which is why the simulator goes non-responsive.
Calling self.detailItem within the scope of setDetailItem: calls setDetailItem: recursively since your class implements a custom setter method for the property detailItem.
I would refer you to the Apple documentation on declared properties for the scoop on properties, ivars, etc; but briefly, declared properties are a simplified way of providing accessor methods for your class. Rather than having to write your own accessor methods (as we had to do before Objective-C 2.0) they are now generated for you through the property syntax.
The properties are basically a way of the compiler to generate a setter and getter for a given instance variable.
So when you use something like:
id detailItem = self.detailItem;
what you are doing under the hood is:
id detailItem = [self detailItem];
Same for:
self.detailItem = otherDetailItem;
would be:
[self setDetailItem:otherDetailItem];
So when you write the setter yourself.. you get in an infinite loop since you access the method itself in itself.
You can freely make use of the 'self.' notation in your class, just not when you're overriding the setter or accessor because of the mechanism I described above.
Cases in a class where I use the . notation over simply accessing the ivar is when I change the value, you never know inside your class what needs to happen when you change the value. do you have something in terms of a status that should notify some delegate that a status changed? Usually this is not the case, however, just by using the . notation you are making sure that in the future you won't have to refactor some code if you did decide to do some magic in your setter method.
I'll make an example (without ARC enabled):
#property (nonatomic, retain) NSNumber* number;
If you don't synthesize it, you can access it this way:
self.number= [NSNumber numberWithBool: YES];
This case the number is retained.If instead you synthesize it and don't use the property:
#synthesize number;
Later in the file:
number=[NSNUmber numberWithBool: YES];
You haven't used the property, so the number is not retained.That makes a relevant difference between using accessors and synthesized properties.

Objective-C : How to access properties of an object within another class?

I'm creating a game in objective-c and need to detect when two objects intersect frames.
I typically would detect this by using
if (CGRectIntersectsRect(object1.frame, object2.frame)) {/*do something*/}
My only issue is that I've created individual class files for each object. I have object1 in class1 and object2 in class2, so I can't access object1 in class2.
I tried passing the object as a parameter but received a strong id error, so I assume objects aren't allowed to be passed. How I can access the object1.frame in class2? I'm still new to objective-c, so I would appreciate any advice! Thanks in advance, let me know if you need any more information.
EDIT: Sorry for the ambiguity, I wasn't very clear. I declared a UIImageView or "object1" in class1.h and is used throughout the class1.m file.
I have another class where I declared another UIImageView "object2" in class2.h and class2.m. At one point during the class2.m file, I need to see if object1 and object2 are intersecting.
Actually as they are both image views neither of them should really be dealing with intersecting with each other. The better route is to have a third class that has a connection to each of the first two classes that takes care of detecting the collisions or to create a C function that just takes any two instances of UIImageView that conforms to an appropriately designed protocol and if there is a collision tells the objects to handle it.
void checkForCollision(object1, object2) {
if (CGRectIntersectsRect(object1.frame, object2.frame)) {
[object1 handleCollisionWith:object2];
[object2 handleCollisionWith:object1];
}
}
With something like this it makes life easier when later on you need to detect collisions involving a third or fourth object. Both objects do need to have the same superview though or this will not work.
Also, you can pass objects but you normally pass by reference. In order for object1 to tell, or ask, object2 anything it has to have a pointer to it the same applies in reverse.

Connecting actions works. Connecting outlets doesn't

I have a XIB file with my controls in it, loaded in the Interface Builder (Xcode 4.0.2 on Snow Leopard).
The file's owner is set to, let's say, the someClassController class, and I've also added (in the Interface Builder) an NSObject instance of someClass, as well.
I've managed to link e.g. a button with an action in someClassController or someClass - and it works for both of them.
However, whenever I link an outlet to ANY of them, it fails to show up; and NSLog reports NULL pointers.
Hint : My issue here could be much more complicated than it seems, since both my someClass and someClassController classes inherit other classes, which inherit other classes and so on (I'm dealing with a huge-to-chaotic codebase, and I don't really know what else could be helpful to post)... However, I would still like to hear your opinion on what might be going wrong in such a case...
When you see problems like this, it's almost always because you have more than one object of the kind that has the outlet. The one in the nib whose outlet you connected is not the one that is examining its outlet.
To investigate this, add statements in the object's initializer method(s) and possibly awakeFromNib to log the value of self.
Some (or all, or none) of the objects may be created in nibs, and some (or all, or none) of them may be created in code; objects in the latter group won't trip awakeFromNib, since they didn't.
Either way, once you've inventoried what instances of the class you have, you can kill them off until you're left with the ones you want.
To add to Peter Hosey's answer, and after reading some more details in the other question you posted about this issue, here are some other factors to consider:
The File Owner class selected in the nib is completely ignored at runtime. It's there only for design-time convenience – for checking available actions and outlets.
Is there any chance you're finding nil pointers in -init? Outlets are connected after -init and before -awakeFromNib. They'll never be connected in -init.
I'm trying to understand the sequence of initialization (from your other post). It sounds like you are creating a new instance of your CTTabContents subclass, and passing it to your CTBrowserWindowController subclass's -addTabContents: method. Then the CTBrowserWindowController loads your objects from the nib.
Or, maybe that's wrong. You might be creating a instance of your CTTabContentsController subclass. Then that object is loading TabContents.xib.
It's important to track down where the nib is being loaded and which object is being provided as the file owner at that time.
Another question: are you using manual release/retain, automatic reference counting, or garbage collection?
Finally, I reiterate the importance of printing out the self pointer in your initialization methods. In addition to -init and -awakeFromNib, try other initialization methods like your CTTabContents subclass' -initWithFrame:. When you're discovering intermittent null pointers in the rest of your debugging, print out the self pointers then, too. You'll probably be seeing different values of self then, too.

How to pass message to instance from a different class in objective c?

Stuck on what I figure is simple thing here. Basically I need to pass a pointer to an object as an argument into an instance method of another class. Said differently: I have a class that creates "Things" and I have an instance of another class that I want to receive the "Things."
Working with Cocos2D frameworks. The Things are a custom subclass of CCSprite, and the instance that receives them is a CCLayer.
I figure I'm misunderstanding something basic about ivars or maybe properties here. Any pointers in the right direction would be appreciated.
Here's the interface for ThingLayer, which should receive the "thing":
#interface ThingLayer: CCLayer {
CCTextureAtlas *textureAtlas;
ThingLayer *thingLayer;
NSMutableArray *ThingsArray;
}
- moveThingtoLayer:(Thing*)athing;
#end
And here's how I'm trying to message to the instance, from outside the class:
[ThingLayer moveThingtoLayer:thing];
I realize I'm asking the class here, not the instance... which is giving me "may not respond to..." errors. But this isn't working either (asking name of instance)...
[thingLayer moveThingtoLayer:thing];
Any obvious answers?
Looks like you should have
ThingLayer *thingLayer = [[ThingLayer alloc] init];
[thingLayer moveThingtoLayer: thing];
As a side thought, you most likely want to init a new thing in thingLayer so that instance owns the Thing, and release thing after calling moveThingToLayer.

Passing a value from one object into another with Objective-C

I have a class called GameScene, with is a subclass of a cocos2d Scene.
In there I have two layers. GameLayer and ControlsLayer. You can probably tell already that I want the ControlsLayer to move stuff around in the GameLayer. To be precise, I'm trying to control a cPBody in the GameLayer from the ControlsLayer.
At the moment, I'm trying to route the instructions from the ControlsLayer, back up into the GameScene and then back down into the GameLayer. If that makes sense. Anyway, I can't get it to work. I have a PHP background so I think I'm incorrectly applying my PHP experience to Obj-C.
My thinking is, I should be able to access a property inside a class/object using something like
aThing *someThing = someInstance->aThing;
From the sample code I've been looking at, it looks like this should work. But it doesn't. Here's the code, stripped down to as much as possible http://pastebin.com/d49c9d0be
Rather than knowing how to fix this particular issue, The question is, what don't I understand?
In Objective-C you need to define accessor methods to get at the instance variable, you can't directly access it like that unless you're calling it from the same class type (for instance when you're implementing the NSCopying protocol and need to set private variables, but don't worry about that now).
The easiest way to do that is to define a property in your header using #property(retain) SomeClass *name;, and have Objective-C generate it by putting #synthesize name = instanceVariable; in your implementation. You can then access the variable outside of that class using object.name; or [object name];. For more information take a look in the documentation for properties and Object Oriented programming.
You're not exposing the gameLayer.myBody property in any shape. You'd have to use the #property declaration (assuming objective-c 2.0) (here's an example).
I don't have any PHP background, so I don't know how it may be different in PHP.
The correct way to access a property in an object is as follows:
aThing * someThing = someInstance.aThing; // new style
or
aThing * someThing = [someInstance aThing]; // old style
If you were coding in c, the -> operator would make sense. In objective-c, however, objects are always passed around through pointers. No objective-c variable ever actually holds an object, they just hold pointers to objects. The language designers simply decided to use the [] or . syntax to access members, so that's what we have to do!