Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: wwdc2006-305
$eventId
ID of event: wwdc2006
$eventContentId
ID of session without event part: 305
$eventShortId
Shortened ID of event: wwdc06
$year
Year of session: 2006
$extension
Extension of original filename: mov
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: ...

WWDC06 • Session 305

Objective-C 2.0 In-Depth

Development Tools • 56:19

Some of Objective-C 2.0's new features can change the way you think about the architecture of your Cocoa application. This session will focus in-detail on how to design for garbage collection, as well as other advanced Objective-C 2.0 features such as enhanced method dispatch.

Speaker: Blaine Garst

Unlisted on Apple Developer site

Transcript

This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.

Good afternoon. I'm Blaine Garst. We're going to be talking about the one section of the new Objective-C 2.0 that we couldn't cover in a quick 10 minutes, so we'll spend maybe 40 or 50 minutes on it here. The talk is organized in the following way. We're going to talk a little bit about what garbage collection is, as a refresher, in case you might need a little bit of refreshing. We're going to talk about the collector itself, which we call LibAuto. We will dive into considerable depth in what I call the GC and Cocoa Essentials. That'll cover all the techniques that you might need to know about or want to know about and things like that.

We'll step back a little bit. Actually, we'll dive deeper in and talk about tools and techniques, a few environment variables among friends. And finally, we'll close on a little bit of conversion notes if you were so daring as to want to try to maybe run GC on stuff you've already written. So let's get into it a little bit.

[Transcript missing]

And then after that's done, we get to reclaim the memory. In other systems, once you send that finalized method, that object might do something we call resurrection. It might try to store itself into some other global thing, keep itself alive for a little bit longer. This is what happens under Java. And then what happens there is that you have to run another collection cycle sometime later in order to really find out that this object has been, is no longer useful and reclaim the memory.

You know, that level of memory latency, being able to, you know, get and reclaim the memory on the first go around is very important to maintaining memory performance on our system. And so from the outset, we've said, you know, resurrection is just, it's kind of a bad idea. I mean, this object has been, you know, already found to be unreferenced and it's been told to go away and it wants to stay around. It's, you know, anyway, we just don't quite, we don't quite like that.

So as I said, we reclaim the memory. So this is a standard, classic, full garbage collection where you go through everything and find every piece of garbage that exists on the system. This, you know, we still, we do this, we do this when we have to. What we like to do instead. Though is do what we call generational or incremental garbage collection. And this starts with the observation that most objects are temporary.

You know, they're little strings or little this or little bits of that. So you build up a lot of temporary objects while you're processing user events and stuff. And you very rarely change, you know, the global state of your program from that. So the idea is if there was, or the scheme behind this, if there was a way to quickly figure out which of these objects.

Which of these newest objects are no longer used and reclaim them. Then you would gain a lot of memory back for a small amount of CPU time. And so this is what was first put in place in small talk systems a long, long time ago and is in modern garbage collected systems such as Java and C sharp and stuff.

So the scheme is I, if I, as I've alluded to before, and I'll go through it a couple times. Because it's a little tricky. The idea is that you keep track of the generational age of an object. How many times has this object survived a collection, for example, is one way to think about the general, about the age.

So as I said before, when you run the collector, this, the scheme is when you're running a generational collector. You only care about the newest objects. If you're tracing and you find that a new object talks to, references an old object. You skip, you go, well, we don't care about the old object. You know, it's only if new to new to new to new kinds of tracing that you need to do.

When you're done, you get to reclaim all those new objects or youngest objects. And the way we make it work is that to limit the number of objects we start out searching instead of the whole heap, we limit the number of objects in the heap we start searching by using something called a write barrier.

A write barrier is a, in our system it's a function call that says whenever you store a garbage collected pointer into something else, you put a little barrier in place, you put a function call in place, and the collector gets to know about that and keeps track of the fact that this older object has had a younger object stored into it. So that's what a write barrier is and you'll learn a little bit more about them.

So a generational incremental collection does not find all the garbage. It just finds garbage among the newest stuff. And then after a time we fall back and do a full generation later and find out all the other older stuff that's turned to garbage. So in a diagram, let's see if I can explain this one a little bit better. So the write barrier starts out, so as you see we divide the heap conceptually.

The heap is really just one continuous address space and the ages are kept within the objects themselves. But conceptually it's divided into sections. And so the write barriers tell us what the heap is. And then we divide it into sections. tell us that perhaps these objects have had new objects stored into them at some point since the last collection.

