23 comments

[ 4.6 ms ] story [ 74.7 ms ] thread
While that's a decent summary of his current perspective, I'd wager that the biggest adjustment won't be memory management. Although that is a trap for new players, a bigger adjustment will be to think and code in the "Cocoa-way".

In my experience, Cocoa Touch has all the benefits of it's very mature Mac OS foundation, and to develop effectively, and minimise friction, you need adhere to the Cocoa way.

I thought ObjC had garbage collection now?
Only on the Mac. I guess Apple feels the memory and/or performance cost is still too high for mobile.
I don't know the history here, but based on the autopool directives it looks like they toyed with the idea.
With gc enabled, [autoreleasePool drain] asks the gc to run (IIRC), with gc disabled, it releases everything currently owned by the pool. Even on the Mac, you may still want to build your app without gc for performance reasons, and Apple gives you that option.

I'm sure we'll see gc on iOS devices eventually, but it's probably too memory hungry to be practical on the phone right now.

Either that, or the objC gc isn't ready for a phone-constrained environment yet. Android seems to manage OK, but they put in a lot more up-front work into making it viable there.
Android lags often, especially with games, animation, video and such. They are sure not "doing fine" in this regard. Apple could settle for that kind of performance, but they chosen to get that extra mile out of the phone.
The drain method releases the pool itself — it's precisely equivalent to release. This does release the objects in the pool as well, but a lot of people seem to think that drain leaves the pool valid. (The reason it was introduced is so the same method could be used in non-GC and GC code, where release is turned into a noop.)
NSAutoreleasePool is the same on both platforms AFAIK. Any vestiges of garbage collection you see are from there.
I totally agree. All the quirks and idiosyncrasies of ObjC go away after a week of coding in it. What's more difficult to adopt is Apple's Cocoa framework. Coming from a background of most UI frameworks, Cocoa is just simply different from anything else out there. You're gonna find yourself going to developer.apple.com and stackoverflow.com for even the most basic tasks. It requires a different way of thinking about problems because the solutions are so different. Developers who have only dabbled in MVC will find themselves forced to adopt MVC and all its overhead for applications large and small.
A lot of my problems with learning Cocoa was when I fought the framework. Don't fight the framework, save yourself time.
"Message (function call) syntax seemed funky."

"When I first realized the language offered weak typing -- with the (id) variable -- it seemed to go against everything that C stood for in my mind."

These are good observations, and reflect deliberate choices in the design of Objective C.

The funky message syntax makes clear that a sending a message is not equivalent to calling a function. Conflating the two will eventually lead to pain and confusion.

Declaring a variable as "id" does indeed launch you out of C's type system, into a realm where any object can potentially respond to any message, regardless of its type. This leads to a lot of interesting possibilities and patterns that Cocoa (and iOS) leverage heavily.

Sending a message is indeed equivalent to calling a function, specifically the function objc_msgSend (or one of its variants, depending on return type).

It's also very much like calling a virtual function in C++, only slower.

My day job is working on a mixed C++/Objective-C project (Safari). My statement that ObjC method calls are slow is based on staring at a lot of profiles and poking into the assembly. If you study the page you linked, you can see that even the fast path results in four serialized load instructions:

1) Load class pointer from object movq (%rdi),%r11

2) Load cache pointer from class movq 0x10(%r11),%r8

3) Load cache entry (IMP) from cache movq 0x10(%r8,%rcx),%r11

4) Load function pointer from IMP movq 0x10(%r11),%r11

Notice that each of these loads reads into a register which is then treated as an address by the next load. This does a great job of totally stalling your pipeline while you wait for memory reads. I've been staring at this same grim pattern in the profiler since the days of PowerPC.

C++ virtual function calls are doubly-indirect in the worst case (read vtable pointer from object, read function pointer from vtable). And because it's emitted at the call site and not hidden in a function, the compiler can often reduce it down to singly indirect or even direct link it as a static call, if it can prove the exact type.

Objective-C is not as bad as some say, but if your code is highly optimized, method call overhead can become a bottleneck.

Nice reply, thank you.
I started about a month ago... As someone with a background in Java/C++/Python, here are some more that have caught my attention:

- The ability to assign types as ClassName<Protocol>, instead of the protocol specifying the type completely. Neat, I guess...

- Because it's message passing, you get duck typing for free, albeit with compiler warnings (you send a message, and wouldn't you know it, the selector exists!). Not that I recommend this approach over protocols.

- The awesomeness of @selector, especially if you're coming from Java and used to writing anonymous classes everywhere simply for the sake of making functions first-class entities.

- The lack of public/protected/private visibility modifiers and how variables are suddenly part of the public API when you define them in properties. Say you have a variable you want to keep private, but you need to frequently update its value. You go ahead and define @property (nonatomic, retain) for it so within the class so that you can simply do self.varName = newVarValue to update it. But now all your clients/outside clients will see; you need to write the release-and-retain setter yourself in the .m file to prevent this.

- The minimalism of Xcode. Hey, it's faster than Eclipse, thank $DEITY for that. But when I want to alphabetize the filenames in the left sidebar, I have to go to Edit -> Sort -> By Name for _every_ file group?

You can define a category within your implementation file (.m) and redeclare the property like this:

@interface MyClass (PrivateMethods)

@property(readwrite, retain) NSObject *myProperty;

@end

@implementation MyClass ...

Now only your implementation knows about the generated setter methods, and you don't have to implement them yourself.

You can use @public, @private, or @protected on the instance variables. Unfortunately these do not extend to the properties themselves and you have to use a different approach as in the sibling comment:

  @interface SomeClass (SomePrivateCategory)
     @property (nonatomic, assign) NSUInteger count;
     ...
  @end
Actually, Objective-C has class extensions for just this purpose. No need for a private category.
One of the biggest pitfall for beginners is the fact you can send messages to nil objects.

Secondly, while this is minor, I find it annoying that when declaring array's I have to include nil at the end.

Exactly when and where did Apple suggest not to use NSAutoReleasePool?
I know this is a broad statement on my part. Apples says the following in the hello world tutorial, "In general, however, you should try to avoid using autorelease wherever possible since it means memory may be reclaimed later than it would be if you used release."