In objective c can a class essentially delete itself? - objective-c

In objective c, suppose I have an object Obj stored in a NSMutableArray, and the array's pointer to it is the only strong pointer to Obj in the entire program. Now suppose I call a method on Obj and I run this method in another thread. In this method, if Obj sets the pointer for itself equal to nil will it essentially delete itself? (Because there will be no more strong pointers left) I suspect the answer is no, but why? If this does work, is it bad coding practice (I assume its not good coding, but is it actually bad?)

It is highly unlikely that an object would be in a position to cause its own release/deallocation if your code is designed properly. So yes, the situation you describe is indicative of bad coding practice, and can in fact cause the program to crash. Here is an example:
#interface Widget : NSObject
#property (retain) NSMutableArray *array;
#end
#implementation Widget
#synthesize array;
- (id)init
{
self = [super init];
if(self) {
array = [[NSMutableArray alloc] init];
[array addObject:self];
}
return self;
}
- (void)dealloc
{
NSLog(#"Deallocating!");
[array release];
[super dealloc];
}
- (void)removeSelf
{
NSLog(#"%d", [array count]);
[array removeObject:self];
NSLog(#"%d", [array count]);
}
#end
and then this code is in another class:
Widget *myWidget = [[Widget alloc] init];
[myWidget release]; // WHOOPS!
[myWidget removeSelf];
The second call to NSLog in removeSelf will cause an EXC_BAD_ACCESS due to the fact that array has been deallocated at that point and can't have methods called on it.
There are at least a couple mistakes here. The one that ultimately causes the crash is the fact that whatever class is creating and using the myWidget object releases it before it is finished using it (to call removeSelf). Without this mistake, the code would run fine. However, MyWidget shouldn't have an instance variable that creates a strong reference to itself in the first place, as this creates a retain cycle. If someone tried to release myWidget without first calling removeSelf, nothing would be deallocated and you'd probably have a memory leak.

If your back-pointer is weak (which it should be since a class should never try to own it's owner, you will end up with a retain-cycle) and you remove the strong pointer from the array the object will be removed from the heap. No strong pointers = removed from memory.
You can always test this.

If you need a class to bring to a situation where its deleted, the best practice is to first retain/autorelease it and then make the situation happen. In this case the class won't be deleted in a middle of its method, but only afterwards.

I think we can say it might be bad coding practice, depending on how you do it. There are ways you could arrange to do it safely, or probably safely.
So let's assume we have a global:
NSMutableArray *GlobalStore;
One approach is to remove yourself as your final action:
- (void) someMethod
{
...
[GlobalStore removeObject:self];
}
As this is the final action there should be no future uses of self and all should be well, probably...
Other options include scheduling the removal with a time delay of 0 - which means it will fire next time around the run loop (only works of course if you have a run loop, which in a thread you may not). This should always be safe.
You can also have an object keep a reference to itself, which produces a cycle and so will keep it alive. When its ready to die it can nil out its own reference, if there are no other references and that is a final action (or a scheduled action by another object) then the object is dead.

Related

How to replicate NSArray memory semantics in a subclass

Question
In my ARC project I have a class that manages objects, called LazyMutableArray. Some of the objects are actually nil, but users of my collection will never know about this; therefore I made it a subclass of NSMutableArray, and it tries to do "the same thing". In particular, objects are retained when added.
Now let's take a look at a memory behavior of other methods. It turns out that the NSArray destruction methods are documented by Apple to be an exception to this rule, in that they release, not autoreleased object.
There is some debate as to whether the combination of addObject: + objectAtIndex: + array destruction is documented by Apple to be never autoreleasing or simply happens to be in the examples I tested and in the example Apple includes.
How can I create in my subclass a method with exact same memory semantics?
Last update
After some thought, I've decided implementation based on NSMutableArray is more appropriate in this case compared to NSPointerArray. The new class, I should note, has the same retain/autorelease pair as the previous implementation.
Thanks to Rob Napier I see that no modification of my objectAtIndex: method would change this behavior, which answers my original question about this method.
On a practical level, several people said that any method can tackle an extra retain/autorelease pair for no reason; it's not reasonable to expect otherwise and not reasonable to try to find out which methods do this and which do not. It's been therefore a great learning opportunity for me on several levels.
Code (based on NSMutableArray) is available at GitHub: implementation, header, test (that's -testLazyMutableMemorySemantics).
Thank you all for participating.
Why I try to subclass NSMutableArray:
Subclassing foundation objects, I agree, is not always an appropriate solution. In tho case I have objects (in fact, OData resources), most of which have subobjects. The most natural class for an array of subobjects is obviously NSArray. Using a different class doesn't seem to make sense to me.
But for an OData collection this "array of sub objects", while, being an NSArray, must have a different implementation. Specifically, for a collection of 1000 elements, servers are encouraged to return collection in batches of (say)20, instead of all at once. If there is another pattern appropriate in this case, I'm all ears.
Some more detail in how I found this
I unit test the hell out of this collection, and values can be put into array, read from the array, and so forth. So far, so good. However, I realized that returning the object increases its retain count.
How do I see it? Suppose I insert two objects into lazy array lazy, one held weakly, one held strongly (*see the code *). Then retain count of weakSingleton is, as expected, 1. But now I read element:
XCTAssertEqual(weakSingleton, lazy[0], #"Correct element storage"); // line B
And in the debugger I see the retain count go up to 2. Of course, -retainCount may give me wrong information, so let's try to destroy the reference in array by
lazy[0] = nil; // yep, does the right thing
XCTAssertNil(weakSingleton, #"Dropped by lazy array"); // line C <-- FAIL
indeed, we see that weakSingleton is not released.
By now you probably guess that it's not just a retain, it's an autoreleased retain — putting an #autorelease around line B releases the weakSingleton. The exact source of this pair is not obvious, but seems to come from NSPointerArray -addPointer: (and unfortunately not from ARC's [[object retain] autorelease]). However, I don't want to return an autoreleased object and make method semantics different from its superclass!
After all, the method I'm overriding, NSMutableArray -objectAtIndex:`, doesn't do that; the object it returns will dealloc immediately if an array is released, as noted in the Apple's example. That's what I want: modify the method around line A so that the object it returns does not have an extra retain/autorelease pair. I'm not sure the compiler should even let me do it :)
Note 1 I could turn off ARC for a single file, but this would be my first non-ARC Objective-C code. And in any case the behavior may not some from ARC.
Note 2 What the fuss? Well, in this case I could change my unit tests, but still, the fact is that by adding or deleting line B, I'm changing the result of unit test at line C.
In other words, the described behavior of my method [LazyMutableArray -objectAtIndex] is essentially that by reading an object at index 0, I'm actually changing the retain count of this object, which means I could encounter unexpected bugs.
Note 3 Of course, if nothing is to be done about this, I'll document this behavior and move on; perhaps, this indeed should be considered an implementation detail, not to be included into tests.
Relevant methods from implementation
#implementation LazyMutableArray {
NSPointerArray *_objects;
// Created lazily, only on -setCount:, insert/add object.
}
- (id)objectAtIndex:(NSUInteger)index {
#synchronized(self) {
if (index >= self.count) {
return nil;
}
__weak id object = [_objects pointerAtIndex:index];
if (object) {
return object;
}
}
// otherwise do something else to compute a return value
// but this branch is never called in this test
[self.delegate array:self missingObjectAtIndex:index];
#synchronized(self) {
if (index >= self.count) {
return nil;
}
__weak id object = [_objects pointerAtIndex:index];
if (object) {
return object;
}
}
#throw([NSException exceptionWithName:NSObjectNotAvailableException
reason:#"Delegate was not able to provide a non-nil element to a lazy array"
userInfo:nil]);
}
- (void)createObjects {
if (!_objects) {
_objects = [NSPointerArray strongObjectsPointerArray];
}
}
- (void)addObject:(id)anObject {
[self createObjects];
[_objects addPointer:(__bridge void*)anObject];
}
The complete test code:
// Insert two objects into lazy array, one held weakly, one held strongly.
NSMutableArray * lazy = [LazyMutableArray new];
id singleton = [NSMutableArray new];
[lazy addObject:singleton];
__weak id weakSingleton = singleton;
singleton = [NSMutableDictionary new];
[lazy addObject:singleton];
XCTAssertNotNil(weakSingleton, #"Held by lazy array");
XCTAssertTrue(lazy.count == 2, #"Cleaning and adding objects");
// #autoreleasepool {
XCTAssertEqual(weakSingleton, lazy[0], #"Correct element storage");
XCTAssertEqual(singleton, lazy[1], #"Correct element storage");
// }
lazy = nil;
XCTAssertNotNil(singleton, #"Not dropped by lazy array");
XCTAssertNil(weakSingleton, #"Dropped by lazy array");
The last line fails, but it succeeds if I change first line to lazy = [NSMutableArray new] or if I uncomment #autoreleasepool.
First, I would not make this subclass. This is exactly what NSPointerArray is for. Wrapping that into an NSArray obscures important details that this approach can break. For example, what is the correct behavior for [NSArray arrayWithArray:lazyMutableArray] if lazyMutableArray includes NULLs? Algorithms that assume that NSArray can never include NULL need to be wary of the fact that this one can. It's true that you can get similar issues treating a non-retaining CFArray as an NSArray; I speak from experience that this is exactly why this kind of subclass can be very dangerous (and why I stopped doing that years ago). Don't create a subclass that cannot be used in every case that its superclass can be used (LSP).
If you have a collection with new semantics, I would subclass it from NSObject, and have it conform to <NSFastEnumeration>. See how NSPointerArray is not a subclass of NSArray. This was not an accident. Faced with the same problem, note the direction Apple chose.
By now you probably guess that it's not just a retain, it's an autoreleased retain — putting an #autorelease around line B releases the weakSingleton. This seems to be because line A under ARC translates to [[object retain] autorelease]. However, I don't want to return an autoreleased object and make caller remember this!
The caller should never assume anything else. The caller is never free to assume that a method does not add balanced autoreleases. If a caller wants the autorelease pool to drain, that is their responsibility.
All that said, there is some benefit to avoiding an extra autorelease if it's not required, and it's an interesting learning opportunity.
I would start by reducing this code to the simplest form, without your subclass at all. Just explore how NSPointerArray works:
__weak id weakobject;
#autoreleasepool
{
NSPointerArray *parray = [NSPointerArray strongObjectsPointerArray];
{
id object = [NSObject new];
[parray addPointer:(__bridge void*)object];
weakobject = object;
}
parray = nil;
}
NSAssert(!weakobject, #"weakobject still exists");
My structure here (such as the extra nesting block) is designed to try to avoid accidentally creating strong references I don't mean to make.
In my experiments, this fails without the autoreleasepool and succeeds with it. That indicates that the extra retain/autorelease is being added around or by the call to addPointer:, not by ARC modifying your interface.
If you're not using this implementation for addObject:, I'd be interested in digging deeper. It is an interesting question, even if I don't believe you should be subclassing this way.
I'm going to elaborate on why I said this "looks a lot like a homework assignment." This will likely earn me many down votes, but it will also server as a good learning case for others who later find this question.
Subclassing NSMutableArray not a goal of a program. It is a means to achieve something else. If I were to venture a guess, I expect you were trying to create an array that lazily creates the object when they are accessed. There are better ways to do this without dealing with memory management yourself.
Here's an example of how I would implement a lazy loading array.
#interface LazyMutableArray : NSMutableArray
- (id)initWithCreator:(id(^)(int))creator;
#end
#interface LazyMutableArray ( )
#property (nonatomic, copy) id (^creator)(int);
#property (nonatomic, assign) NSUInteger highestSet;
#end
#implementation LazyMutableArray
- (id)initWithCreator:(id(^)(int))creator
{
self = [super init];
if (self) {
self.highestSet = NSNotFound;
self.creator = creator;
}
return self;
}
- (id)objectAtIndex:(NSUInteger)index
{
id obj = nil;
if ((index < self.highestSet) && (self.highestSet != NSNotFound)) {
obj = [super objectAtIndex:index];
if ([obj isKindOfClass:[NSNull class]]) {
obj = self.creator(index);
[super replaceObjectAtIndex:index withObject:obj];
}
} else {
if (self.highestSet == NSNotFound) {
self.highestSet = 0;
}
while (self.highestSet < index) {
[super add:[NSNull null]];
self.highestSet += 1;
}
obj = self.creator(index);
[super add:obj];
self.highestSet += 1;
}
return obj;
}
Fair Warning: I'm not compiling or syntax checking any of this code. It probably has a few bugs in it, but it should generally work. Additionally, this implementation is missing an implementation of add:, count, removeObjectAtIndex:, insertObject:atIndex:, and possibly replaceObjectAtIndex:withObject:. What I show here is just to get you started.

Basic memory management confusion

I bet that I could find the answer of this question from reading similar threads here or by googling, but I would like "hear" it first hand so to speak because it's an anomaly in my understanding.
So here's the thing, I have some code which a previous employee wrote and I see a lot of a certain type of construct which looks rather odd to me and I just want to clarify what is "right and wrong".
For example
- (void) setWwanServiceId: (NSString *) newValue {
[wwanServiceId autorelease];
wwanServiceId = [newValue copy];
}
Here wwanServiceIdis a NSString member of the class, and to me this seem like a strange way to do it. As far as I understand it would begin by putting an autorelease on the object, basically saying: "whenever this object seem to not be used, release it for me I don't really care" then the copy will up the retain count +1 on.... wwanServiceId? or newValue? I guess the first.
Then to make me all more confused let's just quickly run through the life-cycle of the wwanServiceId-string..
basically the value will be set if we receive a notification which then from the notification-handler method will call the above -setWwanServiceId: method. other than that it will only ever be accessed for reading, so we are safe to say that it will at any given point:
put autorelease on object
retain a new string copy
Then there is one more quirk to this and this is where I am getting rather suspicious, namely in the -dealloc method which looks like this:
- (void) dealloc {
[[self wwanServiceId] release];
[super dealloc];
}
So what happens there? it will afaik release the wwanServiceId and as I said the only time it's memory management is being touched (if I haven't missed anything but I feel pretty sure) is to put an autorelease and retain it.
So to summarize:
Is the idea here that he thought that since he always retain a new copy after putting autorelease he need to release it in the end.. or well it's the only thing I can think about. Or just felt it would be safe to do an extra release in the end just in case..?
Because as far as I understand it, if this setter is called one time it will put an autorelease (-1 in future), do a retain (+1) and when destructor is called it will do the "final release" (-1).
Any ideas or suggestions helping me understand (if in fact I am wrong and the memory handling is correct as is) would be appreciated.
You wrote:
Here wwanServiceIdis a NSString member of the class, and to me this seem like a strange way to do it. As far as I understand it would begin by putting an autorelease on the object, basically saying: "whenever this object seem to not be used, release it for me I don't really care" then the copy will up the retain count +1 on.... wwanServiceId? or newValue? I guess the first.
This seems to point to the source of your confusion. wwanServiceId is a variable which can contain a reference to an object of type NSString. Variables do not have reference counts only objects do.
The previous employee wrote:
- (void) setWwanServiceId: (NSString *) newValue {
[wwanServiceId autorelease];
wwanServiceId = [newValue copy];
}
The expression [wwanServiceId autorelease] means: read the reference stored in wwanServiceId and autorelease the object that reference refers to - let's call that object A. Important: This does not delete object A; it will be released as some later stage and if at that time there are no remaining references object A will be deleted.
The expression [newValue copy] means: read the reference stored in newValue, use that to locate the object (call it object B), make a copy of that object to produce a new object (call it object C), and return a reference to the new object. This new object is owned by the caller of copy, so there is no need to retain it.
Finally the assignment stores the reference to object C into wwanServiceId.
So there is a maximum of three distinct objects involved:
A: the original object referenced by wwanServiceId, this is autoreleased to remove the ownership of wwanServiceId.
B: the object referenced by newValue, this is left untouched
C: a newly created copy of B owned through wwanServiceId
Why "autorelease" in the code and "maximum of three distinct" above?
The method could be called with newValue referencing object A, e.g. as in:
[self setWwanServiceId:[self wwanServiceId]]
If this was to occur and (a) release was used instead of autorelease and (b) there was no other reference to object A then the release would delete object A and then when [newValue copy] was evaluated newValue would be referencing a deleted object... The use of autorelease in this case delays the deletion until after the copy.
So what the previous employee wrote is not in any way "wrong", but as some of the other answer suggest it may be an unusual style. Another way you see this written is:
- (void) setWwanServiceId: (NSString *) newValue
{
NSString *oldValue = wwanServiceId;
wwanServiceId = [newValue copy];
[oldValue release];
}
which also ensures any deletion occurs after the copy.
HTH.
To be short and helpful:
This is wrong:
- (void) setWwanServiceId: (NSString *) newValue {
[wwanServiceId autorelease];
wwanServiceId = [newValue copy];
}
This is right:
- (void) setWwanServiceId: (NSString *) newValue {
if (newValue != wwanServiceId) {
[wwanServiceId release];
wwanServiceId = [newValue copy];
}
}
To explain in short:
[wwanServiceId autorelease]; is an unnecessary sent message, because autoreleasing an object will reduce the retain count at some unknown point in the future. And in the next line you are wwanServiceId = [newValue copy]; instantly setting the instance variable. So, in your memory you now have a to be autoreleased object and a new object. One of them is too much The new object is, where the pointer of your IVar is pointing to. The old one is swimming in your Memory Pool with probably no reference to it :-)
Autorelease as few as possible or use ARC.
Oh: And in the dealloc method, please do not send the message like this:
[[self wwanServiceId] release];
Better like this:
[wwanServiceId release];
as Apple recommends to work directly with instance methods in init and dealloc methods, instead of using getters and setters there.
Debug it and have a look.
[wwanServiceId autorelease];
wwanServiceId has an address. This statement does not change it. It does decrement the retain count of this object though.
Thats it.
wwanServiceId = [newValue copy];
This statement creates a new object. The new object is a copy of newValue. Compare the addresses of the objects and you will see, that the address inn wwanServiceId will vary from the address of newValue and it will vary from the address that wwanServiceId did have just before the statement was executed.
The retain, that is implicit in copy will affect wwanServiceId, but it affects the new object, that was just creaed with copy. It does not affect the wwanServiceId object which was autoreleased during the statement before.
At some point after the execution of setWwanServiceId had finished, the old and autoreleased object will dissappear. (Assuming that the retain count is 0 now. If it is >0 because it is still retained for other reasons or just for error, then it will remain and potentially cause a leak.)
Once you understood that you will not question any more what is happening in the dealloc method. wwanServiceId is released. Meaning its retain count is reduced by 1. In the event that it is 0 then it will be deallocated automatically.
You could even autorelease it there too. The diffrerence to autorelease is basically that an autoreleased object is still around and available while the current method is being executed. Its release comes into effect at some later point in time.
But there is no reason for autoreleasing the object in dealloc.
In the example given there is not even a good reason to autorelease the object in the setter setWwanServiceId. You may well release the object directly in both methods.
Assuming wwanServiceId is the private ivar mapped by the getter wwanServiceId and setter setWwanServiceId, I think it is not correct to autorelease the ivar in the setter.
I would have coded the following:
- (void) setWwanServiceId: (NSString *) newValue {
if (newValue != wwanServiceId) {
[wwanServiceId release];
wwanServiceId = [newValue copy];
}
}
What I mean is: it is not necessary to give ownership of your var to the autorelease pool (which it may be drained at the end of the application). Simply release the ivar. If someone is using it, no problem, it will have a strong reference to it. Otherwise it will be deallocated.

Synthesized copy property does not get deallocated with ARC

in my project I'm managing several Drawing objects. I'm trying to add a copy of a SmartPath object to a drawing object. This works. But when the drawing gets deallocated the SmartPath does not. I have put some extra code in the dealloc of the Drawing to explicitly set clear the pointer to the SmartPath. For some reason this works (the retain count was 1). I know I can probably copy the SmartPath and assign that to a strong parameter to fix this leak. But I'm relatively new to IOS and want to know how to use the copy parameters properly in combination with ARC.
Here is the code:
Drawing.h:
#interface Drawing : NSObject{
#private
SmartPath* rawLinePath; //path that is build up from straight lines between input points
SmartPath* smoothLinePath; //smoothened version of rawLinePath
}
#property(atomic,copy)SmartPath* rawLinePath;
#property(atomic,copy)SmartPath* smoothLinePath;
Drawing.m
#implementation Drawing
#synthesize rawLinePath;
#synthesize smoothLinePath;
-(id)init
{
if (self = [super init])
{
[NSThread detachNewThreadSelector:#selector(pointMonitor)
toTarget:self
withObject:nil];
}
return self;
}
-(void)dealloc{
rawLinePath=nil;
}
SmartPath.m
/*
* Init - set all variables in the correct state
*/
-(id)init
{
if (self = [super init])
{
visible=TRUE;
color = [UIColor redColor].CGColor;
width = SMARTPATH_LINE_WIDTH;
path = CGPathCreateMutable();
lock = [[NSLock alloc]init];
}
return self;
}
/*
* dealloc - clean up after self
*/
-(void)dealloc{
CGPathRelease(path);
}
/*
* copy method to be able to pass a SmartPath to a copy property
*/
-(id)copyWithZone:(NSZone *)zone{
SmartPath *pathCopy = [[SmartPath allocWithZone: zone] init];
pathCopy.visible =visible;
pathCopy.color = color;
pathCopy.width = width;
return pathCopy;
}
I hope any of you knows the answer to this problem.
Best regards
Your problem is your call to -detachNewThreadSelector:toTarget:withObject:. This retains target, which is self, and does not release it until pointMonitor exits. I suspect this never happens, so you've effectively created a retain loop.
You should almost never use -detachNewThreadSelector:toTarget:withObject:. It can create an unbounded number of threads. Instead, you should generally use dispatch queues, NSTimer, NSOperation or other async mechanisms. NSThread objects are generally only appropriate for long-lived producer/consumer threads (and usually those are still handled better with the newer tools like dispatch queues).
I'm not certain what pointMonitor does, but is there any reason it needs its own thread at all? You can do a lot of very good Cocoa development and never fork a thread. Could you use an NSTimer here? Note that most of these techniques retain their target until they fire (just like NSThread). If they didn't, you'd crash when they fired.
Without knowing what you're trying to do, I'm not certain which approach to recommend. You may want to put that together as a new question.
By not starting instance variable names with an underscore, you end up with code where you never know whether you are using an accessor method or an instance variable. As a result, you can never be sure whether a copy is made or not.
If you do that in other places, there's a good chance that a reference to your SmartPath object gets stuck somewhere. And what are you doing creating NSLock objects? Do you need to do anything that #synchronized can't do with much less code?
And if you use a newer Xcode version, get rid of all the instance variables and #synthesize statements. Just declare the properties.
And excuse me, but detaching a thread from an init method is just sick.

big memory problem in objective c

i've a function like this:
#property(nonatomic,retain) NSMutableArray *array;
#synthesize array = _array;
(NSMutableArray *) name
{
self.array = [[NSMutableArray alloc]init];
[_array addObject:object];
[object release];
return [_array autorelase];
}
In the other function i've a property like the property above, named result, and i make:
self.result = [... name];
Then in dealloc i make
[_result release];
and it crashes in this point, how can i solve this?
I've tried many roads, but or it crashes, or i see memory leak in Instruments, where am i wronging?
Thanks.
While there's a lot wrong with this code, the likely cause of your crash is that you're releasing object within -name without taking ownership of it- unless you're creating object within the method through a call to -alloc, -new, or -copy, that method doesn't own it and isn't responsible for releasing it. This is causing that object to be invalid within the NSMutableArray, so when _result releases, it attempts to release an invalid piece of memory and crashes.
Also, properties aren't simply local variables for individual functions, they're member variables for instances of the class for which you're writing these classes. If your end goal is only to return an autoreleased array and set it to result you could do the following:
- (NSMutableArray *) name {
//call a convenience method- it comes back autoreleased
NSMutableArray* theArray = [NSMutableArray array];
[theArray addObject:object];
//don't release object unless you took ownership of it in this function
return theArray;
}
then outside the function, either call self.result = [... name] or [self setResult:[... name]];
You have a very strange method definition (the header should have a - before the return type), and inside that definition you are accessing a variable called object that doesn't seem to exist. I'm not sure what you want, but you've got at least one memory problem. The array that you create in name gets leaked every time the method is called. If you add some details, like the crash message, someone may be able to help more.

Objective C difference between self.variable and variable assignments

I fear I am being stupid.
I've spent about three hours tracking down a memory leak that's been destroying my sanity and after commenting out half my app I have come to the following conclusion.
Given the following in the right places.
NSString *blah;
#property (nonatomic, retain) NSString *blah;
#synthesize blah;
-(id)initWithBlah:(NSString*)b {
self.blah = b; //this leaks
blah = b; //this does not
}
I am not particularly experienced with objectice c, I understand that outside of the class if I were calling object.blah = b; I would be transparently calling a setter function which would retain the b. Inside the function I'm presuming by setting it with self.blah = b I'm double retaining for some reason?
Could someone explain to me why that's the case or if not what I might be doing wrong?
Cheers
blah = b
Modifies the class instance variable directly.
self.blah = b
will invoke the accessors if present - thus retaining, releasing resources as needed.
You should add [blah release]; to your dealloc method to free the resource when your class is released.
You are mistaken. The blah = b; assignment makes little sense because after your code returns from initWithBlah: you can no longer rely on the string being around. Whenever you access it next, it has very probably already been deallocated.
The self.blah = b; assignment is correct because it calls the setter and thereby you take ownership of the string. Of course, you have to release blah in your -dealloc, too, to prevent memory leaks.
If you give only blah, it does not allocate for the string , if you give self.blah, it then tries to initiate the self and allocates for the self class and try to allocate for the variable which you trying to access , so you have to release it or make the blah=nil in the dealloc method.
i.e
- (void)dealloc
{
self.blah = nil;
or
[self.blah release];
}
Using the accessor is fine, if it leaks then something else is wrong.
In particular your initializer has to be called correctly, i.e. following the Cocoa Memory Management guidelines that parameters are not owned implicitly by the callee.
So the following would be fine as it passes an autoreleased string in:
YourObj *obj = [[YourObj alloc] initWithBlah:[NSString stringWithString:#"blah"]];
While the following leaks due to passing in a retained string:
YourObj *obj = [[YourObj alloc] initWithBlah:[[NSString alloc] initWithString:#"blah"]];
Another thing you have to be aware of is that declared properties do not automatically take care of the clean-up, so be sure to handle that in -dealloc:
- (void)dealloc {
self.blah = nil;
// ...
}