And so when it's time to do a collection, what we do is we start with those objects, we examine the heap or the set of stacks like we did before, because you might well have a new object on the stack somewhere. So we trace through the heap, again, only going from new objects to newer objects. And as you can see, there's very little connection between new objects and new objects in this diagram. And we get to reclaim, you know, as garbage, the stuff that's from the newest generation only.

So in practical terms, we recover about 90% of, you know, recently newest generation objects, and we kind of lose track of 10%. And so what we've done, you know, we've reduced the number of objects that we've collected, and we've reduced the number of objects that we've collected. And so what we've done is we've reduced the number of objects that we've collected, and we've reduced the number of objects that we've collected.

And so what we've done is we've reduced the number of objects that we've collected. And so what we've done is we've reduced the number of objects that we've collected. And so what we've done is set sort of our collection ratio of generational collections versus full generations at about 10 to 1. So we get to recover a lot of objects, a lot of time, and every now and then we have to do a full generation.

The last thing I want to talk about, and the refresher part, is about weak references. So, again, when you look at the heap and the set of references into the heap, it turns out that there are cases where some of these objects have a weak reference to other objects. The garbage collector kind of knows where they are, and it knows not to use those weak references, these non-retained pointers.

So, when doing the tracing, I have three weak references in here. You can have a weak reference from a global and from, say, an instance variable in an object. And so, when the garbage collector finds, you know, the set of stuff that's garbage, note that some of those weak references now point to things that are going to get collected.

So, what we do is, before we even send the finalizers, the collector zeroes out the weak references. It knows the addresses. It knows the places these weak references are stored. And so, it zeroes them out. It puts zeroes in there where all your dangling pointers used to be in a non-GC system.

Of course, yeah, this really works. Of course, if your weak reference is to an object that's still alive, you know, the garbage collector doesn't zero it out. So, this is a very nice facility for the cases where you really still do need to have a weak reference to one of your objects. And I'll tell you a couple cases where that is still a reasonable thing to do.

So let me talk a little bit about our collector. So garbage collectors are kind of like curries. And my first experience with curry was sort of in a Chinese restaurant and I came with, you know, curried chicken. It was this yellow curry and I loved it. And so later I got exposed to Thai food and lo and behold there were red curries and green curries, but there weren't any yellow curries. But that didn't matter because I liked them both. I liked red curries, green curries, and yellow curries. And then of course I found out about Indian food.

And there's all kinds of curries. Well it turns out that garbage collectors are the same way. There is, I have yet to see two garbage collected systems that are exactly alike. They all have like curry. Curry is a mixture of things. And so garbage collectors are a mixture of techniques.

Some as you know are copying, some are generational, some are, you know, incremental, some are, you know, concurrent. I mean there's just all different kinds of terms used for garbage collection. And I forgot to bring the book on it, but there's this nice book that, an overview book, that talks about all these different techniques.

And it's just a summary because there are thousands of papers written on garbage collectors. So if you think you knew what curry was, or if you think you know what garbage collection is, I just ask for your patience. Because what we've crafted for you is a custom curry for Cocoa.

So we've structured our collector as its own library. It lays below in our system, below the Objective-C runtime. The API to it is not yet cooked, so it's SPI internally for us at Apple right now. We expect to make it API by Leopard GM. The collector has been a little bit tuned for Cocoa, but in fact we have been experimenting with using this collector with other systems like Ruby and Python.

So we really think of the collector as a separate technology and it is actually a little more capable than what we get to use inside Cocoa today. And we're going to try to keep pushing on Cocoa to make use of all of its features. So there's a little bit of distinction between what the collector does and how we get to use it inside Cocoa.

So let me talk about the collector's attributes itself. It is a conservative collector. What that means is we don't try to copy objects into new or better places. We don't compact. If you allocate an object to a particular spot, it stays at that spot for its entire lifetime.

As you've seen before, the collector is generational. We use these write barrier assignment statements to keep track of new stuff going into old. It's non-resurrecting. It's a subtle point, but when you try to use it, you'll find some resurrection errors right away, so you better remember what resurrection means. We don't allow you to resurrect your object.

Again, registered threads are only stopped once and one at a time. A neat side effect of not being a copying collector is we don't have to be so... we don't get to use a bump pointer system that requires fixed memory addresses. So if your application grows and grows and grows in its memory use, the collector will grow right along with you.

