objective-c beginner: getter setter prob and EXC_BAD_ACCESS error - iphone

Iam getting an EXC_BAD_ACCESS all the time and I cannot figure out why...
Simple task:
The Parser Class pases XML with touchXML in an NSMutableArray called listArray.
In the Method grabCountry I can access the listArray and listArray.count works well.
Now I need the listArray.count in another Class the MasterViewController.
But Im getting an EXC_BAD_ACCESS error all the time.
Please help!
Here is the code snipplet:
Parser.h
#import <Foundation/Foundation.h>
#interface Parser : NSObject
#property (strong, retain) NSMutableArray *listArray;
#property (strong, retain) NSURL *url;
-(void) grabCountry:(NSString *)xmlPath;
#end
Parser.m
#import "Parser.h"
#import "TouchXML.h"
#implementation Parser
#synthesize listArray;
#synthesize url;
-(void) grabCountry:(NSString *)xmlPath {
// Initialize the List MutableArray that we declared in the header
listArray = [[NSMutableArray alloc] init];
// Convert the supplied URL string into a usable URL object
url = [NSURL URLWithString: xmlPath];
//XML stuff deleted
// Add the blogItem to the global blogEntries Array so that the view can access it.
[listArray addObject:[xmlItem copy]];
//works fine
NSLog(#"Amount: %i",listArray.count);
}
#end
MasterViewController.h
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import "TouchXML.h"
#import "Parser.h"
#class Parser;
#interface MasterViewController : UITableViewController{
Parser *theParser;
}
#end
MasterViewControlelr.m
- (void)viewDidLoad
{
NSString *xmlPath = #"http://url/to/xml.xml";
theParser = [[Parser alloc] init];
//Starts the parser
[theParser grabCountry:xmlPath];
//Here I want to access the Array count, but getting an BAD ACCESS error
NSLog(#"Amount %#",[theParser.listArray count]);
[super viewDidLoad];
}
Can anyone explain me what the problem here is?
Thanks!

Internally, each #property has a corresponding instance variable.
In your -grabCountry method, you are directly accessing the instance variable in the statement listArray = [[NSMutableArray alloc] init]; (same with url = [NSURL URLWithString: xmlPath];), instead of the #property's setter method, causing the NSMutableArray that you alloc-init'd to not be retained by the property. To invoke the #property's setter method, you should call
NSMutableArray *temp = [[NSMutableArray alloc] init];
self.listArray = temp; // or [self setListArray:temp];
[temp release];
If you want to have Xcode show an error when you are directly accessing the instance variable of an #property, you can have #synthesize listArray = _listArray, which changes the name of the instance variable to _listArray.
Generally, if there is an alloc-init, there must be a corresponding release (except if using Automatic Reference Counting).
Also, in the [listArray addObject:[xmlItem copy]]; statement, the call to copy is not needed, as NSArrays retain every object that is added to them. Calling copy also increases the retain count, which is another leak. Instead, you should just have [self.listArray addObject:xmlItem];
You are getting EXC_BAD_ACCESS because in NSLog(#"Amount %#",[theParser.listArray count]);, you are using %# format specifier, which is for NSStrings. You want to print the array's count, an integer, so you should be using %d or %i.

Related

Saving Arrays Between Classes

Currently attempting to save an array that is populated according to which cells in a UITableView are chosen and saving this array in an instance of a seperate object. I am getting the array to populate just fine, however, my save method, which is an IBAction that is invoked by clicking on a Bar Button doesn't seem to be working. Here is some code:
-(IBAction)saveWorkout:(id)sender {
Workouts *new = [[Workouts alloc] init];
[new addNewWorkout:customWorkout];
[customWorkout removeAllObjects];
}
This code is from the first class.
And here is the code for my addNewWorkouts method in the Workouts class:
-(void)addNewWorkout:(NSMutableArray*)array {
NSMutableArray *temp = [[NSMutableArray alloc] init];
temp = array;
self.workoutList = temp;
[temp release];
}
Here is my "Workout.h"
#import <Foundation/Foundation.h>
#interface Workouts : NSObject {
NSString *workoutName;
NSMutableArray *workoutList;
NSString *description;
int *reps;
int *weights;
int *sets;
}
#property (nonatomic, retain) NSString *workoutName;
#property (nonatomic, retain ) NSString *description;
#property (nonatomic, retain) NSMutableArray *workoutList;
-(void)addNewWorkout:(NSMutableArray*)array;
#end
Before running this code, I get a Warning from Xcode saying that 'Workouts may not respond to 'addNewWorkouts.'
Anyone know what is causing this error? Once I build & run, I click on the Save button and the app crashes with a unrecognized selector sent to instance 0x3b04410 error.
You call [new addNewWorkouts:customWorkout]
when the method's selector is addNewWorkout: (note that there is no plural in the method name)
This will make a bad method call and result in a crash.
Also, there is a problem with the memory management of the addNewWorkout method.
1- NSMutableArray *temp = [[NSMutableArray alloc] init];
2- temp = array;
3- self.workoutList = temp;
4- [temp release];
You allocate a new NSMutableArray on line 1, then lose its reference on line 2 when you replace its pointer by 'array'. The allocation you just made is lost and the program will leak.
Then, on line 4, you send a release message to 'temp' which actually points to 'array', resulting in the release of the parameter that you received and not the temporary object.
Is there a reason whny you create a temporary array? You can just assign the property and make the property copy or retain it, depending on your needs.

Objective-C initializing an NSArray vs NSMutableArray

Why does this work:
self.array = newArray;
But this doesn't:
[[self mutableArray] addObject:object];
Meaning, why do I need to init the NSMutableArray for it to work when I don't have to init the NSArray?
EDIT: Hops this is clearer guys.
Interface:
#interface Foo : UIViewController {
}
#property (nonatomic, retain) NSArray *array;
#property (nonatomic, retain) NSMutableArray *mutableArray;
#end
Implementation:
#implementation Foo
#synthesize array;
#synthesize mutableArray;
- (void)viewDidLoad {
[super viewDidLoad];
self.array = [Class returningAnArray];
[[self mutableArray] addObject:objectIHaveOmittedTheCode];
}
#end
self.array = newArray;
In this line, you are assigning already created object to self.array. So, you've no need to create it.
[[self mutableArray] addObject:object];
But in this line you are trying to add an object to a array which is not created actually. If you don't create the array, it will be nil, and sending message to nil won't take any effect.
In Objective-C NSArray objects are immutable.
self.array = newArray;
This line is a property assignment. The self.array property returns a reference pointing to a location in memory that contains an NSArray object. By assigning the property to different objects you're not really modifying the object themselves.
If you wish to modify an existing NSArray object, you'll have to create a NSMutableArray object containing the same elements:
NSMutableArray *mutableArray = [[NSMutableArray alloc] initWithArray:self.array];
[mutableArray addObject:object];
Note that NSMutableArray inherits from NSArray, so you can safely assign the object referenced by mutableArray to any variable of type NSArray.
[Class returningArray] did the allocation for you. Every object needs to be allocated (and should be initialized) before it can used. The solution is.
- (void)viewDidLoad {
[super viewDidLoad];
self.array = [Class returningAnArray];
self.mutableArray = [NSMutableArray array];
//Now you can use mutable array
[[self mutableArray] addObject:objectIHaveOmittedTheCode];
}
Now you have created your array and your mutable array with out explicitly calling alloc on either because those classes have done it for you.

