What I am doing is
//ClassB.h
#property (strong, nonatomic) NSString *name;
and
//ClassA.h
#interface ClassA : NSObject
+(ClassA*)methodA:(NSData*)data;
-(id)initWithData:(NSData*)data;
#property (nonatomic, strong) NSMutableArray *arr;
#property (nonatomic, strong) RXMLElement *rxmlRoot;
#end
//ClassA.m
#implementation ClassA
#synthesize arr;
#synthesize rxmlRoot;
+(ClassA*)methodA:(NSData*)data {
return [[ClassA alloc] initWithData:data];
}
-(id)initWithData:(NSData*)data {
self = [super init];
if (self) {
arr = [NSMutableArray array];
rxmlRoot = [RXMLElement elementFromXMLData:data];
/*****edit : just been added to make codes clear*****/
NSString *node = #"players.player";
[rxmlRoot iterate:node with:^(RXMLElement *e){
ClassB *classB = [[[ClassB alloc] init] autorelease];
[classB setName: [e attribute:#"name"]];
// adding ClassB into arr
[arr addObject:classB];
}];
}
return self;
}
#end
So now I am having ClassA object whose arr contains ClassB
Question : later on, when I try to access an particular property of ClassB like
((ClassB*)[classA.arr objectAtIndex:0]).name
and I am getting EXC_BAD_ACCESS at above line..
Please advice me on this issue and how to correct the error. Any comments are welcomed here
Thanks
This line
[arr addObject:ClassB]
makes no sense. Is your intention to put an instance of ClassB into that array, or the class itself (i.e. [ClassB class])? Presumably you must intend to put an instance of ClassB in there, otherwise trying to access its properties later on (e.g. firstName) would make no sense. Also, does your ClassB even have a firstName property, because the piece of ClassB's interface that you show us only mentions a name property.
Update:
Since you are using manual memory management, you need to retain the objects (arr, rxmlRoot) you create in your initializer using convenience constructors, which return autoreleased objects. For example, the code should be
arr = [[NSMutableArray array] retain];
Post your ClassB.m .
Are you making the #synthesize name?
Also make the Alloc for arr.
This line is so wrong:
((ClassB*)[classA.arr objectAtIndex:0]).firstName
Your string is called name , not firstName. It should be :
((ClassB*)[classA.arr objectAtIndex:0]).name
The code in the question has changed substantially, so my previous answer now makes no sense and I have removed it. Given revised code, the first thing to do is to log what's going on.
NSLog(#"classA: %#", classA);
NSLog(#"classA.arr: %#", classA.arr);
((ClassB*)[classA.arr objectAtIndex:0]).name
If it blows up on the first log statement, things are really bad. But then at least you know that classA is pointing to something rotten and you can work back from there.
You can achieve the same thing in the debugger, by setting a break point ahead of the line and inspecting. Given that you are getting an EXC_BAD_ACCESS one of the pointers is pointing to a dodgy object, e.g. one that has been released. It looks as if you are using ARC (because you have strong in your property), which should help manage the memory - but then again, you have an autorelease in there, so maybe not.
Related
I have en error that I suspect could be the compiler... I'm not an expert myself, but I checked some other code with the exact same behavior with an expert and we couldn't figure it out.
I setup a property as such:
#interface aViewController ()
#property (nonatomic, strong) NSArray *listOfTitles;
#end
#implementation aViewController
#synthesize listOfTitles = _listOfTitles;
- (NSArray *)listOfTitles
{
if (!_listOfTitles)
_listOfTitles = [NSArray arrayWithObjects:#"first", #"second", #"third", #"fourth", nil];
return _listOfTitles;
}
However, the app crashes (the array would be more complex and serve to setup textLabel.text properties on UITableViewCell).
What's bothering me, it's that the NSArray always have an invalid address (0x00000001). What am I missing? Isn't this the proper way to do lazy instantiating?
Like I mentionned, I experienced the exact same thing (property instantiated with 0x00000001 ) with other classes, in other projects. That's why I'm suspecting the compiler to be the issue. But really, I'm all ears for solutions!...
The most obvious solution is to set the ivar to nil in your init function.
-(id)init{
if (self = [super init]){
_listOfTitles = nil;
}
}
The real question is why isn't this being done for you, since all ivars should be initialized to nil. I suspect it has something to do with the property being declared in the .m file. Either way it will cause no harm to nil out the ivar yourself.
I have a class myClass and would like to access its properties, a NSArray *currentOptions (specifically to get the size of currentOptions and access the NSStrings which I've put in it.)
I have a method called generate options which assigns an filled array to *currentOptions. Generate options is called before I try to access *currentOptions. An instance of myClass has also been added to the ViewController via the App delegate. However when buttonOnePressed is called, I keep getting this error:
[myClass currentOptions]: unrecognized selector sent to instance 0x9b10490
Here is the parts of my code:
//TClass.h
#interface TClass : NSObject {
NSArray *currentOptions;
}
#property (nonatomic, retain) NSArray *currentOptions;
#end
//viewController
- (IBAction) buttonOnePressed:(id)sender {
NSLog(#"button1 pressed");
NSLog(#"int: %d",[myClass.currentOptions count]);
//myClass here is the instance of TClass
}
One thing that sometimes causes that error is failing to properly retain myClass. (Aside: "myClass" is a really bad name for a pointer because the thing being pointed to is almost certainly not a class but an object, i.e. an instance of a class.) If you don't retain the object that myClass points to, it will be deallocated. Sometimes, a different object happens to be created at that some location, and you end up sending a message meant for the original object to the new one, which is a different type and doesn't understand the message.
To all who have been following, the problem has been resolved by making the following changes:
1) Synthesized current options TClass.m
#implementation TClass
#synthesize currentOptions;
#end
2) I made currentOptions a NSMutableArray instead of a NSArray. This is because I need to reassign values to current options. Somehow it crashes with NSArray and everything goes smoothly with NSMutable array like such
#implementation TutorialClass
if ([currentOptions count] > 0) {
[currentOptions removeAllObjects];
}
[currentOptions addObject:[options objectAtIndex:0]];
[currentOptions addObject:[options objectAtIndex:1]];
[currentOptions addObject:[options objectAtIndex:2]];
[currentOptions addObject:[options objectAtIndex:3]];
3) And of course, I'll also have to do the following in the init method of TClass.m
currentOptions = [[NSMutableArray alloc] init];
Now its time to get some food. Thanks Caleb :D
I'm having some trouble with a NSMutableArray. I'm sure i'm doing something wrong with the allocation of the NSMutableArray but it's not obvious to me being an iPhone newbie. When i run the code below i can add the object MyObject to the array objects_ and set the name etc. The NSLog displays the correct data.
But when i try to access the objects_ member from the function printObject i get a SIGABRT. Looks like the memory has been deallocated or something?
Any help appreciated.
#interface MyObject : NSObject {
NSString *name;
}
-(void) SetName:(NSString*) name_str;
-(NSString*) GetName;
#end
#interface ObjectListViewController : UITableViewController {
NSMutableArray* objects_;
}
-(void) initTableData;
#end
#implementation ObjectListViewController
- (void)initTableData {
objects_ = [[NSMutableArray alloc] initWithCapacity:10];
MyObject *obj = [MyObject alloc];
[obj SetName:#"Test"];
[objects_ addObject:obj];
MyObject* testObj = (MyObject*)[objects_ objectAtIndex:0];
NSLog([testObj GetName]);
}
- (void)printObject {
MyObject* testObj = (MyObject*)[objects_ objectAtIndex:0];
NSLog([testObj GetName]);
}
We can eliminate the lack of an init call on MyObject as the cause of the crash as in this case it will be benign. Calling init on NSObject will just return self, so calling it in this case won't change the behaviour. So I don't think the first two answers here will make any difference:
An object isn’t ready to be used until it has been initialized. The init method defined in the NSObject class does no initialization; it simply returns self.
Chuck correctly points out that init is a fundamental step in object allocation and initialization and you should be calling it when you allocate MyObject.
I am not sure the third answer is correct either. I don't really see how adding synthesise on the objects_ array will make any difference. You haven't defined it as a property, and I don't really see why you would need to, given it is just data internal to the class.
The comment on the question Well, for starters, you never define printObject in the #interface. from eykanal doesn't really help you either, because you must be calling printObject internally, otherwise you wouldn't be hitting the crash.
Reading the through the code, I can't see an obvious error. The retain count on objects_ after initTableData finishes should be one, the retain count on the instance of MyObject should also be one. So I think there must be some other code that is releasing objects_ elsewhere?
I am assuming it is crashing on the objectAtIndex call? Is there any info in the console? What does the call stack look like?
MyObject *obj = [MyObject alloc];
should be:
MyObject *obj = [[MyObject alloc] init];
#interface ObjectListViewController : UITableViewController {
NSMutableArray* objects_;
}
#property (nonatomic, retain) NSMutableArray *objects_;
-(void) initTableData;
-(void) printObject;
#end
add the synthesize in the implementation
#implementation ObjectListViewController
#synthesize objects_;
Here are some issues in your code:
You never initialise your MyObj object. Although it inherits directly from NSObject and NSObject is documented to do nothing except return self, you never know if other stuff happens behind the scenes, so put it in just to eliminate the posssibility.
Your methods don't follow the normal naming conventions. method names should begin with a lower case letter and "get" should only be used when passing back data by reference through the parameters as in e.g. NSData -getBytes:length:. Your getter and setter should be -name and -setName: respectively. This may seem like a minor nitpick, but it'll help you later on if you start to use KVO and KVC.
Never do NSLog(someStringVariable) always NSLog(#"%#", someStringVariable). As you have it now, if the object's name contains a percent formatting sequence e.g. %#, %d, %s etc, your program will crash on the NSLog. However, this is not the cause of your current problem - it would be crashing on the NSLog in -initTableData
you don't need to cast the result of -objectAtIndex:
Having said all that, I can't see anything that would cause the particular issue you have. It may be that the getter or setter for the name in MyObject is incorrect. Please post them.
I don't really have a solid understanding of Setters and Getters for objective-c. Can someone provide a good guide for beginners? I noticed that this comes into play when trying to access variables in another class, which I am trying to do right now. I have two classes, lets say A and B. I have a NSString variable in A with the #property (retain) NSString *variable. Then I go ahead and synthesize it. Now when the view loads in the class I set the value for the variable to "hello". Now what I want to do is access the string from class B. I have imported the the class A, and initialized it with this code:
AClass *class = [[AClass alloc] init];
NSLog(#"Value:%#", class.variable);
[class release];
However in the debugger it returns a value of "(null)", which I don't really understand. If someone could lead me into the right path I would greatly appreciate it.
Thanks,
Kevin
The particular section of interest to you is Declared Properties.
b's interface should look like:
#interface b : NSObject {
NSString *value;
}
#property (nonatomic, retain) NSString *value;
- (id) initWithValue:(NSString *)newValue;
#end
Your implementation of b should look something like:
#implementation b
#synthesize value;
- (id) initWithValue:(NSString *)newValue {
if (self != [super init])
return nil;
self.value = newValue;
return self;
}
#end
Which you could then call like:
b *test = [[b alloc] initWithValue:#"Test!"];
NSLog(#"%#", test.value);
The Getting Started with iOS guide in the iOS Reference Library outlines the reading material you should go through to nail down basics like this. Apple's guides are clearly written and thorough, and you'll be doing yourself a huge favor by hunkering down and just reading them.
This makes no sense to me. Maybe someone here can explain why this happens.
I've got an NSMutableString that I alloc at the top of my iPhone app, then append to later in the process. It results in a SIGABRT, which doesn't add up to me. Here's the code:
Header File (simplified):
#interface MyAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
NSMutableString *locationErrorMessage;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, copy) NSMutableString *locationErrorMessage;
#end
And the relevant parts of the Main:
#implementation MyAppDelegate
#synthesize window;
#synthesize locationErrorMessage;
- (void)applicationDidFinishLaunching:(UIApplication *)application {
self.locationErrorMessage = [[NSMutableString alloc] init];
}
- (void)anotherFunction {
[self.locationErrorMessage appendString: #"Blah Blah Blah"];
}
This all seems simple enough. What am I missing?
I would call this a bug in how property setters are generated, but the answer is pretty simple:
You declared the property as (nonatomic, copy). This means that whenever the locationErrorMessage property is set, it's going to invoke copy on the new value and use that copy as the property value.
Unfortunately, invoking copy on an NSMutableString does not result in an NSMutableString, it results in an NSString (which cannot be mutated using something like appendString:).
So the simple fix would be to change the property declaration from copy to retain.
(I would say that the bug would be: If you declare a property for a mutable object as copy, then the copy setter should actually use mutableCopy and not copy) => rdar://8416047
Your property is copying the passed in string. A copy always is immutable, so you’re trying to send appendString: to an immutable NSString. Declare your property as retain and it will work or write a custom setter that copies the string using mutableCopy.
You also have a memory leak, you should use [NSMutableString string] instead of the alloc-init sequence.
Btw, you have a leak there,
self.locationErrorMessage = [[NSMutableString alloc] init];
you're copying the value, but you never release the actual first allocated NSMutableString.