Another interesting thing that we did to the collector that is very specific to Cocoa, but is actually a very good idea, is that the collector in the middle of scanning will actually do a call out and say, should I keep going? And so what we do in Cocoa is we kind of sniff for input events.

So if you're in the middle of collecting, because we're trying to steal idle cycles away, and some user event pops up, and we kind of sniff at that while we're in the middle of scanning, and we'll actually interrupt the collection and abort it and respond to the user event. And so this way, we keep our system very responsive. We kind of like that.

As I said before, maybe I didn't, but this collector is very tunable. We're still playing with lots of different parameters, whether we track approximately the same amount of CPU time that's currently spent in auto-release pools and stuff. But right now, we have the collector tuned on the basis of memory allocation thresholds. It's a very standard practice. And again, it is a modern system. It's got this zeroing weak reference system built into it.

So that's what the Collector is. We're very proud of it. But what you guys want to know is, how do I use it inside Cocoa? We don't try to take over everything. This is not a replacement for malloc. There's a lot of research papers out there as to how do you add garbage collection to C.

And they start out by going, well, let's just replace malloc, and then find out what happens. And, well, disaster happens because you end up having to examine all of memory to find pointers out. And it's not really C, still, because you can't cheat and mask bits onto your pointers and stuff like that. It's a research topic, OK? But we don't do that. So we're not trying to replace malloc for all uses.

If you're using malloc to hold pointers to things, we'll let you get access to the collector for your purposes. But just standard malloc-based stuff. Go at it. Have fun. So a design point that's hard to come to was that all objects have to be collected. We thought and thought and thought.

Trying to figure out how we could have some of it collected and some of it not. Would have made a nice, easier transition plan, but it just doesn't work. So all objects are collected on the system, including core foundation type objects. It is kind of because of that restriction, it's an opt-in system in that everything that you access, everything that you use has to be GC capable.

and by Leopard GM, all frameworks will be capable. As I said in the earlier talk, all the major frameworks are already GC capable and we use them pretty much every day, etc., etc. So we will finish the GC stuff by Leopard GM. We are targeting for garbage collection new Leopard-only applications. And this actually is a fairly simple reason.

Your existing applications can't use GC on Tiger, right? So, and well, you might think, well, they may be GC for Leopard and not GC for Tiger and well, okay, but it's kind of a testing nightmare, don't you think? And, you know, not all the APIs are available, etc., etc.

So, the other reason is that existing applications often have, you know, some kind of plug-in model and, you know, you can't support existing plug-ins because they're not GC capable. So, we're targeting new applications, you know, in time, you know, Leopard will be the old release and there will be a new release coming along and that's when we, you know, actually expect a lot of you to shift over to use GC.

A few of you now are going to be targeting new applications. We want to hear from you. So, another sort of WWDC restriction is this. This is 32-bit only. Small matter of time, you know, about 64-bit. And another big picture topic is that the collections right now run on the main thread and so because of locking issues and other kinds of stuff like that, we have the app kit to run the collector on the main thread.

Because of locking during allocations, we can't actually run collections at allocation time currently. As soon as we move collections off on a second thread, we will be able to do that, and that's a very traditional GC kind of thing to do. So this is sort of a mini-overview of the stuff you want to hear about. And so I'm not going to go into detail right here.

We're going to talk about-- will talk about some thinking changes, new ways of thinking you need to think about when you're doing garbage collection programming. We're going to talk about the actual support in the language. We're going to talk about the APIs that are available to you and some words of wisdom about using core foundation objects.

So first of all, here's Xcode. That's the checkbox you want to use if you want to try it out. It turns into some compiler flag, which you can figure out also. But the biggest thing you need to think about is that your life cycle changes under garbage collection. You, of course, get to forget about retain, release, and auto-release, but you also get to forget about unraveling your sub-graph of dependencies. You know, if those objects are not useful anymore, the collector's figured that out already, and you don't have to let go of them.

And you're not going to get collected if you're being referenced by something else, so you don't have to tell that something else to forget about you. So, you know, there's a lot of stuff you don't have to worry about anymore, and old habits die hard. So, I want to make this point very, very, very, very, very strongly. You have to sit back and think about your object graph and how you would like it to behave. And the way you want it to behave is to let the collector to do all the work for you.

