Objective-C: instance variable as a parameter, but after method call instance variable is empty - iphone

I do some iOS programming stuff and I have a UIViewController with a NSMutableArray:
#property (nonatomic, retain) NSMutableArray* mutableTestArray;
...
#synthesize mutableTestArray;
In viewDidLoad I want to call a method which is inside the implementation of this UIViewController:
//- (void)aTestMethod:(NSMutableArray *)myMutableTestArray;
[self aTestMethod:self.mutableTestArray];
So I call the method with a NSMutableArray which is an instance variable of the UIViewController. Inside this method, I do this:
myMutableTestArray = [NSMutableArray arrayWithCapacity:100];
//... looping & generating some objects and adding them to the array:
[myMutableTestArray adObject:myObject];
Now, I debug it and inside the method myMutableTestArray is fine. There are all objects inside the array. But leaving this method the instance variable mutableTestArray is empty.
So, where is the problem? Anyone an idea?
Note: I know I can access the instance variable with self.mutableTestArray and then everything will be okay, instead using it as a parameter, but I want to know what's wrong with my code.
Thank you in advance & Best Regards.

Parameters are passed by value in Objective-C. Thus, you are creating a copy of a pointer to the object and passing that into the method. When you do myParam = ... new object ...; that resets the copy to point to a new location, but has no effect on the original copy in the caller.
(To reiterate -- you are copying the pointer, not copying the object.)
To solve, declare your test method as returning an object:
- (NSMutableArray *)aTestMethod;
Then, you can simply:
self.mutableTestArray = [whateverObject aTestMethod];
(Since you aren't actually using the value passed in in the first place, there is no need for a parameter at all).

By your command
myMutableTestArray = [NSMutableArray arrayWithCapacity:100];
you are creating new allocation of mutableTestArray. So passing mutableTestArray as parameter to aTestMethod:
[self aTestMethod:self.mutableTestArray];
is useless because you override its value immediately when create this array inside your aTestMethod.
Try to create your array before passing it to your method (where you will fill it with data).

Remember that parameters are transmitted by value, so in aTestMethod: you are modifying not the original pointer but a copy of it!
For this to work, you should pass the address of the pointer as in
self aTestMethod:&(self.mutableArray)
then the prototype of the method should be
-(void)aTestMethod:(NSMutableArray **)myArray
and in the code of it you should used *myArray as in
*myArray = [[NSMutableArray alloc] init];
Yours, JB

Related

passing values to another method

I am setting the value for the string in the viewdidload method and getting the string value in the button action method the app gets crashed. can I know the reason for crashing and how to pass the values to the method.
in .h file
NSString *test;
in .m file
-(void)viewDidLoad
{
test = [NSString stringWithFormat:#"sample"];
}
-(IBAction) buttonPressed:(id)sender
{
NSLog(#"%#", test);
}
When I pressed the button the app crashes.
Please try using this solution, I think this will help you,
Create Property of test in .h file like this,,
#property(nonatomic,retain) NSString *test;
and synthesize it in .m file like this,
#synthesize test;
now use test as self.test in .m file like this,
-(void)viewDidLoad
{
self.test = [NSString stringWithFormat:#"sample"];
}
-(IBAction) buttonPressed:(id)sender
{
NSLog(#"%#", self.test);
}
Another solution for this is just retain that test string in ViewDidLoad also, I think this will also help you..
Hope this will help you..
Let me try to explain it in more detail:
You have a string variable in .h file. In view did load you are assigning it as:
test = [NSString stringWithFormat:#"sample"];
What actually happning in this code is your test is a autoreleased object. When you use this and object without alloc and init this is autoreleased object and will release memory after the method you occupied it.
For avoiding this situation you can use #Mehul's solution by creating property. This is against encapsulation concept. Sometimes you have objects you don't want to access outside of the class or don't want to show with objects. Use following in those conditions:
test = [[NSString stringWithFormat:#"sample"] retain]; // or
test = [[NSString alloc] initWithFormat:#"sample"];
this will keep your string alive till you release it.
There is another way that is not good to use but want to tell you so you can understand it better. Using
test = #"sample";
If you don't want to append string or use it with format you can assign simple string to you NSString object.
using this will have a infinite retainCount of your test variable. You can use this to avoid crash but this is not preferable because as I told this have a infinite retaiCount you can't release it and free your memory after use. So earlier methods are more correct.
This is true with all of your autorelease objects which are created with class methods and not with init.
Hope this will clear you more.
I think simple allocation will solve the problem. Just replace this code in the viewDidLoad method
-(void)viewDidLoad {
test=[[NSString alloc]initWithString:#"Sample"];
}

Why can I not initialise my variable without using self

I have the following variable defined:
#property (nonatomic, retain) NSMutableArray *arraySpeechSentences;
And I am trying to initialise it in the following way:
// Set the array of sentences to the stored array
NSMutableArray *speechSentences = [[NSMutableArray alloc] initWithArray:[tempDict objectForKey:key]];
arraySpeechSentences = speechSentences;
[speechSentences release];
When I try to call [arraySpeechSentences count] the application crashes. However, if I set the variable in the following way:
// Set the array of sentences to the stored array
NSMutableArray *speechSentences = [[NSMutableArray alloc] initWithArray:[tempDict objectForKey:key]];
self.arraySpeechSentences = speechSentences;
[speechSentences release];
I can call [arraySpeechSentences count] perfectly fine. I was under the impression that if you use self. it simply checks to see if variable is already set, and if so it will release the object before assigning it the new value. Have I got this wrong, and if so when should I be using self. to set values?
Thanks for any help,
Elliott
Using a setter (like self.foo = ... or [self setFoo:...]) does release the old value but it also retains the new value, which is needed in the example you give.
The issue is that you're alloc and init'ing your array, and then releasing it. This indicates you no longer need it. So, you should either use the setter (usually preferable) or don't release your array.
If you're not using ARC, you should type
arraySpeechSentences = [speechSentences retain];
because you're accessing the instance variable directly, which means the value of the instance variable arraySpeechSentences will be the address of the speechSentence object, which you just released, so which is an invalid pointer. The semantic you declared in the property doesn't have an effect on the instance variable itself.
When you type self.arraySpeechSentences, you're actually using a shortcut for the setter [self setArraySpeechSentences:speechSentences], which actually retains the value passed as parameter (if you synthesized the property, it is retained because you specified retain in the property declaration; if you wrote the accessor yourself, it is your job to ensure you retained the value).
I'll try to give a detail answer for this.
First when you use #property/#synthesize directive you create getter and setter methods around a variable.
In your case, the variable is called arraySpeechSentences (the compiler will create the variable for you) and you can access these methods (setters and getters) with self..
self.arraySpeechSentences = // something
is the same as
[self setArraySpeechSentences:something]; // setter
And
NSMutableArray* something = self.arraySpeechSentences;
is equal to
NSMutableArray* something = [self arraySpeechSentences]; // getter
In the first snippet of code
NSMutableArray *speechSentences = [[NSMutableArray alloc] initWithArray:[tempDict objectForKey:key]];
arraySpeechSentences = speechSentences;
arraySpeechSentences points to the same object speechSentences points to. But when you do [speechSentences release] you dealloc that object and now arraySpeechSentences is a dangling pointer. You receive a message sent to a deallocated instance I suppose. Try to enable Zombie to see it.
Speaking in terms of retain count, the array has a retain count of 1 when you do alloc-init.
But when you release it, the retain count goes to zero, the object doesn't exist anymore and you have a crash when you try to access arraySpeechSentences.
Instead, when you deal with properties, the policy applied to a variable is important. Since the property use a retain policy, when you set an object
self.arraySpeechSentences = // something
the retain count for the referenced object is increased. Under the hood, saying self.arraySpeechSentences = // something is equal to call the setter like
- (void)setArraySpeechSentences:(NSMutableArray*)newValue
{
// pseudo code here...
if(newValue != arraySpeechSentences) {
[arraySpeechSentences release];
arraySpeechSentences = [newValue retain];
}
}
The second snippet work since the retain count for your object is one when you do alloc-init, becomes two when you call self.arraySpeechSentences = and returns to one when you do the release. This time, the object is maintained alive since it has a retain count of 1.
If you have a property with a retain or copy policy, don't forget to release the object in dealloc like, otherwise you can have leaks.
- (void)dealloc
{
[arraySpeechSentences release];
[super dealloc];
}
To understand how Memory works I suggest to read MemoryManagement Apple doc.
P.S. Starting from iOS 5 there is a new compiler feature, called ARC (Automatic Reference Counting), that allows you to forget about retain/release calls. In addition, since it forces you to think in terms of object graphs, I suggest you to take a look into.
Hope that helps.

Where should I alloc/init my ivar?

If I know that I'm going to use the ivar should I alloc/init it in viewDidLoad like:
if (allPeople_ == nil)
self.allPeople = [NSArray arrayWithArray:[[selectedObject people] allObjects]];
or should I create a getter method and alloc/init in there:
- (Group *)allPeople {
if (allPeople_ != nil)
return allPeople_;
allPeople_ = [NSArray arrayWithArray:[[selectedObject people] allObjects]];
return allPeople_;
}
I'm assuming the getter method, with the if-statement, is for lazy-loading, which in my case I wouldn't need to do because I'm definitely using self.allPeople throughout my code.
Extra Question:
If I use the getter method do I actually have to do it this way?
allPeople_ = [[NSArray arrayWithArray:[[selectedObject people] allObjects]] retain];
I would initialize it whenever you are going to use it.
As for the second question, it depends on how your property is declared if it is declared as retain, and you set it like this:
self.allPeople =
you will not have to send it a retain message, because the synthetized setter will take care of that for you.
Do notice self.allPeople is different than just allPeople, if you don't use self you are not accessing it thru the setter, you are accesing the ivar directly and therefore it won't receieve a retain message.
You might try to make your NSArray an NSMutableArray that way you can alloc init it in your init call. Use property declarations to synthesize your getters and setters. As for putting the people in your array, you can add them to the mutable array every time one is selected

How can i lazy initialize a NSMutableArray?

How can i lazily initialize a NSMutableArray of Buttons ? I do something like this :
-(NSMutableArray *)roasteryButtons
{
if(!roasteryButtons)
{
roasteryButtons = [ NSMutableArray new];
//other code
}
return roasteryButtons;
}
And don't know what to do to call this lazy initializer ? i.e. I need to initialize the array so that i may set the frame for every button in the array
What u have done is correct. Instead of allocating the array in the init method of class, u are allocating the array only when required. Thus it serves the purpose of lazily allocating.
In the class, Wherever you want the array, you just call,
NSMutableArray *arr = [self roasteryButtons];
Also declare the method in header file as, -(NSMutableArray*)roasteryButtons;.
If you want the reference of the array in other classes, the call like,
[classObj roasteryButtons];
I have shown it as instance method. You can also declare that as class method, if you want like that.
And release that in -(void)dealloc method.
I guess you know when to call this method, right ?
The first thing is that you shouldn't use "new" method, but [[NSMutableArray alloc] init] instead : You should have a look at all existing [Init] methods available for NSArray : there are a bunch of them (with capacity, with objects, etc...)
Anyway, you should add some parameters to your method [roasteryButtons] : parameters that will help the method to know, for instance how many buttons to create, what is the frame where they have to show, etc. So this will look a bit like
-(NSMutableArray *)roasteryButtonsWithFrame:(*Frame) andNumbersOfButtons:(int)
for example...
or instead of parameters, you can pass a reference to a delegate that will be able to give answers to those questions (How many buttons, what's my frame and bounds, etc.) So in this case, the method will look like :
-(NSMutableArray *)roasteryButtonsWithDelegate:(id)
(This delegate should implement a protocol that you will create, containing the different methods that the delegate will have to respond to. ie methods like [howManyButtons]...)
The Perfect Way to Lazy initialize is as follow
in .h file declare your NSMUtableArray as property as follow
#property (nonatomic,strong) NSMutableArray *array;
Now in .m file synthesize it and do lazy initialize in getter like as follow:
#synthesize array=_array;
(NSMutableArray *) array
{
(!_array) _array=[[NSMutableArray alloc]init];
//this line is called lazy intialization..this line will create MutableArray at program //run time.
return _array
}
Now answer why we need this is that it take care about that if no NSMutableArray is created then it create it at programme run time and like this your app will not crash.
You could make your method a class method:
+(NSMutableArray *)roasteryButtons {
in this way you will be able to call it like this:
[MyRoasteryButtonClass roasteryButtons];
and this will return you your object.
Hope this helps.

Trouble Copying custom class initialization

I have a custom class of type NSObject that contains a single NSMutableArray. This class is called Mutable2DArray and is designed to emulate a 2 dimensional array of type NSMutableArray. There is a custom init method - (id)initWithX:(int)x Y:(int)y that asks for the dimensions for the array and allocates the required arrays within the only array the class owns.
My issue is when I try to copy an instance of Mutable2DArray I get an error saying the copyWithZone is an unrecognized selector. I thought copy was a base method of NSObject so I'm confused why I cant create a copy of the instance like this:
Mutable2DArray *Array1 = [[Mutable2DArray alloc] initWithX:10 Y:10];
Mutable2DArray *Array2 = [Array1 copy];
Am I missing something so obvious here?
Things that I can think of to check, off the top of my head:
Does the header file actually declare the interface as inheriting from NSObject?
Does your custom initWithX: Y: method call [super init] before finishing?