retain with objective-c (again but with more precision) - iphone

I post this question again but with more precision this time,
first, I have this function who return a string, there is some error about memory management or this fonction is ok?
-(NSString *) motAvecCle:(NSString *) cle
{
NSString *motRetour;
motRetour = #"";
cle = [FonctionUtile concatener:[[tableauConfig singletonTableauConfig] langueAffichage] chaine2:#"_" chaine3:[FonctionUtile trim:[cle uppercaseString]] chaine4:#""];
motRetour = [FonctionUtile trim:[dictionnaireLangue objectForKey:cle]];
if (motRetour == nil) {
motRetour = #"Erreur";
}
return motRetour;
}
and when I call this fonction,
NSString *myString = #"";
myString = [self motAvecCle:#"fr"]; // I must do this?
myString = [[self motAvecCle:#"en"]retain]; //or do this?
thx again...

The method motAvecCle: returns an object you do not own. Therefore, at some point it is going to disappear. Whether you care or not depends on where myString is defined. If it's in the same scope:
-(void) foo
{
NSString *myString = [self motAvecCle:#"fr"];
// do some stuff
}
you do not want to retain it (except in one circumstance) because the reference will disappear when foo exits which means if you had retained it you'd need to release it again first.
The one circumstance for retaining is if you modify the object you got the string from i.e. self in this case. That might cause the string to go away (although probably not in your specific example).
If myString is an instance variable of the object, you do want to retain it because otherwise it will disappear (possibly) the next time the auto release pool is drained. However, before assigning the instance variable, you must be sure to release the old value of the instance variable, unless it's actually the same string you are assigning i.e. you need to do something like this:
-(void) foo
{
NSString *tmp = [[self motAvecCle:#"fr"] retain]; // it's a string, technically you should copy, not retain
[myString release];
myString = tmp;
// do some stuff
}
Since you'd have to do that every time you want to assign the ivar, it's normal to create an accessor e.g.
-(void) setMyString: (NSString*) newValue
{
NSString* tmp = [newValue copy];
[myString release];
myString = tmp;
}
-(void) foo
{
[self setMyString: [self motAvecCle:#"fr"]];
// do some stuff
}
If you use properties, you can use #synthesize to create the accessors.

Related

how do I set the value of an NSNumber variable (without creating a new object) in objective-c

how do I set the value of an NSNumber variable (without creating a new object) in objective-c?
Background
I'm using Core Data and have a managed object that has an NSNumber (dynamic property)
passing (by reference) this to another method which will update it
not sure how to update it? if I allocate it another new NSNumber things don't work, which I guess makes sense it's then got a pointer to a different object not the core data object (I guess)
An NSNumber object isn't mutable. This means that the only way to change a property containing a NSNumber is to give it a new NSNumber. To do what you want, you have three options:
1. Pass the Core Data object to the method and have it directly set the property.
- (void)updateNumberOf:(MyCoreDataObject *)theObject {
NSNumber *newNumber = ...; // create new number
theObject.number = newNumber;
}
Called as [self updateNumberOf:theCoreDataObject];
2. Have the update method return a new NSNumber and update it in the caller.
- (NSNumber *)updateNumber:(NSNumber *)oldNumber {
NSNumber *newNumber = ...; // create new number
return newNumber;
}
Called using:
NSNumber *theNumber = theCoreDataObject.number;
theNumber = [self updateNumber:theNumber];
theCoreDataObject.number = theNumber;
3. Pass a pointer to a number variable and update it in the caller (I would only suggest this over option 2 if you need to return something else).
- (void)updateNumber:(NSNumber **)numberPointer {
if(!numberPointer) return; // or possibly throw an error
NSNumber *oldNumber = *numberPointer;
NSNumber *newNumber = ...; // create new number
*numberPointer = newNumber;
}
Called using:
NSNumber *theNumber = theCoreDataObject.number;
[self updateNumber:&theNumber];
theCoreDataObject.number = theNumber;
I did not bother with memory management in any of these examples. Make sure you release/autorelease objects appropriately.
4. (from Greg's comment) Similar to option 1, but passes the key to the update method to be more portable.
- (void)updateNumberOf:(id)theObject forKey:(NSString *)key {
NSNumber *oldNumber = [theObject valueForKey:key];
NSNumber *newNumber = ...; // create new number
[theObject setValue:newNumber forKey:key];
}
Called as [self updateNumberOf:theCoreDataObject forKey:#"number"];

iPhone - NSMutableArray inside a Custom Object. When to release?

When should I be releasing [self.activeLocations], which is an NSMutableArray inside my custom object? I am also getting memory leaks in initWithValue.
Also the Location object below. Am I calling this and releasing this properly?
Method in Custom Object.m:
- (id)initWithValue:(NSString *)value {
if ((self = [super init])) {
self.couponId = [value valueForKey:#"couponId"];
self.couponName = [value valueForKeyPath:#"couponName"];
self.qrCode = [value valueForKeyPath:#"description"];
self.companyName = [value valueForKeyPath:#"companyName"];
self.categoryName = [value valueForKeyPath:#"categoryName"];
self.distance = [value valueForKeyPath:#"distance"];
NSDictionary *activeLocationsDict = [value valueForKeyPath:#"activeLocations"];
//self.activeLocations = [[[NSMutableArray alloc] init] autorelease];
self.activeLocations = [NSMutableArray array];
for (id val in activeLocationsDict) {
// Add JSON objects to array.
Location *l = [[Location alloc] initWithValue:val];
[self.activeLocations addObject:l];
[l release];
}
}
return self;
}
- (void)dealloc {
[super dealloc];
couponId = nil;
couponName = nil;
qrCode = nil;
companyName = nil;
categoryName = nil;
distance = nil;
activeLocations = nil;
}
My Custom Object.h
#interface Coupon : NSObject {
NSNumber *couponId;
NSString *couponName;
NSString *qrCode;
NSString *companyName;
NSString *categoryName;
NSString *distance;
NSMutableArray *activeLocations;
}
#property (nonatomic, copy) NSNumber *couponId;
#property (nonatomic, copy) NSString *couponName;
#property (nonatomic, copy) NSString *qrCode;
#property (nonatomic, copy) NSString *companyName;
#property (nonatomic, copy) NSString *categoryName;
#property (nonatomic, copy) NSString *distance;
#property (nonatomic, retain) NSMutableArray *activeLocations;
- (id)initWithValue:(NSString *)value;
This is how I'm using the above initWithValue:
- (NSMutableArray *)createArrayOfCoupons:(NSString *)value {
NSDictionary *responseJSON = [value JSONValue];
// Loop through key value pairs in JSON response.
//NSMutableArray *couponsArray = [[NSMutableArray alloc] init] autorelease];
NSMutableArray *couponsArray = [NSMutableArray array];
for (id val in responseJSON) {
// Add JSON objects to array.
Coupon *c = [[Coupon alloc] initWithValue:val];
[couponsArray addObject:c];
[c release];
}
return couponsArray;
}
I get memory leaks on initWithValue in the above method as well...
Location Custom Object:
- (id)initWithValue:(NSString *)value {
if ((self = [super init])) {
self.locationId = [value valueForKeyPath:#"locationId"];
self.companyName = [value valueForKeyPath:#"companyName"];
self.street1 = [value valueForKeyPath:#"street1"];
self.street2 = [value valueForKeyPath:#"street2"];
self.suburb = [value valueForKeyPath:#"suburb"];
self.state = [value valueForKeyPath:#"state"];
self.postcode = [value valueForKeyPath:#"postcode"];
self.phoneNo = [value valueForKeyPath:#"phoneNo"];
self.latitude = [value valueForKeyPath:#"latitude"];
self.longitude = [value valueForKeyPath:#"longitude"];
}
return self;
}
- (void)dealloc {
[super dealloc];
locationId = nil;
companyName = nil;
street1 = nil;
street2 = nil;
suburb = nil;
state = nil;
postcode = nil;
phoneNo = nil;
latitude = nil;
longitude = nil;
}
- (id)init {
....
}
Get rid of this. It does nothing.
- (id)initWithValue:(NSString *)value {
[super init];
There's a specific pattern you should use for initialization:
- (id)initWithValue:(NSString *)value {
if (( self = [super init] )) {
// everything except the return
}
return self;
}
Finally, to answer your actual question, assuming you're using retain with your property, there's two places you'll need to release.
Here's the first:
self.activeLocations = [[NSMutableArray alloc] init];
Why: [[NSMutableArray alloc] init] makes your code own the object by retaining it. But the property set also claims ownership by retaining it. You don't really want this NSMutableArray owned by the code and your custom object, you want it owned by your object.
My suggestion is to just use this:
self.activeLocations = [NSMutableArray array];
The second place is in your dealloc:
- (void)dealloc {
self.activeLocations = nil;
// ...and everything else you've set as a property using retain
[super dealloc];
}
(Personally, I've gone back and forth on whether to use dot notation in dealloc rather than [activeLocations release];. I'm favouring setting to nil using the property now, which puts all the memory management rules in a single location.)
Apple has a great document on memory management you should read: Memory Management Programming Guide: Object Ownership and Disposal.
First of all, your overridden -init method is completely unnecessary because by default when a method is invoked, the runtime will perform an upward traversal of the inheritance hierarchy until the specified method is found, so it will find NSObject's -init method and invoke it.
Second, you should invoke release on all of your owned properties (ones with copy or retain) in your overridden -dealloc method.
Third, in your case, when you call the property setter passing in an object that is already owned locally, you must send the object the release message after invoking the setter to correctly hand off ownership of the object to the receiver.
There are two ways to do this:
One way is to create an object that you own using alloc, copy or new, and then invoke the property setter, passing in that object, then send it the release message.
Another way is to pass in an autoreleased object to the property setter, which will then retain or copy its argument and thereby obtain ownership
The answer to when you should be releasing it is a question of whether or not the activeLocations array and all the elements in that array (remember each element in the array is retained by the array itself) are necessary throughout the lifetime of the Location object.
If you use the activeLocations array for some temporary purpose, for example in a method or chain of methods, then don't need it again, or you plan to refresh its members at some later time when you need it next, then it makes sense to release the array (and its elements, which is automatic) when you're done using it, in whatever function last uses the array. You will use the convention
self.activeLocations = nil;
to let the runtime system release the array and set the member to nil.
If, on the other hand, the activeLocations array data is mandatory for the Locations object to function and must exist as long as the Location object exists, then you will want to release the array inside the dealloc method of the Location object, for example:
- (void) dealloc {
[activeLocations release];
[super dealloc];
}
As it happens, you're pretty much always going to want to release member objects such as activeLocations in a dealloc method. This ensures that when the Location object is released the members it contains are cleaned up. Remember that Objective-C does not call methods on null pointers, so if you have previously set activeLocations to nil the call in dealloc is a safe no-op.
Given then that you'll always set things up to release in dealloc, now you really just have to ask yourself if you need a release/recreate phase somewhere in your object lifecycle (again, determined by frequency-of-use requirements).
It depends on what you're asking. In the initWithValue: method that you've shared, you are double-retaining the array. It should be released or autoreleased once within initWithValue:.
The array should be released a second time in the custom object's dealloc method.

IPhone - copyWithZone leak

Testing my app on the device it returns a leak whe i call the copy of a custom object ande i can't understand why.
this is the call:
NSMutableArray *arr = [[NSMutableArray alloc] initWithCapacity:5];
for (SinglePart *sp in [copyFrom partList]) {
[arr addObject:[sp copy]];
}
self.partList = arr;
[arr release];
this is the method:
- (id)copyWithZone:(NSZone *)zone {
SinglePart *copy = [[[self class] allocWithZone:zone] initWithSinglePart:self];
[copy loadImage];
return copy;
}
this is the method that is called by copyWithZone:
- (id)initWithSinglePart:(SinglePart *)copyFrom {
if (self = [super init]) {
self.imagePath = [copyFrom.imagePath copy];
self.color = [UIColor colorWithCGColor:copyFrom.color.CGColor];
self.hasOwnColor = copyFrom.hasOwnColor;
self.blendingMode = copyFrom.blendingMode;
}
return self;
}
copy returns a new object with retain count 1. Meaning you need to release the new object, which you are not doing.
NSMutableArray *arr = [[NSMutableArray alloc] initWithCapacity:5];
for (SinglePart *sp in [copyFrom partList]) {
SingPart *theCopy = [sp copy];
[arr addObject:theCopy];
[theCopy release];
}
self.partList = arr;
[arr release];
Even your custom copyWithZone: method inits an object, but does not autorelease it, which is the expected behavior of a copy method. Copy must be balanced just like a retain or init, meaning you must balance it with release at some point.
Lastly, your initWithSinglePart: method leaks the imagePath as well. In this case if you declare the imagePath property as copy instead of retain then you don't need to do this manually at all. Then you simply assign the value and let the property setter do it for you.
// Header
#property (copy) NSString *imagePath;
// Now this will do the copy for you
self.imagePath = copyFrom.imagePath;
Also, is the property imagePath defined with retain or copy semantics?
If so you need to add an autorelease here:
self.imagePath = [[copyFrom.imagePath copy] autorelease];
because the default setter will retain/copy it too.
So, you either need to autorelease, or omit the "self." to bypass the default setter.
You are making a copy of sp and then adding it to the array. The array then retains the object so your retain count is now 2.
In the end you release arr, thus making the retain count of it's items 1.
You should either add another release to the sp objects, or not use copy.
Try this:
self.partList = [NSMutableArray arrayWithCapacity:5];
for (SinglePart *sp in [copyFrom partList]) {
[arr addObject:sp];
}

variable parameter function - EXC_BAD_ACCESS when calling [obj release];

I have the following method:
(void)makeString:(NSString *)str1,... {
va_list strings;
NSString *innerText = [[NSString alloc] init];
NSString *tmpStr = [[NSString alloc] init];
if (str1) {
va_start(strings, str1);
while (tmpStr = va_arg(strings, id)) {
innerText = [innerText stringByAppendingString:tmpStr];
}
label.text = [str1 stringByAppendingString:innerText];
}
[tmpStr release];
}
I will eventually get to Objective C Memory Management reading, where I'm sure I will find the answer to this - probably related to pointers and copying - , but for now, can anyone explain why if I add [innerText release]; as the last line of this function, i get an EXC_BAD_ACCESS error at runtime?
First, your code is erroneous.
As far as I can see you are only concatenating the strings to assign the result to label.text.
I assume that label is an ivar, so label.text = … ist legal. Then the following should do it:
- (void)makeString: (NSString *)str1, ...
{
if (str1) {
NSString *tmpStr;
va_list strings;
va_start(strings, str1);
while (tmpStr = va_arg(strings, id)) {
str1 = [str1 stringByAppendingString: tmpStr];
}
label.text = str1;
}
}
Some notes:
You should not release any input parameter unless your method is about releasing something.
As the first answer stated, you should not release the result of stringByAppendingString: unless
you have retained it before.
[Update]
I changed the answer because it contained an error. label.text = str1 should retain str1 of course (if it wants to keep it). Especially the calling code should not retain str1 unless it wants to keep it for itself.
stringByAppendingString returns an autoreleased string, which is replacing your original assignment. So your release is not needed. But you are leaking memory with the two allocs above.
You should probably use [NSString initWithCString:va_arg(strings, id)] to assign the tmpStr too.

Please help me in solving retain mystery in iPhone?

Please consider the following code:
//CallerClass.h
#interface CallerClass : UITableViewController {
NSMutableArray *dataArray;
}
#property(nonatomic,retain) NSMutableArray *dataArray;
-(void) setData;
//CallerClass.m
#implementation CallerClass
#synthesize dataArray;
-(id)initWithStyle:(UITableViewStyle)style {
if (self = [super initWithStyle:style]) {
[self setData];
}
return self;
}
-(void) setData
{
dataArray = [CalledClass getData];
[dataArray release];
}
//CalledClass.h
#interface CalledClass : NSObject {
}
+(NSMutableArray*) getData;
//CalledClass.m
#implementation CalledClass
+(NSMutableArray*) getData
{
NSMutableArray* tempArray = [[NSMutableArray alloc] init];
return tempArray;
}
I want to know what is the retain count for dataArray that is tempArray. Is it getting released. I dont want to use autorelease as I dont know till what time period I will need it. So I want to release it on my own. When I allocated tempArray, its retain count becomes 1. But when I assign it to instance variable dataArray whose property is retain, Is the retain count for that array becomes 2 or it stays 1. Like on releasing dataArray will it release memory.
You've set up your property to retain the value, but then you're not using the accessor methods but set the instance variable directly instead:
dataArray = [CalledClass getData];
won't manage the retain count for you. You have to use:
self.dataArray = [CalledClass getData];
Also, in your CalledClass, I'd change the getData method to this:
+(NSMutableArray*) getData
{
NSMutableArray* tempArray = [[NSMutableArray alloc] init];
return [tempArray autorelease];
}
Normally I'd expect to get an autoreleased object back from a method with a name like this.
setData: should then be something like:
-(void) setData
{
self.dataArray = [CalledClass getData];
}
or you could get rid of it entirely and just directly do
self.dataArray = [CalledClass getData]
in initWithStyle:.
By calling self.dataArray instead of assigning directly to the instance variable, your dataArray property will take care of retaining and releasing the object (because you specified "retain" in the property declaration)
dataArray = [CalledClass getData];
That doesn't invoke the retain attribute of the property. That's just plain old assignment iirc. [self setDataArray:[CalledClass getData]] would give a reference count of 2 on your array.