So, that's the model you want to go for. You know, obviously, retain cycles are now okay. This might be job security for some of you, but, well, you can get up and walk out now because retain cycles are just fine. So, as I say before, and I will say in several slides following this, you need to transform your dialect thinking into finalized thinking. And part of that is to use zeroing weak references.

So let's, I've mentioned finalized facts to you before, these two at least. Garbage comes in clumps and the methods can be called in an arbitrary order. Another thing that's going to surprise you though is that you, your object can be called after it's finalized has been finished. Right? That's a side effect of that. So you have to make your object a little aware, sort of idempotent to other methods, independent of whether the finalized has been called or not.

The memory goes away only after all the finalizers are sent. And as I explained earlier to you, any weak references that you own or weak references to you have already been cleared. So with this kind of as the, kind of the mental model of what's going on, let's not forget about resurrection.

Let's move on to thinking. So what should you do in a finalizer? Well, hopefully nothing. Finalizers should do no useful work. Now, what useful work do people do today in dialog methods? Hmm. Flush buffers and really make stuff happen sometimes. Some people program their objects such that they do action, have an action on the last release.

You know, it's kind of a last use model. I mean, this is a kind of a fragile way of programming because if you've got, you know, a dependent captive subgraph and somebody else has a retained reference to the middle of your graph, then your teardown, you know, the last action on release gets kind of screwed up because that middle object doesn't do its last thing because it's not the last reference.

So, as a style of thinking, your finalizer should do no useful work because it's going to be called when the garbage collector thinks nobody's using it, not when the actual last use was let go of. So, as I say, finalizers ideally should not even exist. You don't have to unravel that subgraph. They should really only be used to recover external references. File descriptors, communications channels, you know, graphics images. I mean stuff that's really kind of external to Objective-C itself.

You can't rely on that orderly object reclamation like I said before. We have seen cases where we've had to add methods that say, "Okay, do your thing now in order to get out of this last action on last release semantics." You may need to do that now if you're going to be your ad sort of the do it now method.

So let's take a look at a, maybe, you know, not a typical, but not actually an atypical one. This is a fairly complex dealloc method. So you might think, "Yeah, that's sort of true. You can just turn deallocs into finalizes." Now, is that all you need to do? Well, it would sort of work, but it's far, far from optimal.

You know, all the finalizers happen in a bunch. It's kind of like the auto-release pool going away, and if you can get rid of that bunch of work, then your program's going to run faster. It's a good thing to optimize this. Let's see what we can do to this method.

I already told you that releases do nothing, right? So let's just get rid of those two things. They're just not necessary. What's next? Well, it turns out that the Notification Center is a very old object, and it has, since its inception, held non-retained references to things. You don't want the Notification Center to keep you alive, so when you go do a dialog, though, you don't want it to hold a stale pointer, so you've had to do this remove observer stuff.

It's quite tedious and quite crashy if you forget to do that. Well, what we did was we went in and started and replumbed the Notification Center to use weak references. So the garbage collector zeroes them out when they're no longer used. That method gets to go away. We like that.

What else is possible to do? Well, you know, I threw it away earlier, but this object had a name field, and that's often the case that you might have a global table, you know, indexed by name to this object so that your clients can come in by name and you can hand out the thing that already exists.

You know, it's kind of a weak cache of things that are existing, and so you need to take yourself out of this global table. Well, if you listen up very carefully, you can convert your table into one that has zeroing weak slots in it such that if you adjust that table over to use weak pointers, you don't have to take yourself out of the global table anymore. It's pretty cool.

Another kind of reference that's going to happen in the same way, you might have something like, oh, I don't know, an inspector panel or something that's referencing you, but that you also don't want to hold you alive. You know, there might be a lot of those kinds of things in your application, and it's very tedious and error-prone to remember, you know, who all might be looking at you and to tell them to stop looking at you when you're going away. So, assuming you teach your friends about weak references, then that line can go away.

And, obviously, you've listened to me before, and you know that your parent is part of this graph that you don't have to worry about anymore. I mean, if your parent isn't going away when you're going away, your parent should be using weak references, too. But, let's just assume that this object graph unraveling stuff, you've listened, and you haven't drank my Kool-Aid. But, anyway, you've gotten the message.

