What is difference between initilizing an NSString simply and with retain? - ios

I want to know that difference between following two lines
name1 = [[NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement,1)] retain];
name1 = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement,1)];
What will be effect on name1 if I do use retain at the last,
I face once problem, and couldn't use name1 in a method that is being call by NSTimer, and when I use retain, they it worked fine for me.
If I do call value from database in viewDidLoad, and use in a method that is being called by NSTimer after each second, then it will give bad-exec, but when I do use retain then it will work properly,
I want to know the reason

Here is the difference
- (void)func1 {
name1 = [[NSString stringWithUTF8String:...] retain];
name2 = [NSString stringWithUTF8String:...];
}
- (void)func2 {
NSLog(#"%#", name1); //OK, name1 is still there
NSLog(#"%#", name2); //Would be crashed because name2 could be released anytime after func1 is finished.
}

I wrote this answer to another question, but it explains what you're asking:
Objects in objective c have a retain count. If this retain count is greater that 0 when the object goes out of scope (when you stop using it), it leaks.
The following things increase the retain count
[[alloc] init]
new
copy
[retain]
adding an object to an array
adding an object as a child (e.g. views)
There are likely more, but you don't appear to use any others in your code
The following decrease the retain count
[release]
removing an object from an array
if you dealloc an array, all of its objects are released
You should go through your code and ensure each of the retains or additions to an array are matched with a corresponding release. (You can release member variables in the dealloc method).
Another user made a valid point that my answer doesn't
Once you add an object to an array, it takes ownership and will release the object when it is done with it. All you need to do is make sure you release anything you own according to the memory management rules
There are also autorelease objects, have a look at this example;
-(init){
...
stagePickerArray = [[NSMutableArray alloc] init];
for (int i = 0; i < 3; i++)
{
//this string is autoreleased, you don't have call release on it.
//methods with the format [CLASS CLASSwithsomething] tend to be autorelease
NSString *s = [NSString stringWithFormat:#"%d", i);
[stagePickerArray addObject:s];
}
...
}
Your issue is that when you come to use your string later, it has a retain count of zero and has been released. By calling retain on it, you're saying 'I want to use this later'. Don't forget to match every retain with a release or you're objects will 'leak'

I bet your code wouldn't crash if your name1 was a property - either (nonatomic, retain) or just (copy) depending on your needs.
Second condition is to have name1 initialized to sth meaningful at the time your other function tries to do sth with it.
EDIT:
With a property you'd have to use synthesized setter in this case with: self.name1 = #"your string";.
Normally you don't have to manually retain/release a string created with stringWith... methods since there's nothing you created in memory yourself by using explicit alloc. Also please note that with code:
NSString *str = [NSString stringWithUTF8String:#"your string"];
your str (if not used to set a property) will stop being available when the function gets out of scope (iOS eventloop will autorelease it).

Related

How reference count work? [duplicate]

Here is code I am referring to.
// Person.h
#interface Person : NSObject {
NSString *firstName;
NSString *lastName;
}
#end
// Person.m
#implementation Person
- (id)init {
if (![super init]) return nil;
firstName = #"John";
lastName = #"Doe";
}
#end
// MyClass.m
#implementation MyClass
.....
- (NSArray *)getPeople {
NSMutableArray *array = [[NSMutableArray alloc] init];
int i;
for (i = 0; i < 10; i++) {
Person *p = [[Person alloc] init];
[array addObject:p];
}
return array;
}
.....
#end
Now, I know there is no memory-management going on in this sample code. What would be required?
In the getPeople loop, I am alloc'ing a Person (retainCount 1), then adding it to array. The retain count is now 2, right? If it is two, should I be [p release]'ing after adding it to the array, bringing the retainCount back down to 1?
Am I right in that it is the caller's responsibility to release the array returned by the method? (Which would also free the memory of the Person's, and their instance variables, assuming their counts are at 1).
I have read Apple's memory management document, but I guess what I am most unclear about, is what increases an objects retain count? I think I grasp the idea of who's responsibility it is to release, though. This is the fundamental rule, according to Apple:
You take ownership of an object if you create it using a method whose name begins with “alloc” or “new” or contains “copy” (for example, alloc, newObject, or mutableCopy), or if you send it a retain message. You are responsible for relinquishing ownership of objects you own using release or autorelease. Any other time you receive an object, you must not release it.
bobDevil's sentence "only worry about the retain counts you add to the item explicitly" made it click for me. After reading the Ownership policy at Apple, essentially, the object/method that created the new object, is the one responsible for releasing /it's/ interest in it. Is this correct?
Now, let's say I a method, that receives an object, and assigns it to a instance variable. I need to retain the received object correct, as I still have an interest in it?
If any of this is incorrect, let me know.
You are correct that the retain count is 2 after adding it to an array. However, you should only worry about the retain counts you add to the item explicitly.
Retaining an object is a contract that says "I'm not done with you, don't go away." A basic rule of thumb (there are exceptions, but they are usually documented) is that you own the object when you alloc an object, or create a copy. This means you're given the object with a retain count of 1(not autoreleased). In those two cases, you should release it when you are done. Additionally, if you ever explicitly retain an object, you must release it.
So, to be specific to your example, when you create the Person, you have one retain count on it. You add it to an array (which does whatever with it, you don't care) and then you're done with the Person, so you release it:
Person *p = [[Person alloc] init]; //retain 1, for you
[array addObject:p]; //array deals with p however it wants
[p release]; //you're done, so release it
Also, as I said above, you only own the object during alloc or copy generally, so to be consistent with that on the other side of things, you should return the array autoreleased, so that the caller of the getPeople method does not own it.
return [array autorelease];
Edit:
Correct, if you create it, you must release it. If you invest interest in it (through retain) you must release it.
Retain counts are increased when you call alloc specifically, so you'll need to release that explicitly.
factory methods usually give you an autoreleased object (such as [NSMutableArray array] -- you would have to specifically retain this to keep it around for any length of time.).
As far as NSArray and NSMutableArray addObject:, someone else will have to comment. I believe that you treat a classes as black boxes in terms of how they handle their own memory management as a design pattern, so you would never explicitly release something that you have passed into NSArray. When it gets destroyed, its supposed to handle decrementing the retain count itself.
You can also get a somewhat implicit retain if you declare your ivars as properties like #property (retain) suchAndSuchIvar, and use #synthesize in your implementation. Synthesize basically creates setters and getters for you, and if you call out (retain) specifically, the setter is going to retain the object passed in to it. Its not always immediately obvious, because the setters can be structured like this:
Person fart = [[Person alloc] init];
fart.firstName = #"Josh"; // this is actually a setter, not accessing the ivar
// equivalent to [fart setFirstName: #"Josh"], such that
// retainCount++
Edit:
And as far as the memory management, as soon as you add the object to the array, you're done with it... so:
for (i = 0; i < 10; i++) {
Person *p = [[Person alloc] init];
[array addObject:p];
[p release];
}
Josh
You should generally /not/ be worried about the retain count. That's internally implemented. You should only care about whether you want to "own" an object by retaining it. In the code above, the array should own the object, not you (outside of the loop you don't even have reference to it except through the array). Because you own [[Person alloc] init], you then have to release it.
Thus
Person *p = [[Person alloc] init];
[array addObject:p];
[p release];
Also, the caller of "getPeople" should not own the array. This is the convention. You should autorelease it first.
NSMutableArray *array = [[[NSMutableArray alloc] init] autorelease];
You'll want to read Apple's documentation on memory management: http://developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html

iOS: Reference counting

I am doing some research on reference count increase. Please help on finding it.
Below is sample code and research i'm doing what would happen of reference counting for each line below.
.h file
NSArray *tempArray;
#property (nonatomic, retain) NSArray *tempArray;
.m file
#synthesize tempArray;
-(void) sampleFunction
{
NSArray *myArray = [[NSArray alloc] init]; // Thinking reference count increases to "1"
tempArray = myArray;// reference count increases and tempArray gets retain count "1" now.
tempArray = myArray;// reference count increases and tempArray gets retain count "2" now.
tempArray = [NSArray arrayWithObject:#"SomeString"]; // retain count = ?
}
I know this code may not be for functioning, but this is for only researching about what will happen on reference counting for such scenarios. I tried printing retainCount, but it doesn't show the correct result. Please advise me how does the reference count works on this each line?
In lines 2, 3 and 4 you are affecting the instance variable tempArray to the same object as myArray. But if you write it this way, you try to affect an instance variable. As a matter of fact, if you didn't write any #synthesize tempArray or #synthesize tempArray = tempArray in your code, by default the instance variable generated automatically to store the property value is the same name as the property name, but prefixed with an underscore. So as the property name is tempArray, the instance variable is named _tempArray. The instance variable tempArray itself does not exist and your line of code is invalid.
So if we suppose you wrote instead:
-(void) sampleFunction
{
NSArray *myArray = [[NSArray alloc] init]; // (1)
self.tempArray = myArray; // (2)
self.tempArray = myArray; // (3)
self.tempArray = [NSArray arrayWithObject:#"SomeString"]; // (4)
}
In (1) you are creating a brand new instance of NSArray. "alloc" always initialize new instance with a reference count of 1
In (2) you write self.tempArray = myArray (which is equivalent to [self setTempArray:myArray]; and thus call the property setter), so you set the property to point to the same array you created in (1). This array is thus retained by the property, and its retainCount increses by one, because it is retained by myArray and by the self.tempArray property.
In (3) you affect the property to the very same object as before. This the ref count does not change at all. You could understand that as if you replaced the value of the self.tempArray with another value, so the setter of the property release the old value (decrementing its ref count), then retain the new value (thus incrementing its ref count). As in your case the old and new values are the same object, you would decrement the ref count of your array then re-increment it again. In practice, the ref count does not even change at all (instead of decrementing+incrementing again) to avoid any potential dealloc of the object, because the default implementation of a property setter is as follow:
-(void)setTempArray:(NSArray*)newValue
{
// check if old and new value are different. Only do sthg if they are different
if (newValue != _tempArray)
{
[_tempArray release]; // release old value
[newValue retain]; // retain new value
_tempArray = newValue; // store new value in the backing variable associated with the property
}
}
In (4) you replace again the value of the property tempArray, but this time with a completely new object. So the property will release its old value and retain the new one. Thus the first array you created in (1) which had a refcount of 2 (retained by myArray and by self.tempArray) decrease its ref count to 1 (because the property won't retain it anymore), and the new instance you created [NSArray arrayWithObject:#"SomeString"] is retained by the property, so its ref count is +1.
If you replaced self.tempArray = ... (so the use of the property) with the direct use of the instance variable, using instance variables don't retain the objects they are affected to (except if you are using ARC but it seems you don't), so the ref count of the object wouldn't have changed at all in (2), (3) and (4).
Actually, retain count increase only in new, alloc, retain and copy condition but if we are providing ownership to an object through this for retain count will increase other than that there is no possibility to increase retain count.
First things first, don't even try to rely upon retainCount.
After that: you're wondering which scenario happens among the ones you enumerated. Well, neither one.
Why? Because, in first palce, you're assigning to an instance variable directly - that won't change retain count. At all. Except if you use ARC, but it seems you don't.
You probably wanted to assign stuff to the property of the object, that is, write
self.tempArray = myArray;
etc. Now because the property itself (and not its backing ivar!) is declared as retain, the corresponding accessor method will increase the reference count of the object being assigned to the property. However, in order not to leak memory, an accessor method is usually implemented by releasing the previously assigned object when assigning and thus retaining the new one, i. e.
- (void)setTempArray:(NSArray *)tmp
{
[tmp retain];
[tempArray release];
tempArray = tmp;
}
So basically, when you reassign myArray to the self.tempArray property, it looses and gains a reference, thus its reference count doesn't chnage at all.
When you assign another, new array to the property, then again myArray loses a refcount, dropping to 0 it is deallocated, then the new array, created using + [NSArray arrayWithObject:] is retained. Its exact reference count is supposed to be 1 after this, since it was created using alloc - init - autorelease (that's how the method is implemented), and it has been retained by the property. However, the value returned by - retainCount is still (and never) to be relied upon.
In your particular example, you are assigning to tempArray directly and not self.tempArray, so the retainCount will stay at 1 throughout. But let's go through what would happen if you did what I think you meant.
In objective-c, a synthesized retained property will have a setter functionally equivalent to this:
-(void) setTempArray:(NSArray *value)
{
if(tempArray != value) {
[tempArray release];
tempArray = [value retain];
}
}
This increases the retain count when a new object is assigned to it, essentially does nothing when it is set to the same object, and releases it when something else is assigned to it. So the retain counts in your example go something like this:
-(void) sampleFunction
{
NSArray *myArray = [[NSArray alloc] init]; // Retain count of 1
self.tempArray = myArray; // 2
self.tempArray = myArray; // still 2
self.tempArray = [NSArray arrayWithObject:#"SomeString"];
// myArray.retainCount is 1,
// tempArray.retainCount is 2 but with 1 autorelease
// myArray leaks
}

Incorrect decrement of reference count not owned at this point

I don't understand this one unless it's because I'm releasing the property instead of the ivar. Can someone shed light on the problem?
self.dataToBeLoaded = [[NSMutableData alloc] initWithLength:10000];
[self.dataToBeLoaded release];
The warning is Incorrect decrement of the reference count of an object that is not owned by the caller.
The dataToBeLoaded property has the retain attribute associated with its setter.
My understanding is the the alloc init increments the retain count and the property assignment increments the retain count. Since I only one to retain it once, that's why I release it immediately after the assignment.
UPDATE -- some experimental results:
Since I noted in my comments below that I have received contradictory advice on what the retain property does to the synthesized setter, I thought I would do a little experiment using the code above, modified with some logging:
NSLog(#"retain 1 = %d", [dataToBeLoaded_ retainCount]);
self.dataToBeLoaded = [[NSMutableData alloc] initWithLength:10000];
NSLog(#"retain 2 = %d", [dataToBeLoaded_ retainCount]);
[self.dataToBeLoaded release];
NSLog(#"retain 3 = %d", [dataToBeLoaded_ retainCount]);
The results at each log statement were 0, 2, and 1.
Apparently, it's not possible to step into the alloc or the init code to see the retain count go from 0 to 1 to 2. I could have subclassed the NSMutableData class, but I was short on time.
I know a lot is said that you can't rely on the value of the retainCount property, but what I have seems consistent and I would expect reasonable behavior over the short scope of the code like that shown in the example. So I'm inclined to believe that prior advice is correct -- the retain property is a promise to include a retain within the setter. So here I have the retain from the alloc/init and the retain from the call to the setter. Hence, the retain count is set to 2.
When I run this code:
NSMutableData *theData;
NSLog(#"retain 1 = %d", [theData retainCount]);
theData= [[NSMutableData alloc] initWithLength:10000];
NSLog(#"retain 1a = %d", [theData retainCount]);
self.dataToBeLoaded = theData;
NSLog(#"retain 2 = %d", [theData retainCount]);
[self.dataToBeLoaded release];
NSLog(#"retain 3 = %d", [theData retainCount]);
The retain count at each log statement is 0, 1, 2, 1.
So I have evidence that suggests the setter is providing a retain. This appear to be more of a promise than a hint, because it is actually happening.
I'm open to other explanations. I don't want to be arrogant about this. I just want to get it right as to what is happening. I appears that the warning (in the subject of this question) is really spurious and not something to worry about.
One more experiment is done using assign rather than retain as an attribute in the #property statement. With the same code:
NSMutableData *theData;
NSLog(#"retain 1 = %d", [theData retainCount]);
theData= [[NSMutableData alloc] initWithLength:10000];
NSLog(#"retain 1a = %d", [theData retainCount]);
self.dataToBeLoaded = theData;
NSLog(#"retain 2 = %d", [theData retainCount]);
[self.dataToBeLoaded release];
NSLog(#"retain 3 = %d", [theData retainCount]);
The retain count at each log is 0, 1, 1 (the setter did not retain), then the error message: message sent to deallocated instance. The last release had set the retain count to zero, which triggered the deallocation.
UPDATE 2
A final update -- when the synthesized setter is overridden with your own code, the retain attribute is no longer observed unless your setter explicitly includes it. Apparently (and this contradicts what I had been told in other threads here) you have to include your own retain in the setter if that's what you want. While I didn't test it here, you probably need to release the old instance first, or it will be leaked.
This custom setter no longer has the property attributes of the #propety declaration:
- (void) setDataToBeLoaded:(NSMutableData *)dataToBeLoaded {
dataToBeLoaded_ = dataToBeLoaded;
}
This makes sense. Override a synthesized setter and you override all of the declared properties. Use a synthesized setter, and the declared properties are observed in the synthesized implementation.
The #property attributes represent a "promise" as to how the synthesized setter is implemented. Once you write a custom setter, you're on your own.
The key is to think through what the below code is doing. I'll write it out in full for clarity:
[self setDataToBeLoaded:[[NSMutableData alloc] initWithLength:10000]];
This creates an object with a +1 retain count and passes it to setDataToBeLoaded:. (*) It then throws away its reference to that object, leaking it.
[[self dataToBeLoaded] release];
This calls dataToBeLoaded and releases the object returned. There is no promise whatsoever that the object returned by dataToBeLoaded is the same as the object passed to setDataToBeLoaded:. You probably think they're the same, and looking at your code you can probably convince yourself that it will always work out that way, but that's not an API promise.
The code posted by Antwan is correct:
NSMutableData *data = [[NSMutableData alloc] initWithLength:1000];
self.dataToBeLoaded = data;
[data release];
That creates an object with a +1 retain count. Then passes it to a method, then releases it.
Or, if you're willing to use the autorelease pool, you can simplify it to:
self.dataToBeLoaded = [NSMutableData dataWithLength:1000];
(*) Technically this passes a message to self that may or may not cause this method to be called, but that muddies the issue. For most purposes, pretend it's a method call. But do not pretend that it just sets the property. It really is going to call some method.
EDIT:
Maybe this code will make the issue a little clearer. It's indicative of common caching solutions:
.h
#interface MYObject : NSObject
#property (nonatomic, readwrite, strong) NSString *stuff;
#end
.m
#interface MYObject ()
#property (nonatomic, readwrite, weak) MYStuffManager *manager;
#implementation MYObject
... Initialize manager ...
- (NSString*)stuff {
return [self.manager stuffForObject:self];
}
- (void)setStuff:(NSString *)stuff {
[self.manager setStuff:stuff forObject:self];
}
Now maybe manager does some foolery in the background. Maybe it caches various copies of stuff. Maybe it copies them. Maybe it wraps them into other objects. What's important is that you can't rely on -stuff always returning the same object you passed to -setStuff:. So you certainly shouldn't release it.
Note that nothing in the header indicates this, and nothing should. It's not the callers' business. But if the caller releases the result of -stuff, then you will get hard-to-debug crashes.
#synthesize is just a shorthand for writing some tedious code (code that implements stuff and setStuff: as reading and writing an ivar). But nothing says that you have to use #synthesize for your properties.
My guess would be that the method
- (NSMutableData *)dataToBeLoaded;
does not contain any of the memory management keywords therefore it is assumed that you do not own the data returned and therefore should not be releasing it.
Either use
NSMutableData *data = [[NSMutableData alloc] initWithLength:1000];
self.dataToBeLoaded = data;
[data release]; data = nil;
or if you can why not lazy load it when you actually need it?
- (NSMutableData *)dataToBeLoaded;
{
if (!_dataToBeLoaded) {
_dataToBeLoaded = [[NSMutableData alloc] initWithLength:1000];
}
return _dataToBeLoaded;
}
It just means that you are releasing an object you don't own.
I'd say call it with the instance var directly instead of using the getter, but not sure whether that will fix your analyses warnings. Also why not use [NSMutableData dataWithLength:1000]; which is autoreleased and therefore eliminates the need of that extra release call ( and would probably get rid of that warning too! )
other ways you could fix it:
NSMutableData *data = [[NSMutableData alloc] initWithLength:1000];
self.databToBeLoaded = data;
[data release];
I provided a couple of updates that I think answer what is going on here. With some test results, my conclusion is that this warning is spurious, meaning it does not really identify improper code. The updates should speak for themselves. They are given above.

Need clarification in iPhone memory management

I need few clarification in iPhone memory management.
Here is an example for setters;
1).
-(void)setValue:(NSString*)input{
[value autorelease];
value = [input retain];
}
In this example, why we have to use autorelease?
Can we use like as follows?
if(value)
[value release];
value = [input retain];
In the first example, Why we should not release the memory for input
2).
If I use following statement; what is the retain count for value
NSString *value;
value = #"welcome";
After the above statement, just I am trying to set one more value. Then what will happen?
eg:
value = #"For testing";
3).
What is the different between 2) and 3)?
NSString *value;
value = [input1 retain];
...
...
value = [input2 retain];// Now what is the retain count for value
4).
If I use following statement, why the app is getting crash?
NSString *value = [[[NSString alloc] init] autorelease];
...
...
Thanks in advance..
If "input" is the exact same object as "value" then calling [value release] could dealloc the object. So you must retain the new input value, release the old value, then assign the new value to the ivar:
[input retain];
[value release];
value = input;
After each of 2) and 3), the NSString *value points to a literal NSString object, the retain count will be 1 in each case, and releasing it is probably not a good idea
After this code:
value = [input2 retain];
value is an alias to the input2 object. The thing to realize is that objects have retain counts, variables do not.
As for your last case,
NSString *value = [[[NSString alloc] init] autorelease];
It creates an autoreleased empty string. If you reference that object again once the autorelease actually happens, you may get a crash because you'll be referring to an object that doesn't exist any more.
If you release a value before you retain the new value then you can have problems if the same value is being set twice. This happens if the caller hasn't retained their own copy, such as when they get the value from the same object they try to set it on, like this:
object.value = object.value;
That statement will cause the object to be released before it's retained again which could lead to the memory being deallocated and result in a dangling pointer being retained. By doing the autorelease it ensures that copying the same pointer onto itself will work correctly.
I usually write my setters as
- (void)setValue:(NString *)input {
if (value != input) {
[value release];
value = [input retain];
}
}
which avoids the problem of input and value both being the same object. If you just release it without checking then you might completely free it and the next line will try to retain an object that doesn't exist anymore.
However, it's best practice to copy strings instead of retaining them :)
(assuming you're working in a non-gc env)
1) you can create a temporary variable and release the temp after the retain/assign. otherwise, you'd need to compare the pointers. deferred release may also mask threading errors (whether you consider that good or bad is...)
2) technically, the NSString literals are valid for the life of your program. that is: while (1) [#"why won't i die?" release]; yields an infinite loop.
3) with explicit retain, alloc+init, new, and copy, you must counterbalance the retain count using release or autorelease. since you did not release value1, the static analysis may (correctly) spot that as a leak. since the string constants never die, the two aren't comparable in this regard.
4) there's nothing wrong with that staement. the problem lies elsewhere in your program. either you are assigning it to an ivar without retaining, or releasing it later.
try using static analysis often, and try reducing how much you use autorelease (you can't avoid it entirely).
none of this is magic, but you can at least reduce the location of many problems to the callsites (or very close) by not using autorelease all over the place. just use manual retain/release where possible.
lastly, check for leaks and run with NSZombies enabled.
1)
You will have to do autorelease because of the following:
When input is the same object as value and you will release value it's retain count will reach zero and get deallocated before you can retain it again though the input.
You can do it with retain but you have to change your code:
-(void)setValue:(NSString*)input{
if (value != input) {
[value autorelease];
value = [input retain];
}
}
2)
I believe #"Text" will be treated as a constant. When you want a object which you do not want any memory management with use:
NSString *value = [NSString stringWithString:#"Text"];
This will return an autoreleased object.
3) In this example it is not about the retain count of value, but about the retain count of both objects where value one is referenced to.
When you dont release input1 before you leave that method, you will have a memory management problem.
4) This statement should work. No points to argue. You rather use [NSString string].
Note:
For memory management: when you use alloc new or copy, you also have to use release or autorelease on the same object in the same scope.

Objective C NSString* property retain count oddity

I have the following example class:
Test.h:
#interface Test : UIButton {
NSString *value;
}
- (id)initWithValue:(NSString *)newValue;
#property(copy) NSString *value;
Test.m:
#implementation Test
#synthesize value;
- (id)initWithValue:(NSString *)newValue {
[super init];
NSLog(#"before nil value has retain count of %d", [value retainCount]);
value = nil;
NSLog(#"on nil value has retain count of %d", [value retainCount]);
value = newValue;
NSLog(#"after init value has retain count of %d", [value retainCount]);
return self;
}
Which produces the following output:
2008-12-31 09:31:41.755 Concentration[18604:20b] before nil value has retain count of 0
2008-12-31 09:31:41.756 Concentration[18604:20b] on nil value has retain count of 0
2008-12-31 09:31:41.757 Concentration[18604:20b] after init value has retain count of 2147483647
I am calling it like:
Test *test = [[Test alloc] initWithValue:#"some text"];
Shouldn't value have a retain count of 1? What am I missing?
Thanks for your help.
Don't look at retain counts. They're not useful and will only mislead you — you can't be certain that nothing else is retaining an object, that an object you get from somewhere isn't shared.
Instead, concentrate on object ownership and follow the Cocoa memory management rules to the letter. That way your memory management will be correct no matter what optimizations Cocoa may be doing behind the scenes for you. (For example, implementing -copy as just -retain for immutable objects.)
Furthermore, it's critical to understand the difference between properties of your objects and instance variables within your objects. In your question's code, you are assigning a value to an instance variable. That instance variable is just that: a variable. Assigning to it will behave like any other variable assignment. To use the property, you must use either dot syntax or bracket syntax to actually invoke the property's setter method:
self.value = newValue; // this is exactly equivalent to the next line
[self setValue:newValue]; // this is exactly equivalent to the previous line
The code generated for the dot syntax and the bracket syntax is identical, and neither will access the instance variable directly.
You are passing in a literal string. The compiler probably allocates it in static memory and sets the retain count to the maximum possible value.
Try a dynamically allocated string instead and see what happens.
NSString* string = [[NSString alloc] initWithString: #"some text"];
Test* test = [[Test alloc] initWithValue: string];
You've got a reference to an immutable string. Assignment doesn't need to copy the value (the string data) since it's immutable. If you do a mutable operation, like value = [newValue uppercaseString] then it should copy the bits into value, and value's retain count incremented.
You're passing in a string constant, which can't really be deallocated. I think that 2147483647 is probably UINT_MAX, which basically means that the object can't be released.
I think you want to do this:
self.value = newValue;
which will invoke the property setter and cause the copy to occur. "value = newValue" simply assigns a pointer value to the instance variable.
You shouldn't be paying attention to the retain counts, just follow the Cocoa memory management rules. http://iamleeg.blogspot.com/2008/12/cocoa-memory-management.html
hmm.. we're getting closer.
it appears that newValue's retain count is also 2147483647.
I tried dynamically allocating the string instead with the same retain count results.
I found a helpful article here: http://www.cocoadev.com/index.pl?NSString
FTA:
Does the NSString returned by #"" need to be released, or is it autoreleased?
Neither. #""-strings are of class NSConstantString?, and thus act like atoms in lisp; they hang around. That is, if you use #"cow" in two separate places in your code, they will be referencing the very same object.
I don't think -release or -autorelease does anything to either of them.
If I have "copy" on the property though, shouldn't it copy the contents of the target memory into new memory with a retain count of 1? It would seem the copy attribute does nothing in this case?
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
char *cstr = "this is a c string";
NSString *str = [[NSString alloc] initWithUTF8String:cstr];
NSLog(#"rc1: %d", [str retainCount]);
[pool drain];
return 0;
}
If you run the above code, it will display a retain count of 1
In Cocoa, many immutable objects will simply retain themselves when you ask for a copy within the same zone. If the object is guaranteed not to change (i.e. its immutableness) then an exact duplicate is redundant.
In Objective-C, the constant string class is separate to Cocoa's NSString class, although it may be a subclass of NSString (I'm not too sure). This constant string class may override NSObject's methods like retain, release and dealloc so that they do nothing, and also override retainCount so that it always returns the same number, UINT_MAX or so. This is because an Objective-C constant string is created in static memory. It must have the overall general behaviour of a Cocoa object (when using Cocoa) so that it can be added to arrays, used as keys to a dictionary etc, except in regards to its memory management, since it was allocated differently.
Disclaimer: I don't actually know what I'm talking about.

Resources