share
Stack OverflowCommon programming mistakes for Objective-C developers to avoid?
[+37] [17] Moszi
[2011-01-02 21:46:16]
[ objective-c ]
[ http://stackoverflow.com/questions/4580684/common-programming-mistakes-for-objective-c-developers-to-avoid ] [DELETED]

What are some common mistakes made by Objective-C developers, and how can we avoid them? I would really like to see common memory management mistakes, anti-patterns or any other mistakes on iPhone and OSX platforms as well.

Please justify your answer as well, if applicable and give examples.

If on the other hand you have best practices that is welcome too - it's just the opposite of my question so doing it wrong might answer my question :) ...

Crazy, I was just coming to ask this same question. Amazing timing. - JohnMetta
Let's hope there are going to be some really good answers ... - Moszi
(3) community wiki question if i ever saw one. but a good one. :) - Epaga
Okay - to receive more responses, I've started a bounty for this question. The best answer will receive the 100 bounty. - Moszi
[+33] [2011-01-02 22:13:29] bbum

Do not call -retainCount


(5) @bbum Classic! +1 - Jacob Relkin
This is a bit exaggerated. Using retainCount can be valuable in debugging (e.g., a category that logs -retain and -release) as long as one understands that objects get retained and released all over the place. - Justin Spahr-Summers
(12) No, retainCount is never a useful debugging aid. There are always better means of analyzing memory use, retain/release problems, over-release issues, crashes, and egregious abuse of memory. Use of retainCount is several steps below printf() in the spectrum of debugging tools; only to be used out of abject desperation. - bbum
@bbum retainCount can be useful for finding leaks, since the Leaks instrument is notably lame. I mentioned in my last comment a category that logs retain and release – applying such a category to UIViewController, for example, can help track down leaky controllers in a way that Leaks really can't. - Justin Spahr-Summers
(8) retainCount is not useful for finding leaks. Not in comparison to either the Allocations instrument or Heapshot analysis. Making such a claim is like claiming that a spoon is a better choide building a swimming pool than a bulldozer when you have the keys to the bulldozer in your hand. Leaks isn't, and never has been, used to track down allocations that have outlived their usefulness; a leak is an object that is no longer referenced; a very different problem. - bbum
@bbum In the case being referred to, it was an object no longer referenced. The app in question used a very complex view controller hierarchy where many aspects of their memory management had to be handled manually. - Justin Spahr-Summers
(3) ... and you'd be better off using the Allocations Instrument (likely with Heapshot analysis, these days) than using retainCount. The absolute retain count of an object -- especially one that is intermingled with the framework -- is often modified as an implementation detail of the frameworks. Overriding retain/release as a convenient means of breaking/logging is useful, but the value returned by retainCount is not. - bbum
@bbum I was being unclear. I should've specified that it's useful in such a situation to provide some logging context. Overriding retain and release gets extremely messy, and printing out the retain count (as well as the object being retained or released) is the only way to understand the sequence of what's happening. - Justin Spahr-Summers
(1) Still bringing a spoon to a pool building project... If you know the object that is being abandoned, then the Allocations instrument can answer all. - bbum
Actually i agree with Justin here ... You shouldn't use retainCount for any program logic, but i consider that using it for logging/debugging purposes it can be much more helpful than Instruments ... - Moszi
(3) I would encourage you to file a question showing where you have used retainCount and asking (a) what problems might lurk within said use and (b) if there is a better way. - bbum
@bbum - thanks for the idea, i'll do that ! - Moszi
@bbum - i have awarded the 100 bounty to your answer. - Moszi
Thanks -- some of the other answers had considerably more variety of knowledge. - bbum
1
[+15] [2011-01-02 22:06:57] Inspire48