And so, you're not going to worry about that anymore. And so, you end up with this as your finalized method, which, of course, can go away all, you know, can go completely away. And so, sort of in the competition between dialic versus finalized methods, I claim that finalized is the winner by a no-show.

Let's talk about that API. Here is the garbage collector object. There's a lot of stuff up there. Most of it's not all that interesting, but I do want to talk about a couple of pairs of methods. Collect if needed. If you have performance tuned existing applications already, then you know that every now and then you have to set up your own auto-release pool. As of Tiger, you were supposed to send it the drain message.

Now, why did we do that? Well, the release messages were being ignored, remember? So, what we've done is we actually have a little hook in the auto-release pool mechanism that says, you know, call the collector to collect, you know, or at least think about collecting. So, that's sort of a compatibility bridge for us, but if you're writing new code, what you do instead is, of course, tell the collector to collect if it's necessary.

Again, if there's user events going to be coming up, the collector will, you know, the collector may not run if the thresholds aren't exceeded. It may start running and decide there's better things to do. So this is a call you can make fairly freely. You know, we're not going to collect every time you call us. We're going to collect if it's necessary. But you know, based on your tuning, that this might be a good idea. So we ask you to continue to tell us that it's a good idea to collect at these times.

The other call, collect exhaustively, is something you can use when something big has, you know, you've closed a document or, you know, you know that a whole bunch of stuff has gone away and it might be time to like really wring the memory out of the system. And you can call us, collect exhaustively, and we will, we will, we will collect exhaustively.

Another interesting API pair is on a pointer-by-pointer basis to disable collection for it. This is the JNI global reference equivalent. If you need to stick your pointer into, oh, I don't know, some piece of malloc memory that you don't allocate and you need to, you know, you used to be able to just, you know, retain your object or create a whole new one and that is going to hold the only reference to your object and then there's some kind of free-me-save.

You call back and that's the place you retain it. Well, it's malloc memory. The collector is never going to look through malloc heap and so you need a way to, you know, let your object survive in that hunk of malloc memory. And so what you do is you tell the collector to, you know, don't collect me.

And so, you know, on the, when you call back, when your release-me callback or I'm done callback comes along, you do the other one. You disable the collector. These things stack. There's actually, you know, a little reference count going on underneath, but, you know, you don't really need to know that. You disable the collector and enable it.

Having looked through, or having my tools looked through, but having reviewed what the tools have done, having examined and processed hundreds of thousands of lines of code,

[Transcript missing]

While we're there, there's another couple things you kind of always wanted to do but couldn't do with dictionaries. So let me talk about two other options you can do.

The first one I already mentioned, zeroing memory. That's a very important one from the collector perspective, but this is a general purpose object, and so some of the things that are interesting to do to it are to maybe copy its objects on the way in. That's what dictionary does for its keys. That's interesting.

But another one that's often very useful, I found it in some of the Omnicode, for example, is to key off of the object pointer instead of sending hash method and as equal method. So if you need to map objects by pointers, you get that option also, and this is the initialization method you use for that. And this style is carried over to a hash table. I'm not going to talk about that. Before I talk about how to allocate and use garbage collected memory, You know, for C stuff, I have to talk about how we do some of this stuff in the language.

So, I've already said that assignments are overridden, or assignment helper functions are used, so to be a little bit more literal about it, if you have these three lines of code, you've got a local Y, and you've got an instance variable I of R, and a global. So, what the compiler does is transform the instance variable and the global assignments into helper functions, and those helper functions are my write barriers.

You might go, "Is that expensive?" But in fact, we did this for Tiger. So, Tiger already has been built with these function calls in place. On PowerPC, these function calls are actually single instruction dispatches, so they are very fast. So, to just make the point here, when we write, when you write stuff into stack, into local variables, we know we're going to look at the stack, so we don't need a write barrier for that. So, it's really, really easy to do. It's really only for sort of graph changing kinds of operations that we use the write barrier.

A little bit of review. Const. When we use const in this way, we say that the character x cannot be changed. When we put const on the right-hand side of a pointer, we say that the pointer can't be changed. So, it's a little tricky, but the idea is that right-hand sides of the pointers are actually recognized by the compiler.

The ++xp is really xp is assigned some new value, and it's that assignment that is flagged by the compiler as being told "no." That assignment is very familiar, right? That's sort of like a "hmm, garbage." If the compiler knows about assignments, that's what we've done to the compiler. We've taught it about assignments to things.

