I'm trying to add sprites to a NSMutableArray but it's not adding them. This is what I have:
NSMutableArray *tail;
CCSprite *block;
int j;
-(void)handleTail:(CCSprite*)pos{
CGPoint point= pos.position;
block = [CCSprite spriteWithFile:#"Icon-Small-50.png"];
//Adding the tail blocks
block.scale = .8;
block.color = ccGREEN;
block.position = point;
NSLog(#"Block Pos (%f,%f)",block.position.x,block.position.y);
//CGPoint playerPos = piece.position;
originalPos = point;
if ([tail count] < maxLength) {
[tileMap addChild:block];
[tail addObject:block];
NSLog(#"Tail length:%i",tail.count);
j+=1;
}
if (j == 3) {
NSLog(#"J called");
[tail removeObjectAtIndex:0];
}
}
I don't understand why this isn't working?
You have not alloc+inited the tail.
In awakeFromNib or init or viewDidLoad ( which ever is applicable for your class) use
tail=[[NSMutableArray alloc] init];
Suggestion NOTE : Try to follow naming convention.
As tail is an array (plural) you should use tails.
You forget to alloc init NSMuttableArray
tail = [[NSMuttableArray alloc]init];
without alloc and init your array you cannot add object to it
when you try to access its member it returns nil
try NSLog (#"%#",tail); it returns
You need to instantiate your NSMutableArray.
tail = [NSMutableArray array];
You need to allocate and initialize the array.
Something like:
tail = [NSMutableArray array];
Related
Hello i have got a question. I hope anybody could help me. I do not understand… I have a method returnPointFromArray where i have an array with values (e.g. 50.0, 100.0). Than i want to random it and then i want to use the CGPoint p in the Method drawObjectWithPoint to position my Object (object from another class) with the random value.
But the drawObjectWithPoint Method says always that the CGPoint p ist 0.0, 0,0 or he says "Use of undeclared identifier p or "instance variable hides…" .
I tried the same principle to test with int and this works.
I don´t know what i´m doing wrong.
It would be great if anybody could help me and explain what i´m doing wrong.
Thanks a lot.
.h
-(void)returnPointFromArray;
-(void)drawObjectWithPoint;
.m
-(void)returnPointFromArray
{
NSArray *points = [];
//Random for points
NSUInteger *randomIndex = arc4random() % [points count];
NSValue *val = [points objectAtIndex:randomIndex];
CGPoint p = [val CGPointValue];
}
-(void)drawObjectWithPoint
{
Object *myObject [[Object alloc]init];
CGPoint pNew = p;
myObject.position =
[self addChild:myObject];
}
You can do this: Edited: (This is how yo declare in .h file)
in .h file
#import <UIKit/UIKit.h>
#interface MyClass : UIViewController {
CGPoint p;
}
-(void)returnPointFromArray;
-(void)drawObjectWithPoint;
#end
in .m file
-(void)returnPointFromArray {
NSArray *points = [];
//Random for points
NSUInteger *randomIndex = arc4random() % [points count];
NSValue *val = [points objectAtIndex:randomIndex];
p = [val CGPointValue]; // change here
}
-(void)drawObjectWithPoint {
Object *myObject [[Object alloc]init];
CGPoint pNew = p;
myObject.position =
[self addChild:myObject];
}
assign value directly by method call
-(void)drawObjectWithPoint
{
Object *myObject [[Object alloc]init];
myObject.position = [self returnPointFromArray];
[self addChild:myObject];
}
-(CGPoint)returnPointFromArray {
NSArray *points = [];
//Random for points
NSUInteger *randomIndex = arc4random() % [points count];
NSValue *val = [points objectAtIndex:randomIndex];
return [val CGPointValue];
}
Just declared CGPoint p; in .h file. You declared it as locally(returnPointFromArray function) means its scope is local for that function only. check here
for reference.
Your returnPointFromArray method returns void - just modify it -
-(CGPoint)returnPointFromArray
{
// your code
return p;
}
Then where you want to use p, just write
CGPoint pNew = [self returnPointFromArray];
obviously you'll have to add code which actually uses this value - your code doesn't do this at all -
myObject.position = pNew;
I want to use the variable of a for loop in a property name, example:
#interface
{
Player *rival1, *rival2, *rival3;
}
int number;
for (number=1; number<=3; number++){
rival+number = [[Player alloc] init]; //The compiler accepts this.
rival+number.name = #"";
//^This line gives the error: "use of undeclared identifier 'rival'"
//For the first loop, I want it work like: rival1.name = #"";
}
This isn't a particularly clean way of approaching this, but to answer the question as you've posed it:
- (void)generateRivals
{
for (int i = 1; i < 4; i++)
{
NSString *propertySetString = [NSString stringWithFormat:#"setRival%d", i];
Rival *rival = [[Rival alloc] init];
[self performSelector:#selector(NSSelectorFromString(propertySetString)) withObject:rival];
}
}
We create a selector (I haven't tested this, but it should work in theory) that corresponds to the getter for the property instance represented by i.
Based on comments you've added, it seems like what you really want is a variable number of players (perhaps not exceeding a certain number) with the ability to reference them individually.
Architecturally, rather than create properties pointing to each respective rival, put them all in an array (and keep it as a property on your class). So you'd create them like this:
- (void)generateRivals:(NSUInteger)numberOfRivals
{
NSMutableArray *rivalsArray = [[NSMutableArray alloc] initWithCapacity:numberOfRivals];
for (int i = 0; i < numberOfRivals; i++)
{
Rival *rival = [[Rival alloc] init];
[rivalsArray insertObject:rival atIndex:i];
}
[self setRivalsArray:rivals];
}
Then, when you need to access a particular rival, call a method like this, which will return the rival at the index number you pass:
- (Rival *)rivalWithNumber:(NSUInteger)number
{
return [[self rivalsArray] objectAtIndex:number];
}
you cant create the properties variable names on the fly and use it, as the names get lost during compilation
you can put them in an array and enumerate over it
Player rival1 = [[Player alloc] init];
Player rival2 = [[Player alloc] init];
Player rival3 = [[Player alloc] init];
for (Player *p in #[rival1, rival2, rival3]) {
p.name = #"";
}
probably the best way would be to maintain the rivales in the array from the beginning
#interface …
#property(strong) NSArray *rivals;
#end
#implementation …
//in some useful method, like initializer
_rivals = [NSMutableArray array]
for (int i = 0; i < 3 ++i){
//create player and add to array
}
#end
You might want to look into Key Value Coding.
You need to create properties for your rivals
#interface …
#property(strong) Player *rival1;
#property(strong) Player *rival2;
#property(strong) Player *rival3;
#end
#implementation …
-(id)init…
{
if(self = [super init…]){
for (int i = 1; i < 4; ++i) {
Player *rival = [[Player alloc] init];
rival.name = [NSString stringWithFormat:#"rival%d", i];
[self setValue: rival forKey:[NSString stringWithFormat:#"rival%d", i]];
}
}
return self;
}
#end
Instead of running a for loop to set the names of the players to #"" just add the line
name = #"" in your init method of your Player class. It's faster and more practical than to write a for loop. You can also create a method like
-(id)initWithName:(NSString *)initName{
self = [super init];
if (self){
name = initNane;
}
return self;
}
to set the name of your player at the moment it's created. Although if you want absolutely a for loop, you can use the method posted by vikingosegundo previously.
Although experienced with OOP, I am an absolute newbie with Objective-C. I have the following code:
// header files have been imported before this statement...
CCSprite *treeObstacle;
NSMutableArray *treeObstacles;
#implementation HelloWorldLayer {
}
-(id) init
{
// create and initialize our seeker sprite, and add it to this layer
treeObstacles = [NSMutableArray arrayWithObjects: nil];
for (int i=0; i<5; i++) {
treeObstacle = [CCSprite spriteWithFile: #"Icon.png"];
treeObstacle.position = ccp( 450-i*20, 100+i*20 );
[self addChild:treeObstacle];
[treeObstacles addObject: treeObstacle];
}
NSLog (#"Number of elements in array = %i", [treeObstacles count]);
return self;
}
- (void) mymethod:(int)i {
NSLog (#"Number of elements in array = %i", [treeObstacles count]);
}
#end
The first NSLog() statement returns "Number of elements in array = 5". The problem is that (although treeObstacles is a file-scope variable) when calling the method "mymethod", I'll get an EXC_BAD_ACCESS exception.
Can anybody please help me?
Thanks a lot
Christian
you created treeObstacles by
treeObstacles = [NSMutableArray arrayWithObjects: nil];
which will return an autoreleased object, and you didn't retain it so it will be released soon
you have to retain it by calling retain on it
[treeObstacles retain];
of simple create it by
treeObstacles = [[NSMutableArray alloc] init];
and you need to remember to release it when done like
- (void)dealloc {
[treeObstacles release];
[super dealloc];
}
you need to read more about management in Objective-C
https://developer.apple.com/library/mac/#documentation/General/Conceptual/DevPedia-CocoaCore/MemoryManagement.html
or use ARC so no need to worry retain/release anymore
http://developer.apple.com/library/ios/#releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html
another problem, you need to call [super init] in your init method
- (id)init {
self = [super init];
if (self) {
// your initialize code
}
}
otherwise your object won't initialize properly
I have a UIViewController that has a UIPickerView and UITableView.
The UIPicker has 3 components.
When I try to define the number of components in each array by returning [anArray count], the program freezes without throwing an error when loading the UIViewController.
When I put NSLogs in viewDidLoad: for [self.hours count], [self.minutes count], and [self.meridiem count], the correct number of values are returned.
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableArray *tempHours = [NSMutableArray array];
for (NSInteger i = 0; i < 12; i++) [tempHours addObject:[NSNumber numberWithInteger:(i+1)]];
self.hours = [NSArray array];
hours = tempHours;
[tempHours release];
NSMutableArray *tempMinutes = [NSMutableArray array];
for (NSInteger i = 0; i <= 59; i++) [tempMinutes addObject:[NSNumber numberWithInteger:i]];
self.minutes = [NSArray array];
minutes = tempMinutes;
[tempMinutes release];
NSMutableArray *tempMeridiem = [NSMutableArray array];
for (NSInteger i = 0; i <= 1; i++) [tempMeridiem addObject:[NSNumber numberWithInteger:i]];
self.meridiem = [NSArray array];
meridiem = tempMeridiem;
[tempMeridiem release];
}
Now, in pickerView:numberOfRowsInComponent:Component:, the code freezes without an error if I try to output [anArray count](where anArray is a placeholder for hours, minutes, and meridiem). If I set numberOfRows to an integer value, or to component+1 everything works just fine.
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
NSInteger numberOfRows;
if (component == 0) {
// numberOfRows = component+1;
numberOfRows = [self.hours count];
}
else if(component == 1) {
// numberOfRows = component+1;
numberOfRows = [self.minutes count];
}
else {
// numberOfRows = component+1;
numberOfRows = [self.meridiem count];
}
return numberOfRows;
}
I have a feeling my NSArrays (hours, minutes, meridiem) aren't being retained, but running the Analyzer doesn't show me that I have any memory leaks in that ViewController.
Any and all help would be greatly appreciated!
You are creating unnecessary arrays and not retaining the ones that count. See your code:
self.hours = [NSArray array];
hours = tempHours;
[tempHours release];
Here you created an autoreleased array and set it to your self.hours property (which hopefully has a retain keyword attached to it). But then the very next line you access the ivar directly and set it to tempHours. Then you send a release message to tempHours without having ever retained it. Since tempHours is autoreleased it will magically disappear on the next drain of the auto release pool. I believe what you wanted to do is:
self.hours = tempHours;
Ditto for your other array collections.
These lines are wrong:
self.hours = [NSArray array];
hours = tempHours;
you should just use
self.hours = tempHours;
What was happening in your original code was:
self.hours is set to an array created by [NSArray array]. Call it a. a is correctly retained.
now, hours is set to tempHours. This directly accesses the ivar, without using the setter. So, it doesn't retain tempHours. a is lost without being correctly released, too.
tempHours is released. It's not retained by anybody, so it's dealloc'ed, too.
So, don't do that. You'd like to read the memory management guide. I can't believe the Analyzer (below the Build command) didn't catch this mistake... maybe you'd like to file a bug toward the LLVM team about this case.
The problem was not in the ViewController I was trying to display. I just copied the interface and implementation files into a new proejct, and once I compiled it, everything worked like a dream.
The problem is with the RootViewController of my project that modally invokes the controller with the UIPickerView. Apparently, I have a memory leak in there. Should be trivial to fix.
Thanks everyone for your help!
How can I reload the data in the TTTableViewController? I tried to
call reloadData but it doesn't display the new data.
I have created the datasource file, which is a subclass of
TTListDataSource. Basically, the init function looks like this.
- (id) initWithObject:(NSArray *)objects {
if (self = [super init]) {
NSMutableArray *priceItems = [[[NSMutableArray alloc] init] autorelease];
for (int i = 0; i < [objects count]; i++) {
[priceItems addObject:
[PriceItem itemWithName:
[[objects objectAtIndex:i] objectAtIndex:0]
lastDone:[[objects objectAtIndex:i] objectAtIndex:1] change:[[objects objectAtIndex:i] objectAtIndex:2]]];
}
self.items = [NSArray arrayWithArray:priceItems];
}
return self; }
After the view is loaded, i start to streaming some data, thus the
objects passed to initWithObject is changed, so I call 'reload' in the
subclass of the TTTableViewController in order to update the data, but
the data is not updated. It doesn't work for refresh method too. Do I need to implement any other method in
the subclass of TTListDataSource?
Try
[self invalidateModel];
That should do the trick.
invalidateModel will rebuild the model, that's not efficient.
Try [TTTableViewController refresh], your table will be reloaded.