releasing and retaining NSString* properly

Hi all i'm still new to iPhone development, but had strong experience with other programming languages. The thing that makes me pull my hair out is the Obj-C memmory management and releasing / retaining properly. I know the concept, i know "once i understand it will be easy" but i'm not quite still there, and that makes me crazy. Here i have one simple piece of code with class and method, that simply add's one character to existing string that is synthesized so used as class proprety ... the class for example is called myClass ...
myClas.h
#interface myClass : NSObject {
#private
NSString* someCommonString;
}
#propery (retain, nonatomic) NSString* someCommonString;
myClass.m
...
#synthesize someCommonString;
- (id) init
{
self = [super init];
if(self)
{
someCommonString = [[NSString alloc] initWith String:#"one "];
}
}
- (NSString*) appendString:(NSString*) stringToAdd
{
NSString* result = [someCommonString stringByAppendingString: stringToAdd];
return result;
}
- (void) doTheJob
{
NSString* test1 = #"two ";
NSString* test2 = [[NSString alloc] initWithString: #"three "];
NSString* test3 = [NSString stringWithFormat:#"four "];
self.someCommonString = [self appendString:test1];
self.someCommonString = [self appendString:test2];
self.someCommonString = [self appendString:test3];
NSLog(#"%#", someCommonString);
}
- (void) dealloc
{
[someCommonString release];
[super release];
}
...
Ok, after i alloc myClass and execute the doTheJob method, i should have #"one two three four" in the someCommonString class proprety. I know this is working, but is also leaking as hell. test1, test2 and test3 are 3 ways of initialising NSString, and only the test2 should be released, this is quite self-explanatory, but im much more worried what happens when passing them as arguments to the appendString method. Because there i know i hawe a leak, but don't know how to handle 1. stringToAdd argument [should i worry about it in appendString method at all ?] 2. the result -> if i autorelease the result, i don't know at which point the result will be deallocated. 3. sommeCommonString in appendStringMethod, should i retain it, release it or leave it alone ?
Huh :)
At a first glance, it seems to me like you're not releasing test2. After you have appended it to your common string, you do not need to retain it anymore.
self.someCommonString = [self appendString:test1];
self.someCommonString = [self appendString:test2];
self.someCommonString = [self appendString:test3];
[test2 release];
The other two (test1 and test3) are autoreleased, so your thread will reclaim them at some point).
As far as your appendString: method is concerned, result is already autoreleased and in fact you could reduce your implementation to
return [someCommonString stringByAppendingString: stringToAdd];
someCommonString is not affected by the operation at all. stringByAppendingString: returns a new autoreleased string from the concatenation of self and stringToAdd.
Hope that helps
… but im much more worried what happens when passing them as arguments to the appendString method. Because there i know i hawe a leak, but don't know how to handle
1. stringToAdd argument [should i worry about it in appendString method at all ?] …
You don't have a leak in -appendString:. You are passing stringToAdd around without retaining it and that's okay here.
The result is autoreleased and you don't have to take any action on it.
2. the result -> if i autorelease the result, i don't know at which point the result will be deallocated.
The result is already autoreleased and will be released as soon as the current NSAutoreleasePool will be released. Til then you can pass it around without retaining.
3. sommeCommonString in appendStringMethod, should i retain it, release it or leave it alone ?
Leave it alone, it's managed by the accessors. But as fedmest (and you too) said: release test2.
Well there are few problems in your code, but basic problem is you need NSMutableString string, not NSString to make your code work.
in init method, correct code to initialize is,
someCommonString = [[NSMutableString alloc] initWithString:#"one "];
You have to return the object (self) from init, otherwise it will not work, like this.
return self;
If you wanted to append the string, it should be NSMutableString, not NSString.
[[self someCommonString] appendString:test1];
[[self someCommonString] appendString:test2];
[[self someCommonString] appendString:test3];
In dealloc method, you call dealloc method of super, not release the super. So correct it it like this.
[super dealloc];
There is no need to release test1 and test3, because they are autorelease.
I have witten the correct code, try this out.
#interface myClass : NSObject {
#private
NSMutableString* someCommonString;
}
#property (retain, nonatomic) NSMutableString* someCommonString;
- (void) doTheJob;
#end
#implementation myClass
#synthesize someCommonString;
- (id) init
{
self = [super init];
if(self)
{
someCommonString = [[NSMutableString alloc] initWithString:#"one "];
}
return self;
}
- (NSString*) appendString:(NSString*) stringToAdd
{
NSString* result = [someCommonString stringByAppendingString: stringToAdd];
return result;
}
- (void) doTheJob
{
NSString* test1 = #"two ";
NSString* test2 = [[NSString alloc] initWithString: #"three "];
NSString* test3 = [NSString stringWithFormat:#"four "];
[[self someCommonString] appendString:test1];
[[self someCommonString] appendString:test2];
[[self someCommonString] appendString:test3];
NSLog(#"%#", someCommonString);
[test2 release];
}
- (void) dealloc
{
[someCommonString release];
[super dealloc];
}
#end
This is a technique which might help you deal better with what is happening with the #synthesize directive. Change your header as follows
#interface myClass : NSObject {
#private
NSString* _bob;
}
#property (retain, nonatomic) NSString* someCommonString;
and your class file to
#synthesize someCommonString = _bob;
If you recompile your code you will get build errors. Fix these and you will suddenly be able to see exactly what you are accessing via the synthesized property and what you are directly accessing.
Additionally, as already mentioned, you need to release test2 in the doTheJob method.

How to change the iVar of another class

This is the code. It is pretty straight forward. I made two classes one is returning the error and hydrate the iVar of another class (TheView) and show it to the User. however I cant figure it out why the View return Null at all time. Thanks is advance guys.
#interface AccountControllerModel : NSObject {
NSString *anError;
}
#property (nonatomic, retain) NSString *anError;
AccountControllerModel.m
#synthesize anError;
- (void)uploadFailed:(ASIHTTPRequest *)theRequest{
RegistrationViewController *regoVC = [[RegistrationViewController alloc] init];
[regoVC manageTheError:#"THIS IS AN ERROR"];
[regoVC release]; regoVC = nil;
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#interface RegistrationViewController : UIViewController {
NSMutableString *test;
}
#property (nonatomic, assign) NSMutableString *test;
#synthesize test;
-(void)viewDidLoad {
test = [[NSMutableString alloc] init];
}
-(void)manageTheError:(NSString *)theError{
self.test = [NSMutableString stringWithFormat:#"%#",theError];
resultOfRegistration.text = [NSString stringWithFormat:#"%#",self.test];
NSLog(#"test is %#",self.resultOfRegistration.text); //It comes back Null
}
Alex is right, some clarification on what's not working would help but by looking through I may have found your error. [[NSNotificationCenter defaultCenter] postNotificationName:#"Blah" object:self], you have object set to nil which could be your issue with the notification.
There are a number of problems with your code.
#property (nonatomic, assign) NSMutableString *test;
Two things here, one, exposing a NSMutable* object in a property is never a good idea, two you should 'copy' value objects, especially because this is how you're treating it in your code. Make this #property (nonatomic, copy) NSString *test;
regoVC.test = [NSMutableString stringWithString:self.anError];
You're assigning an autoreleased object to an assign property, this is a leak, the change above will fix that.
NSLog(#"test is %#",test); // It is perfect as you expect
test isn't in scope here, but I'd assume that was supposed to be regoVC.test, these other changes should remedy the situation.

How am I leaking memory?

I have a table view that when loading creates a person object
Person.h
#import <UIKit/UIKit.h>
#import "TwitterHelper.h"
#interface Person : NSObject {
NSDictionary *userInfo;
NSURL *image;
NSString *userName;
NSString *displayName;
NSArray *updates;
}
/*
#property (retain) NSString *userName;
#property (retain) NSString *displayName;
#property (retain) NSDictionary *userInfo;
*/
#property (nonatomic, copy) NSURL *image;
#property (retain) NSArray *updates;
- (id)initWithUserName:userName;
#end
Person.m
#import "Person.h"
#implementation Person
/*
#synthesize userName;
#synthesize displayName;
#synthesize userInfo;
*/
#synthesize image;
#synthesize updates;
- (id)initWithUserName:(NSString *)user{
userName = user;
userInfo = [TwitterHelper fetchInfoForUsername:user];
displayName = [userInfo valueForKey:#"name"];
image = [NSURL URLWithString:[userInfo valueForKey:#"profile_image_url"]];
updates = [TwitterHelper fetchTimelineForUsername:userName];
return self;
}
- (void)dealloc
{
/*
[userName release];
[displayName release];
[updates release];
[userInfo release];
[image release];
*/
[super dealloc];
}
#end
Inside my UITableView method cellAtRowForIndexPath I am creating each person object and assigning the image property like so...
Person *person = [[Person alloc] initWithUserName:userName];
NSData *data = [[NSData alloc] initWithContentsOfURL:person.image];
[data release];
When I run this in Instruments it highlights the NSData *data... row saying that is where the leak is.
Why is it leaking there?
First, you need to understand the difference between instance variables and properties and getter/setters.
instance variables (ivars) are variables stored in
your object. You access an ivar from within a method simply by naming it (eg "userName").
properties define an
interface to your object, allowing
information to be read and/or written
to your object.
getters/setters implement that interface and may use an ivar as backing storage
You access a property by using a getter/setter, either explicitly (eg [self userName]) or (equivalently) using dot syntax self.userName. Note that these two notations are exactly identical. You declare a property (ie, you declare an interface to your object) using #property in the interface of your object, something like:
#property (copy) NSString* userName;
This declartion is essentially equivalent to typing:
- (NSString*) userName;
- (void) setUserName: (NSString*) theUserName;
You implement a property, either by using #synthesize (which simply tells the compiler to write the getter/setter for you) or by implementing it yourself (ie, you write methods implementation for userName and setUserName). There is also a rarely used third option, #dynamic, which tells the compiler you will handle the methods at run time, essentially just silincing the warning you would otherwise get.
Next, you need to read and understand the memory management rules. Its only 9 short paragraphs, go read it now, I'll wait. Done? good.
Further, you need to know that you should not use getters/setters in either the init or dealloc routines.
So your init routine should look something like this:
- (id)initWithUserName:(NSString *)user{
userName = [user copy];
userInfo = [[TwitterHelper fetchInfoForUsername:user] retain];
displayName = [[userInfo valueForKey:#"name"] copy];
image = [[NSURL URLWithString:[userInfo valueForKey:#"profile_image_url"]] copy];
updates = [[TwitterHelper fetchTimelineForUsername:userName] retain];
return self;
}
Note that you take ownership of each value you store in an ivar with retain or copy. Generally, you use copy for NSString to convert an NSMutableStrings into NSStrings you own, rather than retain which would leave you holding a reference to a possibly mutable string. The same issue applies to NSArray/NSDictionary, but we will assume TwitterHelper intends to hand off the fetched data.
Your dealloc will have to release the various ivars:
- (void)dealloc
{
[userName release];
[displayName release];
[updates release];
[userInfo release];
[image release];
[super dealloc];
}
Anywhere else in your code you would use self.userName to access or change the properties, rather than access the ivars directly.
Note that you might consider not storing the displayName (and similarly image) at all, but simply implement a property getter that retrieves it from userInfo. To do this, delete the displayName ivar, change the property to:
#property (readonly) NSString *displayName;
remove the #synthesize displayName, and add a manual getter:
- (NSString*) displayName
{
return [userInfo valueForKey:#"name"];
}
and remove the release in dealloc.
Note that you do not need to retain/release the value in displayName - you return a value that the receiver does not own and it is up to them to copy/retain it if they want to keep it.
If you choose to create a property, you should use:
self.image = [NSURL URLWithString:[userInfo valueForKey:#"profile_image_url"]];
in your init message and not
image = [NSURL URLWithString:[userInfo valueForKey:#"profile_image_url"]];
Setting the value without the self prefix will not call the copy or retain message, and will create a memory problem (not necessarily a leak).
This might be what Instruments is pointing you to.
(This obviously applies to all properties!)
Alternatively, if you don't want to use the accessor, then retain or copy the value retrieved, e.g.:
image = [[NSURL URLWithString:[userInfo valueForKey:#"profile_image_url"]] retain];
You are calling alloc on Person but not releasing it. You've leaked your person object.
(in your cell configuration)