Using accessor methods to set iVars? - objective-c

Initially, I was looking at the way "pickerData" is set and thinking I wonder why you can't just assign it directly (as in METHOD_002), but then I stated thinking that I should really be using the accessor methods I defined and not setting the instance variables directly. Am I understand this correctly that METHOD_001 is a better way of doing this?
#property(nonatomic, retain) IBOutlet NSArray *pickerData;
METHOD_001
-(void)viewDidLoad {
NSLog(#"VIEW: Single ... Loaded");
NSArray *dataArray = [[NSArray alloc] initWithObjects:#"A", #"B", #"C",nil];
[self setPickerData:dataArray];
[dataArray release];
[super viewDidLoad];
}
-(void)dealloc {
[pickerData release];
[super dealloc];
}
OR METHOD_002
-(void)viewDidLoad {
NSLog(#"VIEW: Single ... Loaded");
if(pickerData != nil) [pickerData release];
pickerData = [[[NSArray alloc] initWithObjects:#"A", #"B", #"C", nil] retain];
[super viewDidLoad];
}
-(void)dealloc {
[pickerData release];
[super dealloc];
}
EDIT_001:
First off I have added the "nil" values to terminate the NSArrays, coming from C I always forget this, my bad. Also you're right, I did not account for the fact in METHOD_002 that pickerData might already be set and as a result leak the old object. Once you start noticing these problems and fixing the code it starts to look like METHOD_001 is the best idea. Or to just use the property directly as Vladimir and eJames noted.
self.pickerData = [NSArray arrayWithObjects: #"A", #"B", #"C", nil];
EDIT_002:
Thank you for all the pointers and comments, for now I am going to stick with METHOD_001, I could just as easily use NSArrayWithObjects: but I am trying to keep memory usage low by releasing things myself as soon as I can (not that it matters here, but for future projects) Also I do like the feel of self.pickerData, but I am still unsure how I feel about dot-notation and have for now been trying to stick with old style objects and messages where possible. Again many thanks for the help.
gary

You should always use accessors of properties (which in Objective-C 2.0 means using the self.property notation.)
Why? Because it provides automatic access control and object life-cycle management. The generated accessors can provide a lot protection, such as read/write, copy, retain etc that take a lot of manual code otherwise. If you write your own accessors, you can add in all the validation and side effects you want.
(Back before Objective-C 2.0 writing accessors was considered a high art. It still can be if you fully exploit the potential.)
The only time you should access properties directly is when you are writing an accessor. For example take this common pattern:
#property(nonatomic, retain) NSMutableArray *myObjects;
#synthesize myObjects;
-(NSMutableArray *) myObjects{
if (myObect!=nil) {
return myObect;
}
NSMutableArray *anArray=[[NSMutableArray alloc] initWithCapacity:1];
self.myObject=anArray;
[anArray release]
return myObject;
}
This accessor ensures that myObjects is never nil which removes a lot of boilerplate nil testing in the rest of your code.
You obviously can't call self.myObjects (which is really [self myObjects] )inside the accessor without creating an infinite recursion so you must access the raw variable here but...
...You can call (the autogenerated) self.myObjects= (which is really [self setMyObjects:anArray] ) because it's an entirely different method. If you look at the internals of setMyObjects: you would see that it access the raw variable as well.
If you use the generated accessors, self.myObjects= handles retaining, copying, nilling etc for you, every time you call it. The only time you have to call release is in the dealloc. This alone wipes out probably half the errors people make in Objective-C.
Conversely, outside of an accessor method, you gain absolutely nothing by directly accessing the properties inside the classes own methods. All it does is save some key strokes while exposing you to the risk of hard to find bugs.
As the previous answers demonstrated, you made several memory errors by trying to manage the property directly. Had you used the accessor every time, you would not have made them. For example:
pickerData = [[[NSArray alloc] initWithObjects:#"A", #"B", #"C", nil] retain];
... has to be managed exactly right every time whereas ...
self.pickerData = [[NSArray alloc] initWithObjects:#"A", #"B", #"C", nil];
... is automatically correct.
Remember that the ultimate design goal for any Objective-C class is that is should be perfectly modular and reusable. That means it should manage all its own memory, its own data validation and its own side effects. Accessors are absolutely vital to that management. By wrapping logic around every access of a variable, you ensure that (1) it is the type, range, etc you expect and (2) that it is always around when you need it be (3) that you can control all the side effects of writing or reading the variable and (4) that it doesn't leak.
I cannot extol the virtues of accessors enough. In fact, I might write a little song. ;-)

There are a lot of topics to cover here, but I'll start with the recommended approach:
METHOD_003
-(void)viewDidLoad {
NSLog(#"VIEW: Single ... Loaded");
self.pickerData = [NSArray arrayWithObjects:#"A", #"B", #"C", nil];
[super viewDidLoad];
}
Note: thanks to Vladimir for the reminder to nil-terminate.
Now, for the details:
METHOD_001 does the exact same thing as METHOD_003, but it takes a few more lines of code.
METHOD_002 has a memory leak because you alloc the array, and then also retain it. This results a total retain count of 2. When you release the array in your dealloc method, the count will be reduced to 1, so the array will not be released from memory.
Even if you remove the extra retain from METHOD_002, it will not do two very important things:
It will not send the proper KVC/KVO notifications. Cocoa does a lot of very convenient things behind-the-scenes when you use a property accessor, so unless you have a good reason not to, using the accessors is highly recommended.
It will not automatically release any old data that was stored in pickerData. This is not a concern in viewDidLoad, but if you were in a different method, it would make a difference, so it is best to just get in the habit of using the accessors unless you have a specific reason not to.

The biggest problems with your METHOD_002 are: a) you're retaining an array you already own, which you don't need to do (and will leak the array), and b) you're not accounting for the possibility that pickerData may already have a non-nil value (viewDidLoad may be called many times over the life of a controller).
As a matter of style, the first method is probably better. It would have avoided (b), and generally handles a lot of niggling details so you don't have to.

Related

Block gets released whilst in NSDictionary (ARC)

I'm trying to retain a reference to a Block that's been passed in to my class by a method, to call at a later time. I'm having trouble, however, maintaining a reference to it.
The obvious way, I thought, was to add it to an ivar collection, all of which are supposed to maintain strong references to their contents. But when I try to pull it back out, it's nil.
The code is pretty simple:
typedef void (^DataControllerCallback)(id rslt);
#interface DataController : NSObject {
NSMutableArray* queue;
}
- (void) addBlock:(DataControllerCallback)callback;
- (void) functionToBeCalledLater;
#end
#implementation DataController
- (id) init {
self = [super init];
if (self != nil) {
queue = [NSMutableArray new];
}
return self;
}
- (void) addBlock:(DataControllerCallback)callback {
NSDictionary* toAdd = [NSDictionary dictionaryWithObjectsAndKeys:
[callback copy], #"callback",
#"some other data", #"data", nil];
[queue addObject:toAdd];
}
- (void) functionToBeCalledLater {
NSDictionary* dict = [queue lastObject];
NSLog(#"%#", [dict objectForKey:#"data"]; //works
DataControllerCallback callback = [dict objectForKey:#"callback"]; //this is nil
callback(#"an arguemnt"); //EXC_BAD_ACCESS
}
What's happening?
Update: I've tried it with [callback copy] and just callback inserting into the dictionary, neither works.
Update 2: If I just stick my block into an NSMutableSet, as long as I call copy, I'm fine. It works great. But if it's in an NSDictionary, it doesn't.
I've actually tested it by putting a breakpoint right after the NSDict is created and the callback never gets inserted. The description reads clearly "1 key-value pair", not two.
I'm currently getting around this with a specialised class that just acts as a container. The callback property is declared as strong; I don't even need to use copy.
The question still stands, though: why is this happening? Why won't an NSDictionary store a Block? Does it have something to do with the fact that I'm targeting iOS 4.3 and thus ARC must be built in as a static library?
Update 3: Ladies and gentleman: I am an idiot.
The code I presented here was obviously a simplified version of the actual code; most particularly, it was leaving some key/value pairs out of the dictionary.
If you're storing a value in an NSDictionary using [NSDictionary dictionaryWithObjectsAndKeys:], you had better be damn sure one of those values isn't nil.
One of them was.
ICYMI, it was causing an early termination of the argument list. I had a userInfo-type argument being passed into one of the "add to queue" methods, and you could, of course, pass in "nil". Then when I constructed the dictionary, chucking in that argument caused the constructor to think I had terminated the argument list. #"callback" was the last value in the dictionary constructor and it was never being stored.
Contrary to popular mis-conception, ARC does not automatically de-stackify Blocks passed as arguments to methods. It only de-stackify's automatically when a block is returned from a method/function.
I.e. this....
[dict setObject: ^{;} forKey: #"boom"];
... will crash if dict survives beyond the scope and you attempt to use the block (actually, it won't in this case because that is a static block, but that is a compiler detail that you can't rely on).
This is documented here:
How do blocks work in ARC?
Blocks “just work” when you pass blocks up the stack in ARC mode, such
as in a return. You don’t have to call Block Copy any more. You
still need to use [^{} copy] when passing “down” the stack into
arrayWithObjects: and other methods that do a retain.
The return value behavior could be automated because it is always correct to return a heap based block (and always an error to return a stack based block). In the case of a block-as-an-argument, it is impossible to automate the behavior in a way that would be both very efficient and always correct.
The analyzer likely should have warned about this use. If it didn't, file a bug.
(I derped a stack when I meant a heap. Sorry about that.)
The compiler doesn't automate blocks-as-parameters for a few reasons:
unnecessarily copying a block to the heap can be a significant performance penalty
multiple-copies of a block can multiply that performance penalty significantly.
I.e.:
doSomethingSynchronous(aBlock);
doSomethingSynchronous(aBlock);
doSomethingSynchronous(aBlock);
doSomethingSynchronous(aBlock);
If that were to imply four Block_copy() operations and aBlock contained a significant quantity of captured state, that'd be a huge potential hit.
• There are only so many hours in the day and automating the handling of parameters is rife with non-obvious edge cases. If this were handled automatically in the future, it could be done without breaking existing code and, thus, maybe it will be done in the future.
I.e. the compiler could generate:
aBlock = [aBlock copy];
doSomethingSynchronous(aBlock);
doSomethingSynchronous(aBlock);
doSomethingSynchronous(aBlock);
doSomethingSynchronous(aBlock);
[aBlock release];
Not only would this fix the problem of a block-as-param, but it would also only produce one copy of the block across all potential uses.
The question still stands, though: why is this happening? Why won't an
NSDictionary store a Block? Does it have something to do with the fact
that I'm targeting iOS 4.3 and thus ARC must be built in as a static
library?
Something bizarre is going on, then. Coincidentally, I've been using blocks-as-values in an ARC based application in the last week and it is working fine.
Do you have a minimal example handy?

Objective-C: autoreleased object as parameter for method

could the following code lead to problems?
- (void) method1 {
NSMutableArray *myArray = [[[NSMutableArray alloc] init] autorelease];
... fill the array
[someObject someMethod:myArray]; // someObject does work on that array (without retain!)
}
A sometimes-appearing crash in my app looks like it IS a problem; but I would not understand that... shouldn't myArray stay alive at least until the end of method1?
Thanks a lot for your help!
So my questions besides "can that be a problem" are:
- would it be enough to remove autorelease and make a release at the end of the method?
- if not: do I have to make a retain/release in "someMethod"?
EDIT: but this can be a problem, am I right?
- (void) method1 {
NSMutableArray *myArray = [self getArray];
... fill the array
[someObject someMethod:myArray]; // someObject does work on that array (without retain!)
}
- (NSMutableArray) method2 {
return [[[NSMutableArray alloc] init] autorelease];
}
I'm going to assume for a moment that you meant this to be NSMutableArray rather than NSArray. If you really mean NSArray here, then it is all impossible. You can't "fill the array" on an immutable array.
Yes, this should work. Your problem is likely elsewhere. A common mistake here would be over-releasing something you put into myArray.
Looks like you're missing a couple of open brackets on the line where you allocate your array. Should be [[[NSMutableArray alloc] init] autorelease];
Your code is completely correct. Do not listen to people telling you to remove the autorelease and manually release the array after the call to someMethod:.
In 99% of cases using an autorelease'd object has absolutely no negative performance impact on your application. The only time you want to worry about it is in loops.
The [[[Foo alloc] init] autorelease] pattern is just the same as using a built-in helper method like [NSString stringWithFormat:...]. They both return an autoreleased object, but you probably don't worry about performance with the latter. Again, only worry about autorelease in large loops, or when Instruments tells you you have a problem.
Using the [[[Foo alloc] init] autorelease] style also keeps all the memory management on a single line. If you get used to typing [[[ you won't forget the release. If you are copying and pasting code, or moving code around, you won't accidentally lose the corresponding release because it's all on the same line.
It's also easier to review code that uses the [[[Foo alloc] init] autorelease] style because you don't have to go around hunting for the release further down the method to make sure the memory management is correct.
So in terms of readability and safety and correctness, your code is absolutely fine and good. This applies to both your original snippet and the follow up you added below. Performance of autorelease only becomes an issue when you have large loops.
Your crashing issue must be caused by some other factor that is not evident in the code you posted.
I also suggest you read the Memory Management Programming Guideline if you have not already. Basically, and autorelease'd object is guaranteed to remain valid until the enclosing pool is released. In your case, the autorelease pool exists higher up in the call stack (probably the main runloop) so you can be safe in the knowledge that your autorelease'd array will remain valid for the duration of any calls you make.
One last point. Your array allocation code could also make use of the array helper constructor:
NSMutableArray *myArray = [NSMutableArray array];
This is even simpler, cleaner and shorter than your original.
Best thing to do will be something like that:
- (void) method1 {
NSMutableArray *myArray = [[NSMutableArray alloc] init];
... fill the array
[someObject someMethod:myArray]; // someObject does work on that array (without retain!)
[myArray release];
}
and it's best from two different angles. One way you are holding your array till you have no need for it, so it will no wipe from memory until you say so. Second is the same sentence, only the reason is that you will clean up memory from no more needed objects as soon as it could be done.The last may need some more explanation... Autoreleased objects should be autoreleased as soon as you have no need for it, but they are released on two basic rules: at the end of block(not always) or at the memory pressure after end of the block(yes always). In other words NSAutoreleasePool doesn't always release stuff at the end of some block of the code, and it's not even close to be rare that it delays those releases to later point.Anyway you should always check for over-releasing your object as it will lead to crash when you'll try to reach such object at your code.

Is there a memory leak here?

Please see my comments in code:
-(id)initWithCoordinate:(CLLocationCoordinate2D)c title:(NSString *)t
{
[super init];
coordinate = c;
NSDate *today = [NSDate date];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateStyle:NSDateFormatterLongStyle];
NSString* formattedDate = [NSString stringWithFormat:#"%# %#",
[dateFormatter stringFromDate:today], t];
[self setTitle:formattedDate]; //Why does the app crash when I try and release formattedDate? I have after all passed its reference to the title property?
[dateFormatter release]; //I need to release the dateformatter because I have finished using it and I have not passed on a reference to it
return self;
}
Nope, looks fine. stringWithFormat returns an autoreleased object - you don't own it unless you retain it, which you haven't, so you mustn't release it. You allocated dateFormatter yourself, so you do own it and must release.
(See the object ownership documentation and 8 squillion very similar SO questions. This must be the number one objective-c problem that crops up here by a significant margin.)
Edit: Actually, looking at your comment, there is a very slightly subtler issue here, though the upshot is the same (and actually underlines the point about ownership).
When you pass the string to setTitle, it could retain the string itself, in which case a call to release here might not cause a crash immediately. You would still be over-releasing, however, and it would bite you in the end. It just would be more difficult to locate the cause of the problem further down the line.
As it happens, setTitle makes a copy rather than retaining yours. This is pretty common with NSString properties. One reason is that you might pass an NSMutableString instead, and then randomly change it at some later point that might screw up the receiver. Taking a private copy is safer. As a bonus it exposes your over-releasing right away and allows you to fix it easily.
In any case, the point stands: only ever release something you know you own. If you've just passed it to some other object, it's that object's responsibility to handle whether it owns it or not, not yours.
You appear to have some radical misconceptions about how retain and release work. You say:
"Why does the app crash when I try and release formattedDate? I have after all passed its reference to the title property"
"I need to release the dateformatter because … I have not passed on a reference to it"
Whether you pass a reference to something is irrelevant to whether you're responsible for releasing it. The memory management rules dictate whether you should retain or release something, and they have practically nothing to do with passing references. You should read that document, because it's very fundamental and not really all that hard. Once you understand it, all your questions about this sort of thing will vanish.
The nutshell version is, you own an object and are responsible for releasing it if you obtained it from a new alloc or copy method or if you retained it. In the case of the NSDateFormatter, you got it from alloc, so you must release it. In the case of the string, none of those things are true, so you must not release this object that you don't own.
Slightly unrelated, but...
Initializsers in ObjC are supposed to look like
- (id)init;
{
self = [super init];
if (self == nil)
return self;
...
return self;
}
This allows the super class to return a replacement. This is pretty advanced usage of ObjC, though, but it's a good habit to stick to this pattern.
More info at ADC: Allocating and Initializing Objects.

pointer memory management misunderstanding w/ objective-c

I'm using the iPhone SDK 3.0, but I think this is a general misunderstanding of how things work w/ c & memory management.
I've overridden the viewWillAppear method like this
#implementation MyViewController
- (void)viewWillAppear:(BOOL)animated {
NSArray *items = [NSArray arrayWithOjbects:self.searchButton, self.trashCan, nil];
[self.bottomBar setItems:items animated:YES];
}
// other stuff...
#end
when I try to switch away from the view controller above and switch back everything works properly.
BUT, my inclination is to "release" the original pointer to "items" because I think a reference to the NSArray is now held by bottomBar.
But when I do this (see code below) and try to switch away from the UIViewController, I get a memory management error (-[CFArray count]: message sent to deallocated instance 0xd5f530).
- (void)viewWillAppear:(BOOL)animated {
NSArray *items = [NSArray arrayWithOjbects:self.searchButton, self.trashCan, nil];
[self.bottomBar setItems:items animated:YES];
[items release];
}
Do I need to not release items in this case? Or am I doing something wrong?
Obviously, the empirical evidence indicates that I shouldn't release "items", but it's not clear to me why this is the case.
Thanks for any info/"pointers"!
You do not need to release it because you never init'd it. [NSArray arrayWithObjects:...] returns an autoreleased object. You are not responsible to release it, because it has had the autorelease message sent to it when it returned from the method. You only have to release what you init! (If you had used [[NSArray alloc] initWithObjects:...] you would have had to.)
When you call arrayWithObjects: on NSArray:
NSArray *items = [NSArray arrayWithObjects:self.searchButton, self.trashCan, nil];
You are returned an autoreleased array. The array is returned to you autoreleased, because you do not call alloc, new, or a method containing copy on it. This signifies that you do not need to memory manage that object. (Take a look at the Memory Management Programming Guide for Cocoa for more information)
However, it is then retained when you call setItems on self.bottomBar, passing the array as an argument, bumping its retain count up to 1, but then you release it, returning its retain count back to zero, which causes it to be deallocated.
Since the array is retained by self.bottomBar, this implies that it is managing the memory of the array. When it is no longer needed, the array will be released, implying that the class no longer needs the array, which is the correct way to manage the memory.
For heavens sake guys, just point people to the Memory Management Rules. Don't paraphrase them. Don't say "returns an autoreleased object" (which is not necessarily true, and is irrelevent even when it is true). Just point them to the rules.
The rules are a sum total of 9 paragraphs! There is no need to paraphrase them, abrieviate them, or restate them. They are clear and concise and explicit.
Read the rules, follow the rules, and you will have no memory management problems.
Here's the short version:
+[NSArray arrayWithObjects:] returns an object that you do not own, so no, you should not release it.
On the other hand, if you had done:
NSArray *items = [[NSArray alloc] initWithObjects:self.searchButton, self.trashCan, nil];
this creates an object with a retain count of 1, so you would need to release it to prevent it from leaking.
Check out the Memory Management Programming Guide for Cocoa for more details.

How careful are you with your return types in Objective-C?

Say you have a method that returns a newly generated NSArray instance that is built internally with an NSMutableArray. Do you always do something like this:
- (NSArray *)someArray {
NSMutableArray *mutableArray = [[NSMutableArray new] autorelease];
// do stuff...
return [NSArray arrayWithArray:mutableArray]; // .. or [[mutableArray copy] autorelease]
}
Or do you just leave the mutable array object as-is and return it directly because NSMutableArray is a subclass of NSArray:
- (NSArray *)someArray {
NSMutableArray *mutableArray = [[NSMutableArray new] autorelease];
// do stuff...
return mutableArray;
}
Personally, I often turn a mutable array into an NSArray when I return from methods like this just because I feel like it's "safer" or more "correct" somehow. Although to be honest, I've never had a problem returning a mutable array that was cast to an NSArray, so it's probably a non-issue in reality - but is there a best practice for situations like this?
I used to do the return [NSArray arrayWithArray:someMutableArray], but I was slowly convinced that it doesn't offer any real benefit. If a caller of your API is treating a returned object as a subclass of the declared class, they're doing it wrong.
[NB: See bbum's caveat below.]
It's very common to return an NSMutableArray cast as an NSArray. I think most programmers would realize that if they downcast an immutable object and mutate it, then they're going to introduce nasty bugs.
Also, if you have an NSMutableArray ivar someMutableArray, and you return [NSArray arrayWithArray:someMutableArray] in a KVC accessor method, it can mess up KVO. You'll start getting "object was deallocated with observers still attached" errors.
NSArray is in fact a class cluster, not a type, anyway. So anywhere you see an NSArray, chances are it's already one of several different types anyway. Therefore the 'convert to NSArray' is somewhat misleading; an NSMutableArray already conforms to the NSArray interface and that's what most will deal with.
CocoaObjects fundamentals
In any case, given that you're returning an array (and not keeping it afterwards, thanks to the autorelease) you probably don't need to worry whether the array is mutable or not.
However, if you were keeping the array, then you might want to do this, to prevent the clients from changing the contents.