Const. And so, we've introduced two new language attributes, or storage attributes, on pointers. The first one is called strong, and that's pretty much what we're doing for objects. I'll reiterate that, too. So, this is if all pointers to objects have a strong attribute. But in this case, we use it on just normal C stuff.

So when root is assigned, because it's been told that it holds GC storage, that assignment is overridden by the compiler to be the global assignment thing. We also introduce a weak storage attribute in the same kind of language way. And what's a little even more special about weak is that we need to have a read barrier also. So that when you reference that object, we actually have to go through a helper function call as well. So it's a different one in both cases.

But we need to do that so that when we read that value out, we go under the right circumstances. We don't always have to take locks and stuff. But when we pull it out, under the right circumstances, we interlock with the collector to make sure we're not going to revive something that would otherwise have been found to be garbage and about ready to be taken away. So let's talk a little bit about pointers.

So as I said before, the compiler treats all objects, all object pointers, as if they have that strong attribute. Structures can contain objects and other strong pointers, and the compiler will generate yet another call, a strong cast assignment, when assigning pointers into the middle of structures. And unions can also.

Anyway, be careful. For weak, we only can allow zeroing weak references on instance variables and globals. We need to know when that memory goes away in order to unregister that reference. We can only do that for things we know the layout of, which we do. The runtime knows how many variables we have, and it also knows where they are. So part of the new runtime metadata includes references to which ones are weak.

So when an object goes away, we kind of go, "Hmm, does this class have any weak references?" And if it does, we go and we unregister the addresses. So C is C and you can hang yourself. We all know that. So there are cases where we have pointers to pointers.

The AppKit has been propagating this NSError ** thing and it can give us some troubles because we don't yet handle taking the address of a global pointer and handing it through. We know how to fix that, but that's not fixed in your current seed. So that's just a little bit of a caveat right now. Yeah. Another thing which is we can't and will not support is if you somehow get a pointer to a pointer on another thread, another thread stack.

We might get away with it, but if you kind of update those pointers and stuff, you will fool our collector. The object will get collected and your program will crash. So we really say stay away from that. I should at this point say, I mean, we are adding garbage collection not only to the object part of Objective-C, but to the C part.

And C is, you know, somewhat untameable. So you have to be, you have to cooperate with us. You got to go maybe 5% of the way and let us do the other 95%. But you got to help us out. Not disguise your pointers with masks and stuff. And the garbage collection system will work.

So now we just have a small API to expose to you. You must use these strong attributes in order to get these right barriers, which enable the performance that enables garbage collection to be real. If you miss a right barrier, we will collect that object right underneath you right away, and you will get a very fast crash.

So you have to use these strong attributes if you want to get into C programming. So here's a simple example. We add the strong attribute onto a char star, and we call this new method, or this new function, allocate collectible. And what happens is when you assign from that into the string instance variable, the correct right barrier will happen.

Another example, more complicated, let's say you want to have your own C structure based, collected and stuff. It's possible. This is a little hairy. We don't recommend it necessarily, but it is possible for the intrepid among you. And so in this case, by declaring the next pointer strong, then the appropriate assignments are going to get right barriers. But notice that since we're not only allocating a collectible piece of memory, we are also going to tell the allocator that it's going to hold pointers so that the collector will scan it and try to chase references through it.

So that's a big flavor for garbage collection. Scanning takes a lot of time. And so whenever we don't have to scan, we don't want to. So inside the collector, we mark memory as whether it needs to be scanned or not. And as I said before, for objects, we know the layout. And so we do the right thing with the layout as well.

In this case, the next pointer gets the right barrier because of that strong declaration. I meant to, and I didn't, that elem thing up there, the ID, it also gets a right barrier automatically because it's an object pointer. And object pointers inside structures also get the right barriers.

Diving in, we have an NS reallocate collectible, and we've got a couple options.

[Transcript missing]

And that other funny one, Collector Disabled, tells the collector actually to start this object out as uncollectible. A funny way we integrate with the zone system is that you actually can call free on this.

So if you really just need a list of pointers to objects, you can allocate sort of this thing disabled, stuff pointers into it to get the right barriers, and when you're done with it, and you know, you can hang yourself, but you get to free that hunk of memory right then. The other way is to hand it off to the collector, but I mean, that would be kind of strange. Just don't use this option to begin with.