Great question! A sporadic list of things I've noticed;

  • Don't code your interface (don't be afraid of IB)—it's there for a reason.
  • Adhere to the method name conventions [1]. It makes Obj-C really self-documenting.
  • MVC does make everything better—ignore it at your own peril. It keeps your code clean and organized the way that goto doesn't.
  • Plan out your whole project, from feature list and UI to the code organization. Otherwise, you'll quickly find yourself swamped, trying to juggle features, UI, and fixing bugs in your code. And the project will fall far behind. Yucky situation to be in.
  • Make sure to use your accessors. In one of the CS193P lectures (Fall 2010—the debugging one, I think), the guy had a retain property on an ivar, but he was directly setting the ivar, and then releasing it later on. Because he didn't use his retained property, it crashed...(of course, that was done intentionally for education purposes).
  • Use (and don't misuse) the pointer nature. You pass pointers around; therefore, remember that changes you make in one place percolates back to the source. Java has a lot of methods that use this (Arrays.sort, for example). This isn't so popular in Obj-C, but remember that you can still do this. Of course, this also leads to pitfalls, if you don't make a copy when you shouldn't have changed the initial value.
  • Not using dot syntax: Many people hate dot syntax. I've read the articles, and to an extent, I agree. However, dot syntax really does make everything simpler—at the very least, it removes one nested method call. And the fact that it looks like you're accessing a struct member is simply because you (effectively) are accessing a struct member (in an indirect way)—classes store their ivars in structs. And for me personally, I find that dot syntax is clearer in a nested method call than using a standard method call—it makes the item seem like an actual variable (due to the lack of square brackets) than a method call. But that could just be me.
  • Writing too much code. Rely on the API as much as you can—every line of code you don't have to write is one less chance for a bug. The API has been used for as much as 20 years. If there's a bug in there, chances are it would've been found already. Also, try to condense your code. Following Shipley's great advice [2], don't clutter your code with braces and parentheses when you don't need them. It's just something else to distract you. I prefer to nest message calls; with Objective-C's English like nature, it still keeps the code readable, but also helps with organization—I try to make each line perform one complete operation (rather than "wasting" a line declaring a temporary variable that I don't use again two lines later). But again, this is my own preference; many people will probably disagree.

Finally, make sure to use the tools that Apple provides. Once you perform a Build to test for syntax errors, also run a Build and Analyze (⌘⇧A)—it'll run the static analyzer, which will detect basic errors that you might miss, such as memory leaks, an if without an else, which may end up returning a garbage value because you don't catch every circumstance, and some subtle things like that. And of course, test your program—press every single button, with all different types of inputs. Instead of just putting in your name where it asks for your name, try putting in a period. Or a comma. Or a negative number. See what your program does. Do everything you possibly can to break your code. After all, that's what your customer will (inadvertently or not) be trying to do.

Happy coding!

[1] http://cocoawithlove.com/2009/06/method-names-in-objective-c.html
[2] http://www.wilshipley.com/blog/2005/07/i-will-insult-your-code.html

(9) With dot syntax you ARE NOT accessing a "struct member." You are still sending a message. Dot syntax should be used for getting and setting object state, bracket syntax should be used for invoking object behavior. - Chris Hanson
I agree, except for the dot syntax. Doesn’t work as expected if there are any structs involved, without even producing a warning. And access to a struct field is something completely different than sending a message, so they shouldn’t look the same. - Sven
I've said it before, but the dot-notation adds ambiguity to both the . and = operators. - dreamlax
(1) I agree with Chris and Sven about the dot syntax. Case in point is that it has even confused you. Dot syntax is not accessing struct members, it is still sending a message, only now you have obscured the fact that you are sending messages. I don't know what Apple was thinking of when they invented it. - JeremyP
(2) My understanding (and yes, I buy into it) is that I don't care that I'm calling a method. It's not like there's anything special to be observed when calling a method (as opposed to memory management when creating objects, etc.), so whether I'm calling a method or not does not matter. I mentioned that it's like accessing a struct value. All you're doing is getting or setting an ivar. When you synthesize the accessors, as you usually do, they're almost a decade of fool-proof. There's no debugging involved. Bottom line—I don't care that I'm sending a message. Just get me the ivar. - Inspire48
2
[+14] [2011-01-02 21:59:09] Chris Hanson [ACCEPTED]

A lot of developers coming from Java and C# confuse properties with the instance variables that provide their storage. This results in complaints like "Why do I have to type my members three times?" when what they're doing each time is quite different. Furthermore, a lot of such developers wind up exposing a lot more state to clients of a class than they need to, because they think they need properties everywhere.

For example, they are used to accessing instance variables by dereferencing this, e.g. this.foo = bar;. One should not blindly follow this pattern in Objective-C. When you say self.foo = bar; what you are doing is invoking the -setFoo: method, not simply changing the foo instance variable.

A common idiom among experienced Objective-C developers is to name instance variables with a prefix or suffix different from any associated property. This makes it very clear to readers of code when you're working with an instance variable compared to a property (or even a local variable). For example:

@interface TimelineViewController : NSViewController {
@private
    NSCollectionView *_timelineView;
}
@property (readwrite, retain) IBOutlet NSCollectionView *timelineView;
@end

@implementation TimelineViewController
@synthesize timelineView = _timelineView; // associate property with ivar

- (void)dealloc {
    [_timelineView release]; // use ivar, not property, in -dealloc

    [super dealloc];
}
@end

(1) Generally speaking if you have a property to an ivar (and lets say the property is retaining the ivar) it is preferred to use the -setFoo: instead of foo = [bar retain] - or i got this wrong ? - Moszi
(1) You use each when most appropriate. (There are a bunch of SO questions covering this.) For example, in init and dealloc methods, you work with the ivar, not the property, since the property can have side-effects in a subclass. - Chris Hanson
in the dealloc i would do a self.timelineView = nil; - isn't this preferred either ? usually even if your class is subclassed the property contains the logic to access the ivar, so it might happen that the subclass redefined the property. in this case wouldn't be better to use the property instead of the ivar ? - Moszi
(5) DO NOT use "self.timelineView = nil;" in -dealloc. This has been discussed elsewhere on Stack Overflow, several times; I strongly suggest looking up the questions and reading their answers. - Chris Hanson
i will look this up - thanks ! - Moszi
@Chris Hanson Accessors need be able to properly handle nil values, especially overridden ones. Also, setting properties to nil in dealloc means that the code remains valid no matter what the memory management semantics are. Using naked ivars makes inheritance significantly more difficult, as subclasses must try to catch every corner case where an ivar was referenced instead of a property. - Justin Spahr-Summers
(2) Justin, it is quite explicit in Apple's documentation - and in the other responses to questions about this on SO - that you MUST NOT use setter methods (via properties or otherwise) that may be overridden in -dealloc. Don't try to say "No, you should!" when the documentation explicitly says "Don't." - Chris Hanson
(3) Also, one reason you are only supposed to use ivars in -init/-dealloc methods is because when your -init/-dealloc code runs, you don't have a fully-formed instance of the subclass; your -init code runs BEFORE the subclass -init, and your -dealloc runs AFTER the subclass -dellaoc. Again, see the other SO questions, don't argue this further here. - Chris Hanson
after+before calling: this is a very good point. thanks for that. - Moszi
@Chris Hanson The other questions about the subject that I've found have been idle for quite a while. The point about init is well-taken, but note that Apple is considerably less strict about dealloc than you make it sound: developer.apple.com/library/mac/documentation/Cocoa/Conceptual/… - Justin Spahr-Summers
I'm not sure what you mean by "have been idle" - just because a question doesn't have a very recent answer doesn't mean its existing answers are incorrect. - Chris Hanson
I'll file a bug to ensure that documentation is updated; Apple is actually quite strict about -dealloc. The only reason that documentation does mention the accessors is that older versions of the compiler did not easily allow access to an undeclared synthesized ivar. You've always been able to access a declared ivar, and should always use the ivar in -dealloc. Period. - Chris Hanson
(1) @Chris, @Justin: Actually, I would say the case for not using properties in dealloc is even stronger than not using them during init. In init, the problem is you might be invoking a subclass method before the subclass ivars have been initialised. In dealloc, as well as possibly invoking a subclass method after the subclass ivars have been released, you may also trigger a KVO notification which would be bad considering the object is partially deallocated. - JeremyP
3
[+14] [2011-01-02 22:19:23] Chris Hanson

One issue I see with developers coming from other environments is use of incorrect terminology. As a Smalltalk derivative, Objective-C adheres pretty closely to the traditional terminology of object-oriented programming as defined by Smalltalk, in addition to regular C terminology:

  • Classes have instance variables, not members.
  • Classes have instance methods, not member functions.
  • Classes have class methods, not static functions. (And class methods can be overridden in subclasses, unlike C++/Java/C# static methods.)
  • Classes have an interface and an implementation.
  • Protocols are what other languages refer to as "interfaces."
  • Functions and structures are available exactly as they are in C.

There are also a few other oddities I've seen in the terms used by some people coming to Objective-C recently:

  • A line of code terminated by a semicolon is a "statement," not a "command." (Why are people calling them commands?)
  • A variable holds a reference to an object, it is not the object itself. It is thus senseless to talk of "retaining a variable" or "releasing a variable."

:) - i liked this ... - Moszi
Perhaps also mention that "interfaces" (as they are called in many languages) are called "protocols" in Objective-C. - dreamlax
@dreamlax Good point, thanks! - Chris Hanson
(4) Also, you don't call methods, you send messages. This is a really important distinction because it means that the decision as to which method to invoke in response to your message is actually taken at run time. - JeremyP
+1 Love this answer as it shows comparison of terminology to other languages. - Brian Wigginton
Agrred! Comparison with other languages is relevant indeed. - jollyCocoa
@JeremyP There's actually some debate over the practical equivalence of the terminology. - Preston
4
[+14] [2011-01-03 11:01:36] Joshua

You can't compare NSString's with ==. This is one of the most common mistakes I've seen on SO.

Don't do this:

if (helloString == @"Hello") {

}

Do this:

if ([helloString isEqualToString:@"Hello"]) {

}

(2) If I recall correctly, the incorrect code would be comparing two pointers, which would be a great test if you wanted to know they were the same object, but not at all useful with an immediate string like this. - Hack Saw
What's great is that this bug is a Heisenbug. Sometimes, if both objects are the same string literal, the pointer equality test will succeed. Sometimes the strings genuinely don't have the same value, and again the pointer equality test gives the expected result. It's all those other cases where it goes wrong - hope you tested for them :-) - Graham Lee
5
[+7] [2011-01-03 03:32:19] Jonah

Based on the questions I've seen here and on Apple's developer forums:

  • UIViews and UIViewControllers are different things and cannot be used interchangeably.
  • UIViewController makes some assumptions about its use; read the class' docs and avoid trying to nest view controllers' views until you understand the consequences.
  • The UIViewController lifecycle, in particular views being unloaded in response to memory warnings. This usually appears as an app losing data when the developer was counting on the view to store state.
  • Not every controller needs to be a UIViewController.
  • An array of dictionaries may not be the best model for your app, consider defining actual model classes which can include their own behavior.
  • Pointers are not objects.
  • Not all values are objects; NSInteger is not an object, you probably didn't want a 'NSInteger *'.
  • Singletons and shoving extra properties onto your application delegate are probably not the best way to manage communication or share data between your view controllers.

I think that the most valuable best practice I can suggest would be to learn to write tests. Setting up a testing framework in Xcode is harder than it should be but there are some good alternatives to OCUnit and I think the "if you can't write a test for it then you don't understand it" aspect of TDD would catch most of the mistakes I've seen.


One alternative I was using is GHUnit. Any others you might know that are good ? - Moszi
GHUnit is good, I also like the Google Toolbox for Mac (code.google.com/p/google-toolbox-for-mac/wiki/iPhoneUnitTesting) wrappers around SenTestingKit, and I've been looking at Cedar (github.com/pivotal/Cedar). - Jonah
Why do you say "shoving extra properties onto your application delegate are probably not the best way to manage communication or share data between your view controllers." - Krishnan
(2) @Krishnan that's probably worthy of its own question but a few pain points that causes are: The application delegate is responsible for responding to application lifecycle events, no need to add secondary or tertiary responsibilities to over-complicate the class and violate the single responsibility principle. Doing so leaves all of your controllers tightly coupled to your app delegate implementation which prohibits reuse and good testing practices. Global state is expensive to maintain. It ignores the principle of least knowledge/law of demeter. - Jonah
Thanks Jonah..I got that.. - Krishnan
6
[+7] [2011-01-04 05:50:03] Chris Hanson

Another thing developers coming to Objective-C need to do:

Mind your history!

Objective-C is a derivative of C and Smalltalk; most of its concepts come from one or the other. It's not a language developed in complete isolation by Apple for the express purpose of iPhone development, so don't act like it is when discussing iPhone development.

In fact, if you've been working in Java (or in a Java clone like C#) most of what you've been using was explicitly derived by its creators from the features of Objective-C. There's a lot more and richer history to the language, the tools, and the platform than many developers new to it seem to give it credit for.


7
[+4] [2011-01-03 00:32:57] Chris Hanson

Another common mistake for developers used to other languages: Declaring a new class without a superclass, and assuming it will have a default superclass.

Objective-C uses single-inheritance but does support multiple root classes. Thus if you write

/* has no superclass, don't do this*/
@interface MyClass
@end

you are actually declaring a new root class rather than a subclass of NSObject.

All subclasses of NSObject must declare themselves as such, like so:

/* superclass is NSObject */
@interface MyClass : NSObject
@end

If you don't do this, the compiler will warn you that your class doesn't implement certain required methods such as -methodSignatureForSelector: and -forwardInvocation:.


The two root classes that I do know of are obviously NSObject and NSProxy, but yes, this is good advice. - Jacob Relkin
8
[+2] [2011-01-02 21:51:26] Michael Goldshteyn

Mistakes dealing with memory management are probably the most common, especially for former C/C++ programmers. Dereferencing object pointers, instead of using properties comes in at a close second.


I'm having a .NET background, so i believe i can second your opinion about memory management ... - Moszi
9
[+2] [2011-01-02 22:17:28] Jacob Relkin

I think that there is initially much confusion about object ownership [1] and why it truly matters if you name your method +newObject versus +object.

[1] http://developer.apple.com/library/mac/documentation/cocoa/Conceptual/MemoryMgmt/Articles/mmObjectOwnership.html

10
[+2] [2011-01-04 02:53:23] dreamlax

BOOL is not a “real“ boolean type, it is a typedef for a signed char, so don't compare a BOOL variable directly to YES, instead just rely on whether it is zero or non-zero:

BOOL flag = /* obtain from somewhere */;

if (flag)
{

}

Some places are guaranteed to return YES rather than just a non-zero value, for example [NSNumber boolValue] [1] as of Mac OS X 10.3, but not everywhere will do the same.

[1] http://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSNumber_Class/Reference/Reference.html#//apple_ref/doc/uid/20000178-boolValue

This also true for C's bool from Stdbool.h. - Kendall Hopkins
@Kendall: No, not quite. The bool macro in stdbool.h expands into _Bool, which is an integer type that can only be either 0, or 1 (when non-zero scalar values are converted to _Bool type, they are converted to 1, otherwise they are converted to 0). The true macro expands to 1 and the false macro expands to 0. It is perfectly safe (disregarding further redefinitions of the macros) to compare bool types to true or false. - dreamlax
@Kendall: For reference, see section 6.3.1.2 of the C standard for conversions involving the _Bool type, and 7.16 for the standard definitions of bool, true and false in stdbool.h. - dreamlax
11
[+1] [2011-01-03 21:53:58] ohhorob

When subclassing an Apple framework class, avoid using the underscore prefix (Apple reserves it).

Avoid the use of the underscore character as a prefix meaning private, especially in methods. Apple reserves the use of this convention. Use by third parties could result in name-space collisions; they might unwittingly override an existing private method with one of their own, with disastrous consequences. See “Private Methods” [1] for suggestions on conventions to follow for private API.

(emphasis mine)

See: Coding Guidelines for Cocoa – Typographic Conventions [2]

[1] http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CodingGuidelines/Articles/NamingMethods.html#//apple_ref/doc/uid/20001282-1003829
[2] http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CodingGuidelines/Articles/NamingBasics.html#//apple_ref/doc/uid/20001281-1002931-BBCFHEAB

Since pretty much all classes end up subclassing an Apple framework class (e.g. NSObject) your point can be simplified to "avoid using the underscore prefix (Apple reserves it)." - JeremyP
12
[+1] [2011-01-05 14:11:34] nevan king

You can't check the value of an int like this:

[person setName:@"Charlie"];
[person setAge:23];
NSLog(@"Name: %@ Age: %@", [person name], [person age]);

Forgetting this has caused me many a crash. Here's how to do it:

NSLog(@"Name: %@ Age: %d", [person name], [person age]);

(1) Isn't an int value "%i"? - Telinir
(2) %i and %d do exactly the same. %i stands for integer (against %f for float), %d stands for decimal (against %x for hexadecimal). I would use %i in this context but it makes no real difference. - Sulthan
13
[0] [2011-01-04 15:26:46] JeremyP

OK, nobody has put this one explicitly, although several have alluded to it:

failure to understand and follow the Memory Management Rules [1]. Too many Stack Overflow questions go along the lines of:

I do this:

myIvar = [foo bar];

and it crashes, or I do this:

[self setMyIvar: [[Foo alloc] init]];

and it leaks.

Also, to reiterate Jonah's sixth point, you see this surprisingly often:

myArray = [[NSMutableArray alloc] init];
myArray = [someOtherArray mutableCopy];

along with the question "where is my leak?"

[1] http://developer.apple.com/library/mac/#documentation/cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html

this doesnt apply anymore with ARC and new syntax of array in objective c and LLVM - codejunkie
14
[0] [2011-01-09 23:10:12] Simone D'Amico

A really common error for beginners is to access fields of a struct belong to a property with the dot notation.


15
[0] [2011-01-10 02:38:50] ipmcc

A common mistake I've been noticing lately is folks who declare properties and then make decidedly non-property-like implementations, rather than using @synthesize and using KVO to ripple out any desired side effects. Apple docs [1] tout the following as a "feature" of declared properties:

The property declaration provides a clear, explicit specification of how the accessor methods behave.

The specification implied by the declaration is only as accurate as the implementation is faithful. For instance, if you have a property like this:

@property (nonatomic, copy, readwrite) NSString* dateString;

And then you implement it like this:

@synthesize dateString;

- (NSString*)dateString
{
    if (dateString == nil)
    {
        return [[NSDate date] description];
    }

    return dateString;
}

You've broken the implied commitment that this is a thin, storage-only property with nonatomic, copy, readwrite characteristics. This may not be a big deal when working within your own products, but if you're writing class libraries for others to consume, the declaration may be all consumers have to go on, yet there's this hidden behavior.

Also dangerous is overriding property accessors/mutators in subclasses. Sometimes this is unavoidable, but it's important to try and stay true to the promises made by the initial declaration, since those are the promises that others will have in mind when they polymorphically consume your subclass instance.

[1] http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Articles/ocProperties.html

bit of an unreadable rant, but entertaining non the less! - not really Jake
Glad you got a laugh. I've edited it, lest the underlying point get lost. - ipmcc
16
[0] [2011-01-11 23:50:41] Paul Goracke

Just because you release an object, doesn't mean it goes away. It just means that code won't use it again. It can still be retained by other code you passed it to, the system in a cache or shared object, or the runloop. This is why you need to tell an object you are no longer its delegate before releasing it, or you will/may crash.


17