I have 3 classes namely GameScene, HomeScene and RecordsScene and I am trying to pass an integer value from GameScene to the RecordScene. My problem is, once the game is done, it goes home, then you have to click on Records to see the Records. Basically, the order of the three classes would be something like this:
GameScene --> HomeScene --> RecordScene
What I've been trying to do was pass the integer value from GameScene to the HomeScene then from the HomeScene, I'll pass it to the RecordScene but when I get there, the value of the integer is still 0. Here's my current code:
//GameScene
-(void)goHome:(ccTime)dt
{
HomeScene *home = [HomeScene node];
//myInt is an integer I declared on the HomeScene class, the value changes depending on the stage
home.layer.myInt = 1;
[[CCDirector sharedDirector]replaceScene:home];}
//HomeScene
-(void)viewRecord:(id)sender
{
//View record
RecordScene *record = [RecordScene node];
//lastStage is the integer from RecordScene I'm passing the value of myInt into
record.layer.lastStage = myInt;
[[CCDirector sharedDirector]replaceScene:record];}
I'm implementing the classes like so:
#implementation HomeScene
#synthesize layer;
-(id)init
{
if((self =[super init]))
{
self.layer = [HomeSceneLayer node];
[self addChild:layer];
}
return self;
}
#end
#implementation HomeSceneLayer
#synthesize myInt;
//methods
#end
Then my header looks something like so:
#interface HomeScene : CCLayer
{
int myInt;}
#property(nonatomic)int myInt;
#end
#interface HomeScene : CCScene
{
HomeSceneLayer *layer;
}
#property (nonatomic, retain)HomeSceneLayer *layer;
#end
Why is it resetting back to 0 and how do I pass the integer value from the GameScene to the RecordScene?
#synthesize myInt; creates an iVar _myInt.
RecordScene *record = [RecordScene node];
//lastStage is the integer from RecordScene I'm passing the value of myInt into
record.layer.lastStage = myInt;
you are setting record.layer.lastStage to myInt (iVar of HomeScene). Try record.layer.lastStage = self.layer.myInt;
From Apple's coding guidelines at https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CodingGuidelines/Articles/NamingIvarsAndTypes.html
Make sure the name of the instance variable concisely describes the attribute stored. Usually, you should not access instance variables directly, instead you should use accessor methods (you do access instance variables directly in init and dealloc methods). To help to signal this, prefix instance variable names with an underscore (_), for example:
Xcode now (I don't know since when) also synthesizes properties automatically, naming the iVar for #property [...] foo _foo.
Related
I'm using Cocos2d and I can not access properties of an object from another one.
Here I just want to get the hitpoints of a ship form a scene. It fails and returns an error : uncaught exception 'NSInvalidArgumentException', reason: '-[CCSprite hitpoints]: unrecognized selector...
As hitpoints is declared in the interface of the class Ship I can't figure out why.
The only thing I understand is that it's a inhéritance issue.
Let's show some code :
Ship.h
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#interface Ship : CCSprite {
int hitpoints;
}
#property (nonatomic, assign) int hitpoints;
- (id)init;
#end
Then Ship.m
#import "Ship.h"
#implementation Ship
#synthesize hitpoints;
- (id)init
{
hitpoints = 3;
self = [CCSprite spriteWithImageNamed:#"ship.png"];
return self;
}
#end
In the Scene.m
#import "Ship.h"
#implementation Scene
{
Ship *_player;
}
- (id)init
{
_player = [[Ship alloc] init];
[self addChild:_player];
// ERROR HERE
NSLog(#"%s = %d", "_player hp", [_player hitpoints]);
}
Thank you.
I suspect the issue is with your init method; you shouldn't be accessing properties until the object is initialised and you should be calling [super initWith...] instead of the class creation method.
I would suggest the following changes:
Ship.h:
#interface Ship : CCSprite
#property (nonatomic, assign) int hitpoints;
#end
Ship.m:
#import "Ship.h"
#implementation Ship
#synthesize hitpoints;
- (id)init
{
self = [super initWithImageNamed:#"ship.png"];
if (self) {
self.hitpoints = 3;
}
return self;
}
#end
Always use object.property when referencing a property, even when object == self.
This:
- (id)init
Means "a method that returns any kind of object, which is called init".
This:
self = [CCSprite spriteWithImageNamed:#"ship.png"];
Means "create an instance of CCSprite". You then return that instance.
So _player ends up being an instance of CCSprite. CCSprite does not implement hitpoints so the exception is raised.
What are you actually trying to achieve? A subclass of CCSprite? trojanfoe has covered how to write a proper init for that. Things I think you need to know:
all classes look the same at runtime;
declaring the type of class pointers helps humans and the compiler to check your code but doesn't change the code generated;
some Objective-C patterns (probably most notably including class clusters) are built around init being able to return any kind of class — it's a useful feature, not a misfeature.
just wanted to ask where I define initial class properties?
From other languages I am used to define some standard properties in the head before the content of the class starts.
For example paths to files. Settings and so on.
Where I fill these initial properties with values in Objective-C?
Thanks
Generally it's something like:
MyClass.h:
extern NSString * const staticValue1;
extern NSString * const staticValue2;
#interface MyClass : NSObject
{
NSString *_strval;
int _intval;
float _fltval;
}
#property (retain, nonatomic, readwrite) NSString *strval;
#property (assign, nonatomic, readwrite) int intval;
#property (assign, nonatomic, readwrite) float fltval;
#end
MyClass.m:
NSString * const staticValue1 = #"Something";
NSString * const staticValue2 = #"Something else";
#interface MyClass
#synthesize strval = _strval;
#synthesize intval = _intval;
#synthesize fltval = _fltval;
- (id)init
{
self = [super init];
if (self != nil)
{
[self setStrval:[NSString stringWithFormat:#"This is a %#", #"string"]];
[self setIntval:10];
[self setFltval:123.45f];
}
return self;
}
- (void)dealloc
{
[self setStrval:nil];
[super dealloc];
}
#end
This demonstrates the use of synthesized properties which are being used here to manage the memory of the instance variable _strval, which requires retaining/releasing to avoid memory leaks. Note that [self setStrval] is initialised with an autoreleased object (from [NSString stringWithFormat) and will be retained by the setter method. Alternatively these methods can be called using the following syntax, if you prefer:
self.strval = [NSString stringWithFormat:#"This is a %#", #"string"];
self.intval = 10;
self.fltval = 123.45f;
Maybe some of what you are after can be implemented with class methods.
Class methods are coded with a + (instead of the instance methods' -), and can't refer to instance variables, as they are not associated with any specific instance of the class.
This is a class method to return a default string:
+ (NSString *)myDefaultString
{
return #"Some default value";
}
You call it by simply calling it with the class name at the receiver's place. Imagine you have defined the method in a class called MyClass, the you call it like this:
NSString *str = [MyClass myDefaultString];
You'll notice that there is no alloc/init calls in this.
Public property needs to be define in .h file.
#interface MyClass {
}
#property(nonatomic, reatin) NSString *a;//Define as per needs, then synthesise in .m file
#end
For private property you need define inline category in .m file-
#interface MyClass ()
#property(nonatomic, reatin) NSString *b;//Define as per needs, then synthesise in .m file
#end
#implementation MyClass
#synthesize a = _a;
#synthesize b = _b;
- (void)viewDidLoad {
//You can initialise property here or in init method
self.a = #"Demo1";
self.b = #"Demo2";
}
//Now you can have other code for this class.
#end
Assume that I have a class with a readonly property on it.
//MyClass.h
#interface MyClass
#property (readonly) NSInteger MonitorMe;
#end
Now, let's assume the point of this property is to monitor changes of another property, within another object, and when the property is "observed" it returns a derived value by inspecting a value from the other, external object.
//MyClass.m
#implementation
#synthesize MonitorMe;
-(NSInteger) getMonitorMe
{
return globalStaticClass.OtherNSInteger;
}
... Inits and Methods ...
#end
Now, let's assume that some where I create an instance of the MyClass object, and I want to add a KVO observer on the MonitorMe property.
//AnotherClass.m
#implementation AnotherClass.m
#synthesize instanceOfMyClass;
-(id)init
{
...
instanceOfMyMethod = [MyClass init];
[MyClass addObserver: self
forKeyPath: #"MonitorMe"
options: NSKeyValuObservingOptionNew
context: nil];
...
}
My question is, since the MonitorMe property only monitors the changes of values in an external object, will the observer method execute when the value of globalStaticClass.OtherNSInteger changes? Also, if the answer is yes, how is this done?
If this works, it would seem like compiler voodoo to me.
Note
I don't think it makes a difference, but I am using ARC for this implementation and I'm compiling for an iOS device. I doubt there are compilation differences between OS X and iOS for this type of question but, if it matters, I have an iOS project that requires such an implementation outlined above.
Also, the example outlined above is a very basic setup of my actual needs. It could be argued that I could/should add an observation to the globalStaticClass.OtherNSInteger value instead of the readonly property, MonitorMe. In my actual circumstance that answer is not sufficient because my readonly property is much more complex than my example.
will the observer method execute when the value of globalStaticClass.OtherNSInteger changes?
No, but you can make that happen, via +keyPathsForValuesAffectingMonitorMe (or the more generic +keyPathsForValuesAffectingValueForKey:, if the "globalStaticClass" is actually a property of MyClass. See "Registering Dependent Keys" in the KVO Guide.
Here's a quick mockup:
#import <Foundation/Foundation.h>
#interface Monitored : NSObject
#property NSInteger otherInteger;
#end
#implementation Monitored
#synthesize otherInteger;
#end
#interface Container : NSObject
#property (readonly) NSInteger monitorMe;
#property (strong) Monitored * theMonitored;
- (void)changeMonitoredInteger;
#end
#implementation Container
#synthesize theMonitored;
+ (NSSet *)keyPathsForValuesAffectingMonitorMe {
return [NSSet setWithObject:#"theMonitored.otherInteger"];
}
- (id) init {
self = [super init];
if( !self ) return nil;
theMonitored = [[Monitored alloc] init];
[theMonitored setOtherInteger:25];
return self;
}
- (NSInteger)monitorMe
{
return [[self theMonitored] otherInteger];
}
- (void)changeMonitoredInteger {
[[self theMonitored] setOtherInteger:arc4random()];
}
#end
#interface Observer : NSObject
#end
#implementation Observer
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
NSLog(#"Observing change in: %# %#", keyPath, object);
}
#end
int main(int argc, const char * argv[])
{
#autoreleasepool {
Observer * o = [[Observer alloc] init];
Container * c = [[Container alloc] init];
[c addObserver:o
forKeyPath:#"monitorMe"
options:NSKeyValueObservingOptionNew
context:NULL];
[c changeMonitoredInteger];
[c changeMonitoredInteger];
}
return 0;
}
P.S. Cocoa style notes: properties/variables should have lowercase initial letters, and (this is actually more important now because of ARC) don't name accessor methods to start with "get" -- that has a specific meaning in Cocoa involving passing in a buffer and getting data back by reference.
I'm trying to teach myself Objective-C and as an exercise, I'm trying to write an app with one button and one label. When I click on the button, I want to trigger a calculation then see the results in the label. The following code compiles and runs with no errors or warnings but as far as I can tell, the [object method] 'call' doesn't do anything. I've spent hours on this and just don't see what's wrong. Can anyone explain the problem? Thanks.
*** testMethodViewController.h ****
#import <UIKit/UIKit.h>
#import "testBrain.h"
#interface testMethodViewController : UIViewController
{
IBOutlet UILabel *display;
testBrain *model;
}
- (IBAction)cellPressed:(UIButton *)sender;
#end
*** testMethodViewController.m ****
#import "testMethodViewController.h"
#implementation testMethodViewController
- (testBrain *)model
{
if (!model) {model = [[testBrain alloc] init];}
return model;
}
- (IBAction)cellPressed:(UIButton *)sender
{
int x = [model check:3]; //This method call doesn't work. But gets no errors.
NSLog(#"Results from model: %i", x); //Says x = 0, but I expect 6
NSString *xAsString = [NSString stringWithFormat: #"testBrain: %i", x];
display.text = xAsString; //Label is updated and displays: testBrain: 0
} //I expect: testBrain: 6
#end
*** testBrain.h ****
#import <Foundation/Foundation.h>
#interface testBrain : NSObject {}
- (int) check:(int) anInteger;
#end
*** testBrain.m ****
#import "testBrain.h"
#implementation testBrain
- (int) check:(int) anInteger //3 passed as the parameter.
{
int r = anInteger + anInteger;
NSLog(#"inside check %i", r); //Debugging line: doesn't print.
return r;
}
#end
When this code runs:
int x = [model check:3];
model is nil. In Objective-C, messages sent to nil silently do nothing, and return 0. So, as you see, x is 0 and -check: is never called.
Apparently you were expecting this method to be called automatically:
- (testBrain *)model
{
if (!model) {model = [[testBrain alloc] init];}
return model;
}
However, that method will be called only if you do it yourself, by saying [self model] or self.model. So, this line would fix it:
int x = [[self model] check:3];
Try it and see.
Going a little further: It would be clearer to remove the model method entirely, and create the instance variable model when the UIViewController is created. That way, we can guarantee that model is valid anytime any code in the testMethodViewController class runs.
You would do that by overriding UIViewController's designated initializer:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Now you can initialize your instance variables
model = [[testBrain alloc] init];
}
return self;
}
With your model method, you are halfway towards Lazy Instantiation, however to properly achieve this, you must always acess the lazily instantiated object through its accessor method. You aren't doing this in your button action, so your messages are going to nil, which is silently ignored.
This is one of the reasons you often see instance variables in objective-c declared with a leading or trailing underscore. If you then typed model anywhere in the rest of your class, it would be a compiler error, forcing you to use the accessor. Typically this is implemented with properties and the synthesize statement:
In your interface:
#property (nonatomic, strong) TestBrain* model;
In your implementation:
#synthesize model = model_;
Your model method would be:
-(TestBrain*)model
{
if (!model_)
model_ = [[TestBrain alloc] init];
return model_;
}
You would then use self.model instead of model throughout the rest of the class.
If you are just starting out, the Stanford iOS course on iTunes U is an excellent resource, a lot of this sort of material is covered.
int x = [model check:3];
This line should be:
int x = [self.model check:3];
you are almost there. You need to use #property and #synthesize in order to complete this. The #synthesize directive will direct the compiler to create the setters and getters for a particular property. The #synthesize directive tells the compiler that variable is a property. Properties allow you to use the dot syntax. i.e. self.model which will automatically the call the getter or setter method, depending on the context.
In your testMethodViewController.h file change it to look like this:
#interface testMethodViewController : UIViewController
{
IBOutlet UILabel *display;
testBrain *model;
}
#property (nonatomic,retain) testBrain *model;
- (IBAction)cellPressed:(UIButton *)sender;
#end
then in the .m implementation you need to use #synthesize after the #implementation. Like this:
#implementation testMethodViewController
#synthesize model; // tells the compiler to synthesize the setter and getter for you
- (testBrain *)model
{
if (!model) {model = [[testBrain alloc] init];}
return model;
}
then in your cellPressed: method, you need to use self.model in order for the getter to be called:
- (IBAction)cellPressed:(UIButton *)sender
{
int x = [self.model check:3]; //This method call doesn't work. But gets no errors.
NSLog(#"Results from model: %i", x); //Says x = 0, but I expect 6
NSString *xAsString = [NSString stringWithFormat: #"testBrain: %i", x];
display.text = xAsString; //Label is updated and displays: testBrain: 0
}
Hope this helps.
I dont see anywhere in the testMethodViewController.h file
IBOutlet UIButton *button;
Also check if u have properly connected all IBOutlet, IBAction & delegate, datasource.
i want to pass a variable from a viewcontroller to another the way i do is
at the first viewcontroller header file i declared a variable
1.h:
NSString *string;
and at the second viewcontroller i have import the 1.h in my 2.m file,the way i call the variable is
2.m:
NSString *string2 = 1.string
however it return an error can somebody teach me how to do,because of i dont have the strong basic at Object Oriented programming ,thanks
Just defining and declaring the two strings is not enough. That makes sure that each class has a variable called string or string2 - but when your program is running, it is actual objects that must refer to specific instances of string1 (or string2).
It's like designing a house with a letterbox - the letterbox is there on the plan of the house, but nothing happens until a specific letter gets sent to a specific house.
What you need to do is wire up the actual instances of your class, possibily in an init method, like this:
// 1.h
#interface ViewController1 : UIViewController
{
// declare our variable
NSString* string1;
}
// declare 'string1' as a property
#property (retain) NSString* string1;
// 1.m
// implements the property for string1
#synthesize string1;
// 2.h
#interface ViewController2 : UIViewController
{
// declare our variable
NSString* string2;
}
// declare 'string2' as a property
#property (retain) NSString* string2;
// 2.m
- (id)initWithTitle:(NSString*)aTitle andString1:aString
{
if (self = [super init])
{
self.title = aTitle;
self.string1 = aString;
}
return self;
}
Then in 1.m, you create the second controller, and wire the strings up, like this:
// 1.m
mySecondController = [[ViewController2 alloc] initWithTitle:#"Controller 2" andString:string1];
Although it is possible to directly access members variables like that (using the -> operator) it is not recommended.
The correct way is to provide an accessor to get/set your member variables.
In Objective-C 2.0 (iPhone and OSX 10.5) you can easily do this using the "property" keyword. As part of the property syntax you can also express how you wish "set" objects to be treated.
retained - the previous object will be released and the new one retained
copy - the object will be copied
assign - the object will be assigned.
These are the basics, I suggest you read up more about properties.
The below shows how you would use properties in your example. Note that because we are dealing with an NSString, which is an NSObject derived class, we use the "retain" option to ensure the reference counts are correctly updated.
// 1.h
#interface ViewController1 : UIViewController
{
// declare our variable
NSString* _string;
}
// declare 'string' as a property
#property (retain) NSString* string;
// 1.m
// implements the property for string
#synthesize string = _string;
// constructor for ViewController1
-(id)initWithNibName:(NSString *)nibName bundle:(NSBundle *)nibBundle {
if (self = [super initWithNibName: name bundle: bundle]) {
// Initialize the string here.
self.string = #"Hello World";
}
}
// 2.m
NSString* oldString = view.string;
view.string = #"New String";