Being noob in objective-c,I just came across a problem in object ownership in a class that cannot explain the reason.
Suppose that I have a class called Point that describes a point with x,y.
A class called rectangle has an object of type Point that represents its left bottom edge. With following implementation
#implementation Rectangle {
Point *origin;
}
-(Point *) origin {
return origin;
}
-(void) setOrigin: (Point *) pt {
origin = pt;
}
#end
Now I simply instantiate the Rectangle, assign its origin a pointer to object of Point
Rectangle *rectangle2 = [[Rectangle alloc] init];
Point *pt = [[Point alloc] init];
[pt setX:1 andY:2];
rectangle2.origin = pt;
NSLog(#"Point's x= %i, y=%i",pt.x,pt.y); // result: Point's x=1,y=2
NSLog(#"Rectangle's origin x= %i, y=%i",rectangle2.origin.x,rectangle2.origin.y); //Rectangle's origin x=1,y=2
Now if I change the x,y of the object pt(following line of code), the rectangle2 object's origin will change as well, because it does not own its object, instead just points to where pt pointing to
[pt setX:10 andY:12];
NSLog(#"Point's x= %i, y=%i",pt.x,pt.y); // result: Point's x=10,y=12
NSLog(#"Rectangle's origin x= %i, y=%i",rectangle2.origin.x,rectangle2.origin.y); //Rectangle's origin x=10,y=12
This is completely logical, with no issue.
Issue seems to be happening in a similar scenario.
AddressCard class has an object of type NSString,
#implementation AddressCard{
NSString *phone;
}
-(void) setPhone:(NSString *) p {
phone = p;
}
-(NSString *) name {
return name;
}
#end
I instantiate AddressCard, assign an NSString pointer to its phone object.
AddressCard *addressCard1 = [[AddressCard alloc] init];
NSString *phoneObject = #"(111) 111 - 1111";
addressCard1.phone = phoneObject;
NSLog(#"addressCard1 phone: %#",addressCard1.phone); // result: (111) 111 - 1111
NSLog(#"phone object: %#",phoneObject); // result: (111) 111 - 1111
But now if I change phoneObject, addressCard1.phone won't change despite this way I'm setting the phone object in AddressCard class (setPhone method in class implementation)
phoneObject = [NSString stringWithString:#"PHONE IS CHANGED"];
NSLog(#"%#",phoneObject); //result : PHONE IS CHANGED
NSLog(#"Phone after change in the AddressCard is: %#",addressCard1.phone); // result: (111) 111 - 1111
Could some objective-c ninja tells me what's the difference between two snippets, and the reason of this?
In the first case, you modify an object: changing the x, y that are in the point.
In the second case, you create a new string, and point to that with phoneObject. You didn't modify the string itself (and you couldn't anyway - NSString is immutable). That means the original string you gave to the address card still exists and is still valid, so the address card still uses that string. If you want it to use the new one, you need to tell it to, putting:
addressCard1.phone = phoneObject;
again after reassigning phoneObject.
Related
I'm implementing a objective C wrapper for Box2d (which is written in c++). The b2Body keeps a reference to its wrapper B2Body in its userData field. GetUserData returns a void*. I'm now implementing fast iteration for getting the B2Bodies out of the B2World.
I get an 'Assigning to 'id' from incompatible type 'B2Body *' error at the line indicated below. Why?
#import "B2Body.h"
#import "B2World.h"
#import "Box2d.h"
#implementation B2World
-(id) initWithGravity:(struct B2Vec2) g
{
if (self = [super init])
{
b2Vec2 *gPrim = (b2Vec2*)&g;
_world = new b2World(*gPrim);
}
return self;
}
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained [])buffer count:(NSUInteger)len;
{
if(state->state == 0)
{
state->mutationsPtr = (unsigned long *)self;
state->extra[0] = (long) ((b2World*)_world)->GetBodyList();
state->state = 1;
}
// pull the box2d body out of extra[0]
b2Body *b = (b2Body*)state->extra[0];
// if it's nil then we're done enumerating, return 0 to end
if(b == nil)
{
return nil;
}
// otherwise, point itemsPtr at the node's value
state->itemsPtr = ((B2Body*)b->GetUserData()); // ERROR
state->extra[0] = (long)b->GetNext();
// we're returning exactly one item
return 1;
}
`
B2Body.h looks like this:
#import
#interface B2Body : NSObject
{
int f;
}
-(id) init;
#end
NSFastEnumerationState is a C structure, and the itemsPtr field is:
id __unsafe_unretained *itemsPtr;
In earlier versions, the __unsafe_unretained specifier was obviously missing.
Note, that the field itemsPtr is a pointer-to-id. Since id is essentially a pointer, itemsPtr is a pointer to an object pointer. Actually, this field is what holds the array of objects that allows the fast enumeration. Basically, it trolls through this array of object pointers.
Since I know nothing about Box2d, that's about all I can say. Assuming b->GetUserData() returns a pointer to an array of objects, you should be able to do this:
state->itemsPtr = (__unsafe_unretained id *)b->GetUserData();
While a bit dated, Mike Ash's article is still a great source for implementing fast enumeration.
EDIT
Just noticed that you are returning a single object. So, I assume GetUserData just returns a single object pointer. Since you need to return a pointer to object pointers, you would need to do something like this:
id object = (__bridge id)b->GetUserData();
state->itemsPtr = &object;
However, that stack object will be gone once you return from this method, which is why you are passed a stack buffer you can use. Thus, you should probably stuff that single pointer into the provided stack buffer:
*buffer = (__bridge id)b->GetUserData()
state->itemsPtr = buffer;
This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
Scoring System In Cocos2D
I got a reply from a question I asked earlier but I am new to coding and have no idea how to do it. Here is the reply:
"#synthesize a "score" property of type int, and a "scoreLabel" property of type CCLabelTTF.
initialize your score property to "0" in -(void)init
On line 126, increment your "score" property by 1, and set that value into your CCLabelTTF."
Can you tell me how to do this? plz. link to my other post
----- Scoring System In Cocos2D
When you synthesize a private variable (other classes cannot see it) you allow a way for other classes to see and/or modify the value of that variable.
First, you want to create the variable:
NSMutableArray *_targets;
NSMutableArray *_projectiles;
int _score;
CCLabelTTF *_scoreLabel;
Then in your init method to set the _score to 0:
-(id) init
{
if( (self=[super init] )) {
[self schedule:#selector(update:)];
_score = 0;
Then increment (add 1 to) your _score variable and set the string (the text content) of your _scoreLabel to that value.
if (CGRectIntersectsRect(projectileRect, targetRect)) {
[targetsToDelete addObject:target];
_score++;
[_scoreLabel setString:[NSString stringWithFormat:#"%d", _score]];
}
The line [_scoreLabel setString:[NSString stringWithFormat:#"%d", _score]]; is a way to convert the integer of _score to a string (NSString). It's an old C way of doing it, the %d means that whatever is going to be there should be displayed as an integer as opposed to a float (having decimal points).
It also looks like you need to "instantiate" your label and add it as a child to the layer. Instantiation is just a fancy term for creating a instance of something. Think of a "class" as a blueprint for a chair, and an "instance" as a chair created from that blueprint. Once you have the chair created (an instance), you can modify it (paint it, add/remove legs, etc).
So, to instantiate your label and add it to the layer (itself):
-(id) init
{
if( (self=[super init] )) {
[self schedule:#selector(update:)];
_score = 0;
//Create label
_scoreLabel = [CCLabelTTF labelWithString:#"0" fontName:#"Marker Felt" fontSize:16];
//Add it to a layer (itself)
[self addChild:_scoreLabel];
Create a score property in HelloWorldLayer.h after the interface declaration, like
#property (nonatomic, retain) int score;
Then synthesize it in your .m file just after the #implementation HelloWorldLayer line.
Create methods for setting and getting scores:
-(int)getScore {
return self.score;
}
-(void)setScore:(int)newScore {
self.score = newScore;
}
In the init method, set the value of the property to zero,
if( (self=[super init] )) {
//... other stuff
[self setScore:0]
}
You can update the score with the setScore method, but I suggest having another method for this that calls setScore so that you can use it at different places with a single line call, and make any changes like assigning more score in certain situations, like two collisions within half-a-second etc..
-(void)updateScore:(int)increment {
int currentScore = [self getScore];
[self setScore:(currentScore + increment)];
}
Similarly, for label,
#property (nonatomic, retain) CCLabelTTF scoreLabel; // in header
and
#synthesize scoreLabel; // in .m file
Again, in your init method, initialize the label with position, layer and initial text etc. Then you can update that text in the updateScore method.
-(void)updateScore:(int)increment {
int currentScore = [self getScore];
[self setScore:(currentScore + increment)];
[scoreLabel setString:[NSString stringWithFormat:#"Score: %i", [self getScore]]];
}
Make sure you read through the tutorial before going ahead in order to avoid confusion regarding common tasks.
I wanted to create 2 global arrays which can be updated during the run of the programme.In each update i add one element to zeroth position and deleted the last number
I created the arrays as....
In the .h file..........
//////////////
#interface Shared : NSObject{
NSMutableArray *x;
NSMutableArray *y;
}
#property (nonatomic,retain) NSMutableArray *x;
#property (nonatomic,retain) NSMutableArray *y;
+(Shared*)sharedInstance;
#end
In .m file
staticShared* sharedInstance;
#implementation Shared
#synthesize x;
#synthesize y;
+(Shared*)sharedInstance
{
if (!sharedInstance) {
sharedInstance=[[Sharedalloc]init];
}
returnsharedInstance;
}
-(Shared*)init
{
self = [superinit];
if(self)
{
x=[[NSMutableArrayalloc] init];
x=[NSMutableArrayarrayWithObjects:#"0",#"0",#"0",#"0",#"0",#"0",#"0",nil];
y=[[NSMutableArrayalloc] init];
y=[NSMutableArrayarrayWithObjects:#"0",#"0",#"0",#"0",#"0",#"0",nil];
}
returnself;
}
#end
Then i used to call them and re,ove and added elements using the following code....
[[shared sharedInstance].y removeLastObject];
[[shared sharedInstance].y insertObject:new_element atIndex:0];
[[shared sharedInstance].x removeLastObject];
[[shared sharedInstance].x insertObject:new_element atIndex:0];
In the mean time i call these values and calculate an arithmetic value using an expression.
This seems to work well. But it seems to be an inefficient way to handle floating point numbers which i store in it. As these arrays creates objects. Is there any easy method that i can create a global array containing specified amount of floating point numbers and update it during the run of the programm(array size is fixed) by deleting the last object, and call them back to do calculation?
Please help me!
EDIT 1
To sir deanWombourne
.................................
I implement as you instructed! Can you please go through this and help me to correct 2 errors i get.
IN the .h file
#interface Shared : NSObject{
#private
float input[7];
float output[6];
}
+(Shared*)sharedInstance;
-(void)addNewInput:(float)input1;
-(float *)input;
-(void)addNewOutput:(float)output1;
-(float *)output;
#end
in .m file............
#implementation Shared
-(id)init{
if((self =[superinit])){
for(int n=0; n<7 ;++n)
input[n]=0.00f;
for(int n=0; n<6 ;++n)
output[n]=0.00f;
}
returnself;
}
-(void)addNewInput:(float)input1{
input[0]=input[1];
input[1]=input[2];
input[2]=input[3];
input[3]=input[4];
input[4]=input[5];
input[5]=input[6];
input[6]=input1;
}
-(float *)input {
returninput;
}
-(void)addNewOutput:(float)output1{
output[0]=output[1];
output[1]=output[2];
output[2]=output[3];
output[3]=output[4];
output[4]=output[5];
input[5]=output1;
}
-(float *)output {
returnoutput;
}
#end
When calling it
float reading= (accel_reading)/(1.165969038*1e5f);
[[SharedsharedInstance] addNewInput:reading];
Problems i get
1. In the implementation, it says incomplete implementation (it's a warning not an error)
2. How can i used a for loop to fill array values or is this way ok?
Major problem i get,
When i call it as shown above, program stops running telling
Terminating application due to uncaught exception 'NSInvalidArgumentException', reason '+[SharedsharedInstance]: unrecognized selector sent to class 0x5780'
Please help me through this...............
Your code Smells (and I mean that in the nicest possible way!)
Using two parallel arrays and keeping in sync is a bad design pattern (and a performance hit in quite a few ways!). Especially as there is already a struct that handles storing an x and y at the same time - CGPoint).
You're solving the 'only objects go in arrays' problem by converting your float' primitives toNSString` objects, which is horrendously inefficient - take a look instead at the NSValue class, it's designed to put native C primitives into an object without expensive parsing operations :)
You might also want to look into malloc (and free etc) and deal with the whole problem at the C level - this will mean no objects at all and would be blindingly fast (at the cost of more complicated code).
Hope this helps, if you have any questions just add a comment to this answer :)
EDIT
If all you want to do is store 4 x and y values, then this is probably the easiest way to do it :
#interface Shared : NSObject {
#private
CGPoint points[4];
}
+(Shared *)sharedInstance;
- (void)addNewPoint:(CGPoint)point;
- (CGPoint *)points;
#end
#implementation
- (id)init {
if ((self = [super init])) {
// Start with 0,0 for all your points
for (int n = 0; n < 4; ++n)
points[n] = CGPointZero;
}
return self;
}
- (void)addNewPoint:(CGPoint)point {
// Just move all the points along one and add the new one to the end
// (yes, this could be done in a loop but there's not that much point for 4 points!)
points[0] = points[1];
points[1] = points[2];
points[2] = points[3];
points[3] = point;
}
- (CGPoint *)points {
return points;
}
#end
This gives you a method addNewPoint that removes the first point and adds the new point to the end of your array.
You also get the method points that returns the 4 points. Use it something like :
// To add a point
CGPoint newPoint = CGPointMake(100, 100);
[[Shared sharedInstance] addNewPoint:newPoint];
// To do something with the points (in this case, NSLog them)
CGPoint *points = [[Shared sharedInstance] points];
for (int n = 0; n < 4; ++n)
NSLog(#" Point %i : %#", n, NSStringFromCGPoint(points[n]));
EDIT #2
From your comments, you need two arrays, one with input data and one with output data. Try something like this :
#interface Shared : NSObject {
float inputs[4];
float outputs[5];
}
...
This will give you two arrays to read/write to - one called inputs and the other called outputs. Access them in pretty much the same way you did the ones in my first edit :
float *inputs = [[Shared sharedInstance] inputs];
for (int n = 0; n < 4; ++n)
NSLog(#" Input %i : %f", n, inputs[n]);
float *outputs = [[Shared sharedInstance] outputs];
for (int n = 0; n < 5; ++n)
NSLog(#" Output %i : %f", n, output[n]);
Would a linked list be overkill for what you're trying to achieve? It's not quite as simple as a static array of floats, but makes the removal of the last object and insertion of the zeroth object reasonably simple and fast.
If you want an array containing a specific number of Objects, you can use NSArray, which is static, opposed to NSMutableArray.
As for the array being Global, just implement a singleton class that contains the 2 arrays and provides the associated methods.
in Globals.h:
#interface Globals : NSObject
+ (Globals *) sharedGlobals;
#end
in Globals.m:
#implementation Globals
static Globals *sharedGlobals = nil;
+ (Globals *) sharedGlobals{
#synchronized(self){
if (sharedGlobals == nil){
sharedGlobals = [[self alloc] init];
}
}
return sharedGlobals;
}
you then can access the arrays (after you implemented them) with the following line:
[[Globals sharedGlobals] getArrayX];
Here is a sketch to get you going.
Your array size is fixed and only contains floating point numbers, start with a C array:
double x[] = {0, 0, 0, 0, 0, 0, 0};
double y[] = {0, 0, 0, 0, 0, 0};
The number of elements in these arrays can be calculated rather than hard-coded:
int xCount = sizeof(x)/sizeof(double);
int yCount = sizeof(y)/sizeof(double);
Now use these arrays as a circular buffer, declare a cursor and initialise:
int xCursor = 0;
The item at the front of the queue is at the cursor:
valueAtFrontOfQueue = x[xCursor]; // get the current front item
To remove the value at front and add a new one to the rear replace the value at the cursor with the new value and increment the cursor:
x[xCursor] = newValueForBackOfQueue; // replace it with new item for back of queue
xCursor = (xCursor + 1) % xCount; // and advance cursor using mod arithmetic to it cycles around
No wrapping doubles as objects, no dynamic allocation at all.
Wrap the above up as you see fit, maybe as a class, and you're done.
SOLUTION:
Simply separating the method call
Tile *tile = [[Tile alloc] initWithX:x Y:y];
into two separate lines such as:
Tile *tile = [Tile alloc];
[tile initWithX:x Y:y];
caused the method to stop receiving the ints as memory pointers. I'm baffled as to why this occurred but it fixed the problems. Any explanations as to why this happened would be appreciated. Thanks to all for the help!
ORIGINAL POST:
This is a bit of a weird question as I've run into this problem while doing a tutorial at: http://www.iphonegametutorials.com/2010/09/23/cocos2d-game-tutorial-building-a-slide-image-game/.
The program I have typed out works except for the switching of tiles, and I have looked in the XCode debugger and it seems that my x and y values for the Tile class are being set way to absurdly high values. The weird thing is, when the values are passed to the method (initWithX:Y:) from the original method (initWithSize:imgSize:) they are what they should be, but when they arrive at the method they are huge (for example: in one instance an x passed as '1' becomes '1065353216' when it arrives at the method.)
I have a feeling that it may be pointing to the memory location of the passed int, but I don't see any reason why it should be in my code. See below.
-(id)initWithSize:(CGSize)aSize imgSize:(int)aImgValue {
self = [super init];
imgValue = aImgValue;
size = aSize;
OutBorderTile = [[Tile alloc] initWithX:-1 Y:-1];
content = [NSMutableArray arrayWithCapacity:size.height];
readyToRemoveTiles = [NSMutableSet setWithCapacity:50];
for(int y = 0; y < size.height; y++) {
NSMutableArray *rowContent = [NSMutableArray arrayWithCapacity:size.width];
for (int x = 0; x < size.width; x++) {
Tile *tile = [[Tile alloc] initWithX:x Y:y];
[rowContent addObject:tile];
[readyToRemoveTiles addObject:tile];
[tile release];
}
[content addObject:rowContent];
[content retain];
}
[readyToRemoveTiles retain];
return self;
}
-(id)initWithX:(int)posX Y:(int)posY {
self = [super init];
x = posX;
y = posY;
return self;
}
Everything should be the same as in the linked tutorial, but I can't see anybody else complaining of the same problem.
I have also noted that if I go into the debugger and manually change the posX and posY values in the initWithX method that the tile switching code works, so my problem definitely lies with the really large int values forming in that method.
Edit for Jim: Hopefully this shows a more complete picture of the layout I have for the Tile object.
#interface Tile : NSObject {
int x, y, value;
CCSprite *sprite;
}
#property(nonatomic, readonly) int x, y;
#property(nonatomic) int value;
#property(nonatomic, retain) CCSprite *sprite;
#end
#implementation Tile
#synthesize x, y, value, sprite;
// Code is logged in below.
-(id)initWithX:(int)posX Y:(int)posY {
self = [super init];
x = posX;
y = posY;
return self;
}
#end
Somewhere, the x and/or y value are being assigned to a pointer. One thing I would check is to make sure you aren't declaring your ints as pointers to ints. I.e. int *x, etc.
The common Superclass of Rectangle and Circle is Shape.
If I initialize some shapes, what is a good way of converting the shape into a circle later and keeping the same properties set while it was a shape? Should I implement a initWithShape in the subclasses that looks something like this?
- (id) initWithShape:(Shape*)aShape {
self = (id) aShape;
// set circle or rectangle specific values
return self;
}
Does anyone have an example that I can look at?
Don't do what you just did. Think about what happens when you do this:
Shape *shape = ...;
Rectangle *rect = [[Rectangle alloc] initWithShape:shape];
In the second line, an instance of Rectangle gets allocated. Then, the return value for initWithShape is just shape again, so the new Rectangle that we just allocated has been leaked!
The cast to id is also unnecessary—any Objective-C object can be implicitly cast to id.
I'm not entirely clear on what you're trying to do. Perhaps if you clarified your question, I could tell you what you should be doing.
You cannot change an object after it has been created, except by freeing it and creating a new one (which you can do in an init method, and is in fact quite often done for singletons or class clusters), but that is not really what you're after.
Give an existing Shape object, with some properties, your only real option is to create a new object based on the shape properties. Something like:
In Shape.m:
- (id) initWithShape:(Shape*)aShape {
self = [super init];
if (self != nil) {
// copy general properties
_x = aShape.x;
_y = aShape.y;
_color = [aShape.color copy];
}
return self;
}
In Circle.m:
- (id) initWithShape:(Shape*)aShape {
self = [super initWithShale:aShape];
if (self != nil) {
// base properties on the class of the shape
if ( [aShape isKindOfClass:[Oval class]] ) {
// average the short and long diameter to a radius
_radius = ([(Oval*)aShape shortDiameter] + [(Oval*)aShape longDiameter])/4;
} else {
// or use generic Shape methods
_radius = aShape.size / 2;
}
}
return self;
}
If you have a reference to a Shape, and it might be a Rectangle or Pentagram or whatever, and you want to 'convert' to a circle (I guess you mean a circle with the same bounding box?), you have to create a new object. You can't change the class of an object after it's been created (except through very nasty low-level hacks.)
So yes, you would create an -initWithShape: method in class Circle. But the method would look like a normal init method, setting up the instance variables of the new Circle object ('self'). It would access properties of the given Shape, like its position and size, and set up the new object accordingly.
Why not implement a method in your shapes to take properties from other shapes rather than trying to replace the instance of the object altogether. It's probably safer.
// for rectangle
- (void) takePropertiesFrom:(Shape *) aShape
{
if ([aShape isKindOfClass:[Circle class]])
{
float radius = [aShape radius];
[self setWidth:radius * 2];
[self setHeight:radius * 2];
}
else
{
[self setWidth:[aShape width]];
[self setHeight:[aShape height]];
}
}
// for circle
- (void) takePropertiesFrom:(Shape *) aShape
{
if ([aShape isKindOfClass:[Rectangle class]])
[self setRadius:[aShape width] / 2];
else
[self setRadius:[aShape radius]];
}
Obviously you would want to set up a public interface for Shape that exposes the basic properties of a shape, such as height and width, and then you won't need to hard-code property stealing based on class type.