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.
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.)
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.
"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.
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 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:
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."
23 comments
[ 4.6 ms ] story [ 74.7 ms ] threadIn 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'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.
"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.
It's also very much like calling a virtual function in C++, only slower.
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.
- 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?
@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.
Secondly, while this is minor, I find it annoying that when declaring array's I have to include nil at the end.