Xcode Passing NSMutableArray from one class to another - objective-c

I know this question has been asked countless of times. But none of them seems to be working! Please help! I am unable to pass arrays from one class to another. Blow are my codes
firstClass has an array declared
#FirstClass.h
- (void)viewDidLoad
{
testArray = [[NSMutableArray alloc] init];
[testArray addObject:#"test"];
[testArray addObject:#"test2"];
[testArray addObject:#"test3"];
//method to pass array
TableViewController *tvc = [[TableViewController alloc] init];
tvc.getData = [[NSMutableArray alloc] init];
tvc.getData = testArray;
NSLog(#"%i", [tvc.getData count]); // returns a value n
Here i get count of 3
}
TableViewController.h
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"%i", [getData count]); // returns a value n
Here i get 0
//Set the title
self.navigationItem.title = #"Countries";
}
I tried
FirstClass *fc = [[FirstClass alloc] init];
getData = fc.testArray;
Doesn't work as well. I tried writing a method and calling the method from my second class.
Ended up with #interface error.
Sorry, this is my first time on objective c. Am java educated.
Please help!

For the second problem, why the fc.testArray does not work is that viewDidLoad is called only after the view property is called. (lazy initialization)
So it works if you do like
FirstClass *fc = [[FirstClass alloc] init];
fc.view;
getData = fc.testArray;

Related

Use the variable of a for loop in a property name

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.

How to get an NSArray that is returned from a getter method?

Note: This question has been updated with suggestions supplied in answers below in which to bring a fuller context to the present state of the problem.
You may view complete project files here: https://github.com/cxx6xxc/Skeleton/blob/master/README.md
Conditions
I create an NSArray in an object's init method.
I return the NSArray with it's get method.
Problem
Upon arrival, the NSArray is null.
Creating instance
Attempt 1:
This is my original implementation.
- (id)init:
{
labels = [NSArray arrayWithObjects:#"Red", #"Green", #"Blue", nil];
return self;
}
Attempt 2:
Ismael suggested I wrap it with a sub-classing protocol.
neo suggested I retain the NSArray.
- (id)init:
{
self = [super init];
if (self)
{
labels = [NSArray arrayWithObjects:#"Red", #"Green", #"Blue", nil];
[labels retain];
}
return self;
}
Attempt 3:
Anoop Vaidya suggested I force ownership with alloc and NSMutableArray:
- (id)init:
{
self = [super init];
if (self)
{
labels = [[NSMutableArray alloc] initWithObjects:#"Red", #"Green", #"Blue", nil];
}
return self;
}
But, when I return the object, despite the different init suggestions cited above...
Returning the object
- (NSArray *)getLabels
{
return labels;
}
...with NSMutableArray...
- (NSMutableArray *)getLabels
{
return labels;
}
... the NSArray getter returns a null object.
Calling the method
int main(void)
{
id view;
view = [ZZView alloc];
id model;
model = [ZZModel alloc];
id controller;
controller = [[ZZController alloc] init: model: view];
labels = [[controller getModel] getLabels];
if(labels)
NSLog(#"allocated");
else
NSLog(#"not alloced");
[view dealloc];
[model dealloc];
[controller dealloc];
return EXIT_SUCCESS;
}
Question
What am I not doing, missing or what am I doing wrong that causes the null return value?
init methods need to call some [super init], so you will need to do something like this:
- (id)init
{
self = [super init];
if (self) {
labels = [NSArray arrayWithObjects:#"Red", #"Green", #"Blue", nil];
}
return self;
}
Edit: looking at your git repo, I found
controller = [[ZZController alloc] init: model: view];
I'm not entirely sure how the compiler interprets the empty arguments, but my guess is that it reads them as nil, and therefore your ZZController doesn't have model
Also, you have some messy argument order, the first argument (with text init:) is your model, and your second argument (with text model:) is your view
(this according to your - (id)init: (ZZModel*)Model: (ZZView*)View
In order to make it work quickly, you should do
controller = [[ZZController alloc] init:model model:view];
I'm gonna take a (short) leap here and guess you are new to iOS development, so I'll recommend that you read about objc programming, how to write functions, how to send multiple parameters, so on and so forth, and after that, do some refactoring
Cheers!
I suggest you put a breakpoint in both your init and getLabels methods, and check the value of the instance variable that stores the array: you'll see which method does not behave as expected.
Assume you are not using ARC nor synthesising variable labels, you need to retain the array,
- (id)init:
{
labels = [NSArray arrayWithObjects:#"Red", #"Green", #"Blue", nil];
[labels retain];
return self;
}
Also, you need to release it when not using the array to prevent memory leakage.
You can do it in this way, hoping in .h you have NSMutableArray *labels; :
- (id)init{
if (self = [super init]) {
labels = [[NSMutableArray alloc] initWithObjects:#"Red", #"Green", #"Blue", nil];
}
return self;
}
The init method to model was never called, it is only allocated. Therefore, NSArrray labels doesn't exist, because it is created in the init method.

Objective-c Array not deallocating objects when removing

// AClass.m
// init
enemyBullets = [[NSMutableArray alloc] initWithCapacity:0];
enemy1 = [[Enemy alloc] initWithBullets:enemyBullets];
// At some point
NSMutableArray *bulletsToDelete = [NSMutableArray array];
for(BulletEnemy *thisBullet in enemyBullets)
{
// If I have to delete
[bulletsToDelete addObject: thisBullet];
}
[enemyBullets removeObjectsInArray:bulletsToDelete];
//dealloc method
[enemyBullets release];
[enemy1 release];
Now Inside Enemy some point in time I do the following:
// Enemy.m
- (id)initWithBullets:(NSMutableArray*) _bullets{
// Enemybullets is a var of Enemy
enemyBullets = _bullets;
}
// At some point...
myBullet = [[BulletEnemy alloc] init];
[enemyBullets addObject:myBullet];
[myBullet release];
The problem is when I do the following at Aclass:
[enemyBullets removeObjectsInArray:bulletsToDelete];
The dealloc method inside BulletEnemy doesn't get called because the retain count isn't 0. Why? But If I release ACLass (which releases enemyBullets) then My bullets get deallocated.
Make Enemy own the enemyBullets. And use reverseObjectEnumerator
// AClass.m
// init
enemy1 = [[Enemy alloc] init];
// At some point
for(BulletEnemy *thisBullet in enemy1.enemyBullets.reverseObjectEnumerator)
{
// If I have to delete
[enemy1.enemyBullets removeObject:thisBullet];
}
//dealloc method
[enemy1 release];
//Enemy.h
#property (retain) NSMutableArray* enemyBullets;
// Enemy.m
#synthesize enemyBullets = _enemyBullets;
- (id)init{
// Enemybullets is a var of Enemy
_enemyBullets = [[NSMutableArray alloc] init];
}
// At some point...
myBullet = [[BulletEnemy alloc] init];
[enemyBullets addObject:myBullet];
[myBullet release];
-(void) dealloc{
[_enemyBullets release];
}
Aparently, I was asigning something with retain propery, which made the object +1 thus not to deallocate. Just changed the propery to assign and it worked.

Objective-C Array of Objects

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

Get & Edit NSMutableArray from different class file

I am trying to access and change a array from a different class file. When using a NSLog, I get a result of (null). Below is my code:
RootViewController.h
NSMutableArray *listOfItems;
#property (nonatomic, retain) NSMutableArray *listOfItems;
RootViewController.m
#synthesize listOfItems;
listOfItems = [[NSMutableArray alloc] init];
[listOfItems addObject:#"One"];
[listOfItems addObject:#"Two"];
[listOfItems addObject:#"Three"];
SecondViewController.m
RootViewController *test = [[RootViewController alloc] init];
NSLog(#"Results: %#", test.listOfItems);
I get the following results in my console: Results: (null)
Thanks in advance,
Coulton
P.S. Obviously I have left out a bunch of code. I just tried to make it easier to read. If you need to see anything else, I would be more than happy to post more. Just ask
EDIT #1:
I am getting hundreds of NSLog Messages that look something like this:
*** __NSAutoreleaseNoPool(): Object 0x4e39020 of class __NSArrayI autoreleased with no pool in place - just leaking
And here's my init code:
- (id) init {
//NSLog(#"%#", theUserID);
// Set up database connection
NSString *myDB = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"database.db"];
database = [[Sqlite alloc] init];
[database open:myDB];
//Initialize the array.
listOfItems = [[NSMutableArray alloc] init];
// Add to array to display in the tableView
NSArray *listOfItemsTwo = [database executeQuery:#"SELECT * FROM albums"];
for (NSDictionary *rowone in listOfItemsTwo) {
NSString *getName = [rowone valueForKey:#"name"];
if (getName != NULL) {
[listOfItems addObject:getName];
[getName release];
}
}
return self;
}
I guess you reversed RootViewController.m and RootViewController.h snippets right?
Are you sure that the
listOfItems = [[NSMutableArray alloc] init];
gets called? Maybe you can put a breakpoint there.
EDIT: Order of RootViewController.m and RootViewController.h has been fixed in the question. It's not clear from the question where the above line is in the code. That's a important piece of information.
EDIT2: Example of init method.
#implementation RootViewController
- (id) init
{
listOfItems = [[NSMutableArray alloc] init];
[listOfItems addObject:#"One"];
return self;
}
#end