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.
Related
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.
I'm confused on what the proper way to write custom init methods for youw own subclass in terms of memory management, custom subclasses, and arrays. If I have properties like:
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSMutableArray *array;
#property (nonatomic, retain) SomeSubclassOfNSObject *object;
#interface SomeSubclassofNSObject
#property (nonatomic, retain) NSString *category;
How do I write my init method?
do you do:
initWithName:(NSString *)aName object:(SomeSubclassOfNSObject *)anObject {
if (self = [super init]) {
self.name = aName; // or do you do name = aName or name = [aName copy] autorelease] or name = [NSString alloc] initWithFormat:#"%#", aName]
self.object = anObject; // do I need to make a copy of this object so they don't point to the same object?
// loop through NSMutableArray and copy the objects?
// not really sure what to do for arrays.
}
return self;
}
I would recommend self.name = aName; in most cases because this takes advantage of the generated setter and thus the retain count is incremented implicitly. That means your reference is safe whatever the caller of init will do afterwards with aName. The same applies to the dealloc counterpart: just write self.name = nil; and your done.
Things are different if you are supplying NSMutableString because changes inside of your class will affect other classes' references to this. But from design view mutable strings should be used as paramters only if it's your intention that they are manipulated.
self.object = anObject;: Well it depends what you want. If all classes should point to the same object don't make copy (I think this is most often exactly what you want). There might be cases when it is reasonable to make a deep copy.
Arrays: You need something like array = [[NSMutableArray alloc] init]; in your init method. Afterwards the retain count will be incremtented automatically for every object you add i.e. you must not call retain on the added object itself. Like in 2. there might be situations where you really want to have copies of the objects of some source array. OK do it but this is rarely the case.
More often you have an existing array and want a sub-array of it or a special sorted version. So you have got a new array but the same elements.
This was the short answer. More about this in:
Advanced Memory Management Programming Guide
Declared Properties
I have a leakage in my connectionDidFinishLoading class how can fix this?
#property (nonatomic,retain) NSMutableData *responseXMLData;
#property (nonatomic,copy) NSMutableData *lastLoadedResponseXMLData;
-(void)dealloc {
[responseXMLData release] ;
responseXMLData=nil;
[lastLoadedResponseXMLData release];
lastLoadedResponseXMLData=nil;
[super dealloc];
}
#property (nonatomic,copy) NSMutableData *lastLoadedResponseXMLData;
Since you are working with a mutable object that you are going to be setting and changing, you should use RETAIN:
#property (nonatomic,retain) NSMutableData *lastLoadedResponseXMLData;
retain - "Specifies that retain should be invoked on the object upon assignment. ... The previous value is sent a release message." So you can imagine assigning an NSString instance (which is an object and which you probably want to retain).
copy - "Specifies that a copy of the object should be used for assignment. ... The previous value is sent a release message." Basically same as retain, but sending -copy rather than -retain.
Here is some good reading on the various get/set methods you can instruct #property/#synthesize to create for you: http://cocoawithlove.com/2010/06/assign-retain-copy-pitfalls-in-obj-c.html
you did not tell us where you are creating the object for responseXMLData? where ever you are initializing that object should release that
self.responseXMLData = [[NSMutuableData alloc]init]autorelease];
and in your dealloc method you could have just say
-(void)dealloc {
self.responseXMLData = nil; //which is equivalent to [responseXMLData release]; responseXMLData=nil;
}
and
Just a thought, in your:
self.lastLoadedResponseXMLData = nil;
self.lastLoadedResponseXMLData = self.responseXMLData;
Before one release happens at dealloc, could there be a possible where your just set to nil and copy next responseXMLData to lastLoadedResponseXMLData without releasing any previous copies?
In this example:
#interface something : something
{
NSString *saveString;
}
-(void)saveStringForLater:(NSString*)myString
{
//do stuff with myString
...
//then save it for later
saveString = myString;
}
-(void)someOtherTimeInFuture
{
//do stuff with saveString
...
}
So given the above, my questions are:
1) Is this safe/proper way of doing this?
2) Will I need to worry about releasing saveString?
3) Should I be copying the string instead of just saving the pointer?
Excuse my ignorance as I am fairly new to Obj-C but have a C++ and C# background.
Thanks!
This is what #properties are for. They manage getter and setter code for you, so you don't have to think about these questions.
.h
#interface MyClass : NSObject
{
NSString *myString;
}
#property (nonatomic, retain) NSString *myString;
#end
.m
#implementation MyClass
#synthesize myString;
-(void)dealloc
{
[myString release];
[super dealloc];
}
#end
With those things in place, you can now talk about self.myString and not worry about memory. When you assign to it it'll do a retain. If you assign again, it'll release the first object and retain the new one. And then it'll stick around retained until your viewcontroller unloads.
You can by all means accomplish this same end with an iVar (which is what you're doing in your code sample), but then memory management is yours to handle, and it can be a bit fiddly. Best to use the #property system to create appropriately memory-managing setter code.
You have 3 optins,
Copy - should be using if the string can change or when getting called from a code you have on control of like third party
Retain - will increase the reference count to the object and will prevent destruction of it
In both these options you have to release it when you done with it
Last you can define a property with retain,copy attribute - this will let the system worry about managing it and probably the best option in most cases
I post this topic because I have a problem with my iPhone application since 3 days. I hope someone can help me because I'm going crazy.
Here is the thing : I fill in an object userXMLData,in the delegate of my application, with a XML Parser. This object contains many NSStrings and a NSMutableArrays which contains objects type Album to.
My problem is : I can display all data of userXMLData with an internal function, but when I'm trying to get the data from the array in my viewController , it doesn't work. I mean, it crashes. It's weird because I can access to the appDelegate.userXMLData.NSString but not of my appDelegate.userXMLData.NSMutableArray
Here is my code :
// Initializaiton in the delegate
userXMLData = [[UserXMLData alloc] init];
userXMLData.myArray = [[NSMutableArray alloc] init];
UserXMLData.h
#interface UserXMLData : NSObject {
// User Data
NSString *userId;
// Content
NSMutableArray *myArray;
}
#property(nonatomic, retain) NSString *myString;
#property(nonatomic, copy) NSMutableArray *myArray;
#end
//Album.h
#interface Album : NSObject {
NSString *albumId;
NSMutableArray *content;
}
#property(nonatomic, retain) NSString *albumId;
#property(nonatomic, retain) NSMutableArray *content;
#end
As I said, I really don't why it crashes. I'm stuck and I cannot continue my application without fixing it.
Enable Zombies by following the instructions here:
http://loufranco.com/blog/files/debugging-memory-iphone.html
This will cause your app to not release any objects and instead cause them to complain to the console if messages are sent to them after they are released.
The most common cause of a crash is releasing too often (or retaining too few times).
Also, running a Build and Analyze can sometimes point these out.
Would be able to answer better if you'd show the code where you are trying to access the array and the error you receive on crash, but I'd hazard a guess that you don't have #synthesize myArray in your implementation (.m) file