Using core foundation objects. So there's a handful of core foundation objects that are called fully bridged. And it means two things to us. Well, it means one thing to you, which you probably know already. A fully bridged object is one that has-- you can have Objective-C API on it or use the C function API on it underneath.

So a dictionary, you've got cf, get item, or something like-- no, CFDictionary, get item on it. Or you can pass it an Objective-C method if you cast it in the right way. So that's the first definition of fully bridged. The second definition for us is that inside these objects, we've made them GC savvy, such that they can hold references in a GC savvy way.

It's programmed in C, and so the compiler doesn't give us the right barriers. We've had to hand code the right barriers in there, and it is a-- difficult thing to do. That's why this is for Objective-C. It's for the object part. But we do what we need to make this work well.

There are hundreds of other sort of CF-type objects out there, though, including other things in core foundation. Now, the only bridging that really kind of goes on with these things is that, well, they get to go into a collections. You can-- it turns out you can actually send retain and release to these things, or, well, you used to be able to. So let's talk about-- about a little bit of a rule change here.

This toll-free bridging for retain and release has been changed under garbage collection. There's a lot of C code in our system that uses CF release on things, and we couldn't change that. We didn't want to go in and change every line of core foundation-based programming in our system. So we honor CF retain. It really means what it has always meant, and that is this object needs to stay alive.

So if you've been cheating and using release methods where CF releases could have been used, you're going to have to go back and re-look at that code. So it's a very simple rule. Rule number one, the only rule really, is don't mix those styles. If you have a CF run loop create, then don't try to send it an auto-release message. You have to send it a CF release.

Under the covers, when we allocate that core foundation style object, it's marked uncollectable until that last CF release. Now, think about that. The last CF release doesn't let go of that object. It lets it go so the collector can find it sometime if it's nowhere else used. So... There is an option, however, if you don't like doing CF releases, then you can tell the collector to start looking for it right away by declaring the instance variable with a strong attribute and on creation using this little piece of code.

Let me show you this in code. Rule #1: If you have an existing use of CF style objects, you can CF create the image just fine, it's an image ref just fine, it's an IVAR just fine, and in your finalized method, you can do a CF release. If you don't like that, though, if you want to get rid of that finalized method, you can use option 1. Option 1 says, "Call CF make collectible." Right at the allocation point, declare it with that strong attribute, and you don't have to have a finalize. The collector will recover the Core graphics image when it's time.

So as I said before, some of those fully bridged objects, there is code out there that is kind of like this. It's declared as an NSString, but they use a CFString, and it's just an object, right? Well, this is a case where that auto-release is ignored under garbage collection, but that uncollectible attribute is not. And so since you don't need the auto-release, you can get rid of that thing, and you should do a CF make collectible on these guys. So fully bridged objects, you've got to look at your retain and release logic on that.

So, a few things that probably, I don't know, a few people in here might be doing, I don't know, custom CFAllocators we don't support. These null callbacks are actually correctly handled by the collector, but you still have to zero them out. Switch over to map table and hash table wherever you can. And if you have custom callbacks for some of these things, the collector will do the right thing. If you use the standard retain and release callbacks.

Otherwise, you'll have to find out what happens. So, that's it for programming. Let's talk a little bit about some low-level tools and techniques for figuring some of this stuff out. We've got two tools. X-Ray you've seen demos of. It shows you what's going on in your app when collections are happening. We've got a couple parameters here that you can set.

To kind of tune when collections happen. We've adjusted this on several different apps. This seems to be a good ratios for us. You can set some environment variables up and watch things happen as you launch them from the command line. We have another tool which we'll show you in the labs called zone monitor.

It helps you dig in. It lets you look at all the heap, kind of chase your objects around. It has some very important functions. It'll find all the routes to your objects. You can double click on one of the items and find out. Who else? Allocated and where it got allocated to find out what's going on. So.

When you're running garbage collected, a few new things can happen to you. If you've been trying to convert code, you might have missed the right barrier because you weren't thinking about it. There's a hidden malloc somewhere going on. If you turn off the generational stuff, and we always examine everything, often that'll help you find out where you're missing that right barrier. Clearly, your objects can get recycled, get sent funny messages.

You typically break on rays and find out who allocated that spot, and very quickly find out where you were missing that right barrier or declaration. And we've got some entry points in the runtime that you can break on to find out other things that might be a little bit out of whack. So performance, performance is pretty good.

[Transcript missing]