|
Re: Why
|
Posted: Aug 20, 2003 6:04 PM
|
|
"I like Java is things like not having to deal with garbage collection, array bounds-checking, and native string types. When writing a Mac application, do you do everything in Objective-C, or C, or ... I am curious about your strategy."
Well, I think you'll be pleasantly surprised. While the ObjectiveC stuff on OS X doesn't have full blown garbage collection, a great deal of memory management has been automated for you in a pretty transparent way. I'll explain that in a second.
ObjectiveC is a superset of C - and yet when working in Cocoa you hardly ever use raw C data types. So instead of C arrays you use instances of NSArray - its a bit like List in Java. For strings you use NSString. Even string literals can be specified to be NSStrings rather than C array of char. To do that you just prepend the string with @.
"This is a C String" @"This is an NSString"
Making NSArray's on the fly is easy too. [NSArray arrayWithObjects: a, b, c, nil]. This makes use of the C vararg package to allow variable numbers of arguments - you need to put a nil at the end of the list to mark it.
The Foundation library uses a neat trick such that NSString, NSArray, and NSDictionary are all immutable, like java.lang.String. So when returning an immutable copy - you're telling clients that this is your data - no fiddling. There are mutable subclasses of each of these. NSMutableString is a subclass of NSString - its got all the stuff you need to change the string in place. Sort of like StringBuffer (although why StringBuffer doesn't derive from String beats me). You can return a mutable one by sending it mutableCopy if you want to muck with something you got from someone else.
Memory management is done in a pseudo generational fashion. There is a class called NSAutoReleasePool. One is constructed at the very beginning of the program and destroyed at the end. Another one is constructed before each event is delivered to the Cocoa UI and destroyed after the event is handled. They live in a stack, when you create one it takes the place of the most recently created one and when you release it the one it took the place of becomes active.
As you construct objects, their intialization methods generally register them with the current autorelease pool. So you can create temporary objects with the same level of abandon you do in Java. They will get collected when the current autorelease pool is released - which happens automatically for you every event. If you plan on holding onto something for awhile, you send it a retain message. Autorelease pools pass to their parents any object that has a retain count greater than zero. So this is the reference counting part. Each setter is typically written as:
-(void)setFoo:anObject { [anObject retain]; [foo release]; foo = anObject; }
If you follow this idiom in setters, you'll have no memory management issues.
BTW, if you know you are going to create a lot of garbage - like in a loop, you can consruct your own AutoReleasePool at the beginninng of the loop and release it at the end of the loop - thus cleaning up memory more often. I hope this helps.
|
|