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: wwdc2008-353
$eventId
ID of event: wwdc2008
$eventContentId
ID of session without event part: 353
$eventShortId
Shortened ID of event: wwdc08
$year
Year of session: 2008
$extension
Extension of original filename: m4v
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: [2008] [Session 353] What's New ...

WWDC08 • Session 353

What's New in Objective-C

Essentials • 56:18

Objective-C is the dynamic programming language at the heart of Mac OS X and iPhone OS application development. From its roots as a simple object-oriented superset of C to powerful new features in Leopard, the language has evolved to meet your needs. Learn how to use properties, take advantage of fast enumeration, and use garbage collection in your own development. Discover Objective-C as it is today and learn where it's headed.

Speaker: Blaine Garst

Unlisted on Apple Developer site

Downloads from Apple

SD Video (499 MB)

Transcript

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

Good morning. This is the What's New in Objective-C and C and C++ talk. My name is Blaine Garst. I work on runtimes. And so let's find out what we have cooking on your seed, on your So first let's talk a little bit about Objective-C evolution. It turns out Objective-C has changed over time and most of the change happened in its early days.

And in the early late 80s, early 90s, very early 90s, the major changes in the language were made when it moved from a preprocessor into GNU and syntax changed and we added some features and stuff. But it stayed pretty much the same for quite a while. This was what we used at Next.

This is what we used on Mac OS X Cheetah, on Puma, on Jaguar. Even back then, though, we were kind of looking at the language and looking at Java and we're going, well, you know, we could probably improve some things. And so we took a small step forward in 03 in the Panther release by introducing a better exception construct. And a better synchronization construct. But that's, you know, fairly small changes.

But we kind of liked that idea. We took a look at the patterns that we used in Cocoa and said, you know, we could do a lot better. And we had been working on this other thing called garbage collection for a while and some of that actually got out in Tiger, it turns out.

When Leopard came out in '07, we ended up delivering a whole bunch of features. And again now, as you preview Snow Leopard, we're introducing just a few more. And so we sort of are going to call this new thing Objective-C 2.1. So what we're going to talk about today is the most recent set of changes.

In particular, the Objective-C 2.0 language is what you will find on Leopard and on the iPhone. And then we'll spend some time talking about 2.1 features that are available only on Snow Leopard. So let's get started. Ah, I forgot. That one thing we heard about, blocks. We're going to spend a fair amount of time talking about blocks.

Objective-C 2.0. So this is, well, I wouldn't say review, but Let's review! First thing we did was add the for in statement which is a fast, concise, and safe way to travel through collections. So, in this case, walking through an array, this is the fastest way you can go through an array. It's faster than doing objected index. It's faster than creating an object enumerator and going through the object enumerator.

It's just faster and it's safer because we have a mutator check in there to say, "If somebody changed the array underneath you, then the next time you go after an object, you'll get an exception." So, we like that. We like the concise way to go through that. The for in statement is just nice. Of course, it applies to more than arrays. It applies to any collection class. Actually, any class that implements this particular protocol.

So, if you've got a set of stuff you want to vend via the for in statement, you just implement this fast enumeration protocol and you can use the language feature to get to it. So, in this case, we're enumerating a dictionary. We get the keys out of the dictionary.

Well, it's quite often the case that you actually want the values that are associated with those keys. And so, you have to go and get the value back. And people said, "Well, why can't you do sort of like for key and value in dictionary?" And we go, "Well, that's really hard to get the syntax and make the handshakes and all sorts of stuff." So, although for in is very good for doing one way through your collection, it's very bad in that it only goes one way through your collection.

But, you know, it's pretty good, pretty nice. One of the nicest things about it is that the way you go through collections in different ways is you build an object enumerator of some kind. There's many different enumeration methods. And we built the fast enumeration protocol on that so you can use the for in statement with any object enumerator. And at least you get some conciseness out of it.

So, we like that. These are all very -- one of the things -- one of the reasons they're fast is that they allocate no extra memory. It uses a little stack buffer to rip through the stuff. And so, we like the idea that you don't have to create memory to go through a collection. Because going through collections is something you do quite often.

Another thing we did was we added an optional statement in protocols. Now protocols are, we're We extended them with the optional keyword so that you don't have to implement all the optional methods. You can implement some of them. So an example here, a guest, for example, has to implement the RSVP but can do either eat, drink, or laugh. And then you can build an adult object that does the eat and drink method, and you can build a child object that just does the eat and laugh.

Then you can have one kind of a party that takes a guest, another kind of party that takes a different kind of guest. So you get to reuse the protocol idea across many different implementations. Now, this is kind of a silly example. In truth, what we're doing is for the delegate methods inside the app kit. Now, delegation is a very powerful technique where a large object like the application has an object called its delegate.

And the delegate gets to implement sort of extender behavior. It gets to do the fun stuff. So we have these fun stuff methods, and the application object says, if I have a delegate, does that delegate implement this particular method that does the fun stuff? And if it does, it calls out to that. So that's a very powerful technique. And so what this does is specify the methods on how you do that, which methods you can implement. And it helps make your code more readable. and nicer.

So it turns out that the AppKit is introducing these protocols, and on the phone, they get to use them quite a bit more. So we see delegate methods being used in both the AppKit and in the UIKit. And in the case of UIKit, where they're writing brand new APIs, they get to specify that, you know, when they have the set delegate method, they get to specify the type of that delegate coming in using that protocol, which lets you, you know, it helps you understand what that thing does and what you need to do to do that. That funny syntax, ID, UI, modal view, delegate, says... Any object that conforms to this protocol can be sent in as the delegate.

The other thing we looked at in Objective-C 2.0 were setters and getters. So this is your typical You got an instance variable and you write a setter and a getter access method because you don't want to make the instance variable public. You don't want people just hacking on your instance variable. You want them to go through some methods.

And that gives you a level of control and flexibility monitoring, all kinds of stuff. You don't even have to have an instance variable, really. You can just implement it through magic if you want it. So this is a very typical pattern. And the issues with it are that it's tedious to write.

And in fact, error-prone to implement. And so there's other things. It's like, well, there's things about that interface that you actually don't know. And you have to go to the documentation to find out if there is documentation. So we didn't like these things about this. And even when you go to use these accessors, they're kind of, again, tedious and cumbersome to use. So the first thing we did was we improved accessors.

We improved access by introducing the dot syntax. So wherever you used to see set title or get title or something like that, you can now use the dot syntax to do it. And it's very straightforward. And we reused dot because every object in Objective-C comes off the heap.

And so there is no sort of local thing on the stack that you would use dot for anyway. So it's an unused syntax for us. And so we used it to say we're going to send methods there. But you could. You get to write them. So these two are exactly equivalent. The compiler just synthesizes these messages underneath you. And you just get to write things in a simpler way.

Unfortunately, the compiler just sort of does this pattern match, and so you can actually use dot syntax in places sometimes where you shouldn't. But anyway, for convenience, we just relax the rules a little bit. So the second thing we wanted to look at was how do you simplify declaration and implementation. So we turned the setter-getter pattern into a language concept called a property. So where you wrote before that other stuff, you can now simply write @property copy and a string doc name and get your job done.

So you can use You can use properties inside classes, as we see here, but also inside protocols or inside categories. Now, that funny thing, copy, is what I was talking about before. Copy, in this case, says that when you set it, The setter is actually gonna make a copy of your input strings. You can supply a mutable string to this as the value in the setter, and a copy will be made, and so you can feel free.

You can knowingly go ahead and change your mutable string later, knowing that whatever value you had is well preserved. So this is a very important aspect of the interface. And so we get a place to say that now in the interface. It's part of the contract that this class provides.

There are many or several different sets of attributes, and I wanted to talk about them because you're going to see them in interfaces, and you might as well know what they mean. So assign is the default. If you don't say anything, you get assigned. This turns out to be, under non-garbage-collected environments, a bad thing to do for objects. So the compiler warns about that.

So assign is we just sort of copy the bits over. Under garbage collection, we apply garbage collection write barrier technology. And so under that environment, assign is the default. It just sort of works. Your pointers are held the way you kind of want them to be. Under a non-GC environment, retain is what you expect. We retain the object coming in and hold onto it strongly. You can create graphs.

I'm sorry, you can create cycles using the retain primitive. Otherwise, assign just kind of takes that pointer and holds onto it, and it's going to dangle if it goes away, and you don't. Undo it. But these are two common patterns in Cocoa, and we needed to support them both. As you saw before, copy makes a copy of the input thing. And so these are three attributes you apply to objects.

Non-atomic-- oh, and you choose one of the above three-- assign, retain, or copy. A property can have the non-atomic attribute, and what that means is it's not atomic. By default, properties are atomic, which means when you go to change them, the change happens. If you assign a structure, like a rectangle or a larger structure, you need to know that it all gets there in a multi-threaded context. Either all of it's there or not. So the default being atomic was the right way to go in the face of multi-core and multi-threaded programming.

However, at a setter, going and getting little items at a time might not make sense totally. Sometimes you'll have a different context around access to getters and setters. You might have your own at-synchronized statement, or you might have your other locking things, such that you change two or three things at once.

And what non-atomic says is don't go to the bother of making them intensely thread-safe because you already know better and are going to do that on your own, or you know that you don't need to do that. So non-atomic gives you a faster implementation of getters and setters under non-GC, and is used for speed where you know you can use the speed.

The interface, we use methods to get to things. Sometimes for historical or aesthetic reason, you might not just want the names that we provide by default. So you can specify the names you want. And finally, you can declare your property to be read only, meaning you just have a getter. And later, you can actually override that, say, in a subclass or in a category or something, and say, it's actually read-write. We can add a setter. So read-write is the default. You have to say read only to say we only do a getter.

So so far, I've talked about how properties help the interface, help the specification. On the implementation side, we also said writing those setters and getters is tedious and error prone. And so let's let the compiler and runtime do it. So we do that with a synthesize statement. So inside your implementation, you provide an at synthesize statement. And it tells the compiler to fill in the implementation if it doesn't find one.

On the new runtime, which is what we use on 64-bit on the iPhone, it will actually synthesize the instance variable if you haven't supplied the instance variable. So it makes writing simple properties just dead simple and fast and does it right. You can choose to back your property with an instance variable with a different name.

And you can choose to implement your getters and setters at runtime and tell the compiler you're going to do that and not to warn you when it doesn't find it. So we will talk more about how these low-level mechanisms work on Friday, and I'll have a pointer to the talk on Friday a little bit later.

I mentioned this newer runtime, a modern runtime. It has a lot of very nice features in it that are sort of independent of the language, but suddenly you get to do new things in the language. For example, this thing called non-fragile instance variables. It turns out you don't have to expose the actual layout of your object.

You don't have to swear by that across releases because your subclassers know exactly where everything is, and you can't change that underneath subclassers that have already shipped. So there's a lot of really nice features in the runtime, and we will definitely talk about those more on Friday at this talk.

The big thing in Objective-C 2.0 was garbage collection. It is the motivator for changing, putting the 2.0 on it. Garbage collection is a fabulous way to program. It is currently only on our Mac OS X platforms. Every framework on the system is garbage collectible compatible, and we have seen some fantastic applications built on top of core animation, all of our WebKit, all of our heavy-duty frameworks.

It lets you, of course, do, you know, when you code, when you think about designing things, it gives you new cycles, new patterns to program with. Cycles are okay now. We have these things called zeroing weak references, which lets you keep weak pointers, you know, non-retained pointers to things, and they zero out automatically when the object goes away. It makes, it's, they're fun to program with. However, garbage collection programs, they run fast, they have been shown to run in less memory, and they, of course, have fewer crashes and leaks.

So what we have in Mac OS X is a fairly sophisticated collector, and I wanted to go through it a little bit. So right now you might have several threads, main, a couple worker threads. You've got some global graph of objects, your documents, you know, the stuff that's in your application, and you've got a heap.

So what happens is, you know, a thread will, you know, allocate a couple objects and change, you know, the global graph. And on average, about half the objects allocated are actually just temporaries. So the temporaries are discarded, and so another thread can kind of do the same thing. And, of course... You can lop things off the global graph.

You can change the global graph. You can delete a document. And so what happens is we have a two-stage kind of collector. We have a generational collector, which comes in. It uses write barriers to detect whether the newest objects are still interesting or not. And so a generational collector generally comes in and sweeps up the temporaries that you haven't done much with. And then we have a background full collection that will pick up everything else. So we have a two-stage collector there.

So with that, let me shift into Objective-C 2.1. But keeping in the garbage collection theme, let's talk about that first. Oh, yeah, I forgot. No more badges. From here on out in the talk, it's new stuff for Snow Leopard. So the first thing we added was something we call thread local collection.

In a garbage collection environment, the generational hypothesis is that most objects die young. So go to extra work to make sure that allocating and collecting local objects is done very fast. On Snow Leopard, we've improved upon that. We say most objects die local, meaning they never get attached to the global graph.

When we introduced garbage collection, I got a few bug reports right early. They go, my program crashes right away. And I go, really? You know, I mean, okay, show me your program. And so they showed me this little allocation loop. They showed me a program that only created garbage. And so this is a program I actually run, and I'm going to show you some numbers from it. But you can run this program or this little piece of code on many different threads at once. And all it does is just allocate stuff.

And, well, eventually you outrun the collector and it crashes the program because you run out of memory. So on Leopard, you run the program and it turns out that the traditional system, the retain-release system that uses malloc, you know, has some performance characteristics. And the garbage collection one actually outruns it for at least a few seconds before it runs out of memory. And so that's why it's in red.

So what this graph shows is that by adding threads, this was run on an eight-core machine, so seven threads are, you know, each has a dedicated processor, essentially, or a dedicated core. And, you know, allocation rates are linear up to the point where you run out of cores and then they kind of dip down a little bit. So we're talking a peak of about 500,000 allocations and recoveries per second. In the GC case. So in Snow Leopard, one thing they did was they provided for malloc an allocation cache. And they dramatically improved the performance for multiple threads.

Six times the performance. That's very nice. We asked them to do that, actually. Actually, a lot of people did. But allocation caches are good. And we had put one in for our garbage collector very early on in Snow Leopard. And then we added this other thing, which is. Thread collections as well. And so whereas we had to double the scale on this graph to show you how good Snow Leopard malloc is, we have to, again, adjust the graph. This time by a factor of ten to show you how fast we can create and collect garbage.

Now, why is collecting garbage important? Well, I just told you, half the stuff you create is thread local and is garbage. And so the faster you can create it, the faster your program is going to run. It's very sweet. Let me show you a little bit about that.

So here's the same diagram before. And what we've done is we've added thread local caches. And again, when you allocate one object, though, we actually pull in a few extras as well. So we fill the cache using one lock. That's one speed saving. Again, it takes a couple objects out.

One's attached and one isn't. And other threads are doing this thing at the same time. And again, they also attach one and get rid of one. But in this case, what we do is every thread at the right time says, hmm, it's kind of like an auto-release pool in some respects.

It says, what things in my local graph? We keep track of the local objects. Which ones of these are no longer visible from my current stack? And so we just get to examine our current stack and nothing else on the system. It's very fast. It's very fast. And so what happens is the objects come and go.

Back and forth, allocated, garbage back, garbage in, garbage out. And it's just very fast. And there's no other thread involved. It's a beautiful system. Now, of course, you can still disconnect graphs, you know, lop off chunks from the regular graph. And the regular collector's got to, you know, pick that up. But what we've done here, obviously, is increase the scale. The background collector has half the work to do. And so we can really go after larger and larger applications. with this. We think it's pretty neat.

It just fits. The next thing we added in Objective-C 2.1 is associative references. So the problem is if you know how to write categories, and I'm sure you all do, You sometimes need some data. And how do you associate that data with the thing you're extending without that thing's cooperation? There's no way to do it, really. So you try to add something. Where do you put this extra data? You can't stick it in a global map table because the target object will never go away because the global map table is going to keep it.

Under garbage collection, you might think one of these weak, strong map tables will work, but actually, much to our surprise, that'll actually form a cycle that's uncollectible. And so this is just a hard problem, and people don't do that. If you're in a scripting language and every object is a dictionary, it's fine. Anybody can add something to it. But in a compiled language, it's very hard to extend an object that's already out there. But we figured out how to do it.

And so in Objective-C 2.1, we have some new runtime calls. It's called setAssociation and getAssociation. And what you do is you ask, associate this extra thing, this nifty object, to the object, in this case, self, and hold on to it in sort of a property-style retain way. And so, like the properties, each way we have a property, we have an option for that. And you have to have a key.

Because several things can want to extend the same object. And so you have to have a key for your use. And we stick in a unique void star, which is the address of some unique location that's known only to you. So we use that as the key. So this is a pretty nifty little system.

We added another thing. Ah, I'm sorry. So again, associative references add data to arbitrary objects. came from this study that actually had leaks in GC that we were kind of afraid of. It works under both GC and non-GC. So this is a new design pattern again. You can extend existing objects with value, not only with methods, but with some data. I do have to caution you, though, it's not trivially cheap.

It is not just adding another field in the dictionary. There's 15, 20 words of memory and some CPU that go in to making that happen. Getting the association is a locked hash table access. So the getters and the setters aren't totally cheap, but when you have to do it, this is the way to do it.

So the other thing we added in Objective-C 2.1 is this thing we call blocks. So your first blocks are very simple. A, B, and C. We all have them or have had them and have done that for our children if we have children. But blocks actually, can be different.

They can actually be in different languages, for example. And we think blocks should be in several different languages. So they are. And the other thing about blocks is they can be a little complicated. They can be a lot of fun. They can hide a lot of interior stuff behind you. And in combination, they can do phenomenal things for you. So let's spend some time and talk about what blocks are for C, Objective-C, and C++. So in this case, we have a string.

Our blocks were inspired by Smalltalk, because that's where we got the name and some of the ideas, and also Ruby, and actually a little bit of Java. You'll have to pay attention to figure out where the Java connection is. But in this case, we have a string, and we have a new enumeration method on string.

It says, This is a very simple project. It gives me every line that is in this huge string. So here we read a string in from a file, and then we want to enumerate that string, just handing off every string, every line that's in that file to this block of code.

And so the block of code takes some parameters, and for every line that contains the string AKE, it'll stick it into the AKEs array, and that's pretty simple, pretty fast, pretty concise. So...

[Transcript missing]

That's all you have to do. One of the options is do it in the background and it'll do whatever you pass in in the background.

Let's go back to that first example, the lines iterator. And here I've added some mutations. So let's say you have a counter in your function and you want to update it from within the block. Well, we actually let you do that, but we ask that you provide us a little extra syntax. And that syntax is very much like Smalltalk or Ruby. You put your shared variables inside the or bars to say they're shared. They're shared between the stack and this block.

That begs the question, well, what was going on with the aches variable before? And what happens there, if you don't say that it's shared, then what we do is we actually make a copy of that variable and stick it in the block itself. A block is a data structure. It starts out on the stack, and it's got copies of things that are not by reference.

And so when you hand these things off to other threads, as you'll soon see, they actually get to operate on many of the values right there in the block and not have to go through, you know, not have to share them for one, which is kind of a hard thing to do in a multi-threaded environment.

So they get very much a local copy of it. And the deal here is the local copy is a constant. You can't update it. If you updated it, you know, you'd read and write your code, and it would look very funny. The values would go away. out of sync and stuff.

Where else can we use blocks? So here's an example of QSort, QSort that takes a block comparator. And it's very natural. In this example, what we're doing is it's sort of like your first name and last name. It fixed positions in every string. And so you pass in the beginning and ending positions, and it does a two-field sort. And so you can actually read the code I actually haven't tested it, but I think it's right.

And it's pretty clear as to what's supposed to be going on. You don't, I mean, blocks are a convenient way to do this. You don't, if you didn't have blocks, of course, you would use the existing QSortR. But QSortR takes one of these void star parameters, and what you would have to do is pass in some structure that contained all the parameterization of the sort, and all that code in orange is actually extra code that sort of gets in the way of your algorithm. And so blocks are very convenient for expressing your algorithm without a lot of this extra boilerplate.

So in general, the trouble with this boilerplate is anything that takes a function pointer is kind of limited because functions can only refer to globals and their parameters. And sometimes the parameters can't carry the information. And so globals make them thread unsafe, very much so. And the way you make an algorithm or a global function thread safe is you pass a context pointer.

So you pass a context pointer in and, well, you can do it. We all do it. It's all kind of tedious. But it really gets hard when you use it as a callback, when you try to use a function pointer as a callback, where both the function pointer and the data have to be saved. Because how do you save somebody else's data? It's like, I mean, there's many different ways of saving data. And so it's... I'm gonna go through an example here.

As I said, callbacks are a popular way to program, though. So here's a callback in our existing code. You set up some callback function that does your thing. And you pass that callback function into, in this case, a Net Service browser. And that's fine. Easy so far. Simple, straightforward. And then you get to the wrapped data pointer. You don't just get to hand your void star in.

You have to put your void star inside a very specific structure. The structure is a Net Service client context. And I think you have to allocate it. I know that it's got a retain and a release function pointer in there. And so you have to set up a retain function and a release function. You have to figure out when that retain function is going to be called. I mean, you can do it. We have documentation. But it just gets in the way. It's going to get in the way of what you want to do, which is just write that code.

You can imagine what would this look like if we did it with blocks. And it would look just like this. Your code would be there, and that would be all you have to write, and that's what you want to write. So two things about this. So far, this is just imaginary. We have a lot of places where we use callbacks in our code today, and we haven't wholesale replaced them with block variants.

But the second thing you might be asking, we're talking about callback, right? So how the heck can you refer to a stack local variable in a callback? That's an interesting question. So before I answer it, I want to say we wanted to build something else brand new in Snow Leopard, something very sophisticated.

And it would have had to use callbacks in order to get off the ground. And we didn't like callbacks. So that's one of the reasons we did blocks. And so that new thing is, of course, Grand Central Dispatch. So I'm going to spend just a couple minutes here talking about Grand Central Dispatch.

So as you saw in Bertrand's talk, he had a much better animation. You've got work queues, and you fill the work queues up with blocks. And then there's a manager that will spin up a thread to process that. Or maybe it'll spin up two threads. Or if you're on a big machine that's got a lot of threads or a lot of cores, it'll spin up a lot of threads to work on this. In this case, it probably wouldn't spin up more than three threads. And so the threads process the data.

And, you know, when the -- essentially when the data is done, though, the right thing happens. The manager notices and the worker threads go away and it's done. And you don't have to spin threads up. You don't have to do that management of what happens when the work queue goes down. And so it's a very nice facility for just pushing work off and letting something else do it.

You can find out a lot more about that later today. Not in this room though. So the API to add a block of work to Grand Center Dispatch is blocks. And so this is the API. You say dispatch call, you name the queue, you give it a block of work.

And, of course, how can some object that's a stack local be referenced from something that's stored over whatever? And so, as I said before, that construct, that up arrow thing, is a stack local block. It's actually allocated on the stack, and it references things that are on the stack. So the way we do that, of course, is that we introduce a copy API.

So you copy a block, and when you copy the block, the magic happens. So in this case, let's imagine how we would implement lib dispatch. First of all, you would have some kind of a work queue item, and the work queue item would hold a block. And so this is the syntax for declaring a block that takes a void as a parameter and returns a void. It's kind of like function pointer syntax. It's not the prettiest.

To do the dispatch call, what you do is you allocate a new work queue item, and you block copy the block coming in and stash it in there. And then when the worker thread needs to do something, it finds one of the work queue items, and it simply calls it. Again, much like a function pointer. Passing in no arguments and expecting none back. And then it does a block release. And so that's how we do it. We use a little bit of extra memory management to keep track of blocks and make them happen.

Well, okay, sort of. What really happens is that there's some sophisticated data structures going on below. And so on your stack on the left, when you start out with a block, you've got a block data structure and it has a reference to another data structure, a shared variable in this case. And so that's fine.

And so we use sort of a double indirection to get to that shared variable. So it's more expensive, but it works. And so when you do a block copy, we create a new block. And what we do is we sort of move that variable off into the heap under the table.

You don't see that part happening. You just see your copy of the block. And so it can happen that you have, since it's a shared variable, you can use that same shared variable in another block. And so there can be multiple blocks referencing your shared variable. And if those blocks get copied to the heap, then they all get copied to the heap. And they also end up referencing the correct variable.

So we do a little bit of funny business and whatnot such that if the stack goes away, the heap preserves that shared variable. And it also works, though, if somehow your heap copies of the blocks go away first, we keep enough magic around that the stack still has a valid reference to the thing in the heap. So it's just magic.

Let's talk a little bit about the syntax. So sort of the abstract syntax thingy for it is this. It's kind of like a function pointer. It's got a return type and it's got some argument types. We kind of use the abstract part when you declare methods in Objective-C. And so this is what it looks like when you declare a method.

When you actually declare a variable, it's more function pointer style. And again, this is very much like function pointers in Objective-C already. So it takes just a little bit of getting used to. Maybe a lot.

[Transcript missing]

Funny things look funny. They look very funny. So this is how you declare an array of blocks. And if you want to have a lot of fun, here's a function that takes an int that returns a block.

Now, you could do this with function pointers. And if you like doing this stuff, you could play around with function pointers. Or if you've got the seed installed, you could do this here. You could have a pointer to a function that takes an int. I'm sorry, that was a function that took an int that returned a block.

And this is the pointer to a function that takes an int returning a block. And finally, here's a block that takes an int that returns a block. And so it gets ugly. And I have some ideas on how to improve this. But this is what it is in the seed today.

Let's talk about that syntax. When we wrote the block expressions, We actually didn't have to talk about the return type. We infer the return type for the block expression from the return statement if there is one. So if there's a return type, then we know what the type is. If there's no return statement, then the return type is void.

So that's a little bit of shorthand. If you write a block that takes no arguments at all, void, you can say void if you want, or you can just say nothing. So it makes writing short little things short. We like that. And if you have more than one shared variable, separate them with commas.

So let's talk about which variables get done which way. And so local variables, stack local variables, as well as static local variables, are imported as const. And if you try to update them, the compiler will get mad at you. Globals, static file globals, globals, extern globals. Globals are globals. Just use them. We don't do anything magic with globals. Only the local variables do we do something funny with.

Mmm, I forgot something. Blocks are objects. What I talked about was all C stuff for the most part. In Objective-C, a block is actually an object as well. So they can be sent messages like ID variables, They respond to only a few messages. Retain, release, auto-release, and copy are the principal messages you send to them. But this allows you to very conveniently express the idea of create a block on the fly, hand it out in the normal auto-release fashion that you do when you create something.

Under garbage collection, you don't have to worry about the release thing. But under non-GC, you create a block, something has to release it to get rid of the memory. At the moment, any objects that are inside that block literal, when they're copied, are retained. And so you can create cycles that way. We're working on a way such that you can say, don't retain this one, please.

I showed you block copy, capital block copy, and capital block release earlier. Always use those in pairs. Don't mix and match the block copy stuff with the methods, copy and release and whatnot. They share some plumbing underneath, but don't intermix them. So you can use blocks like objects. They can be properties.

It just works. You can use them. Somebody put blocks into an array. They made a copy, so it's a real object. They stick it in the array, and then they can walk through the array and call every block that's in the array. OK, it works. But remember that block literals are the first case and probably the only case we're gonna do where it's an actual Objective-C object created on the stack.

So you can't return them off the stack. You C++ programmers know all this, but the Objective-C programmers in here are going to have to remember this. Happily, the compiler is going to warn you about that. And so if you assign one of these into a variable and return the variable, it's still returning a stack-based object. And so you probably just won't get a compiler warning, but it will still crash for you.

It turns out that the stack-based objects actually do respond to retain and release. They don't do anything, but they respond to the messages. So you can actually stick a stack-based object into an array and take it out before the block goes out of scope. So it's allowed, but it's dangerous.

When you're inside a method, an instance variable is still directly accessible. We import the self instead of the instance variable. So you don't get a const copy of the instance variable. You get a const bit copy of self. So you can update and access instance variables within blocks directly.

Blocks are fairly new. There will be bugs. There won't be blood, but there will be bugs. So we haven't gotten our C++ support yet. We will. The syntax is still a work in progress. Blocks that have shared variables in your seed cannot be copied. You'll get a big warning.

Unless you're running garbage collection and you add this funny marker where you declare the variable. So here, if you want to play around with the full, you know, thin edge, you know, thin ice, if you want to play on thin ice, you play around with making copyable shared variables this way. So in this case, the poly variable is going to be shared. You have to both mark it with the OR bars and use this funny biref thing. But it works. It's fun. We almost have support for non-GCN, but it didn't quite make the seed in the compiler.

Hmm. Let's talk about tools. There is already support, refactoring style support, for converting old style iterations and whatnot into new style. I won't go through these. It's, I don't know. You can try it out. Try it out in Xcode. There's a convert to blocks thing, and it will analyze your code and make some suggested changes, just like refactoring does. So, well, it's kind of interesting.

When you get into the debugger, what you'll find is that it mostly works. You can single step into blocks. They are, after all, just function pointers, it turns out. There are real functions underneath with magic arguments. And so stepping into them sort of works, except that the

[Transcript missing]

By default, imported is just pointers to their stack counterparts. And so you have to go star whatever to see their values, unless you use that BiRef thing. And if you use the BiRef thing, you're just gonna see the raw implementation of how we do it. So that's where we're at with the debugger. It's possible.

So for the talk, Objective-C 2.0 has some great features. Garbage collection, for end properties. 2.1 adds some more. Associative references, this fast allocation stuff. We've added blocks to three different languages. Well, eventually, C++ as well. provides dramatically better iteration, much better callbacks, and it lets the app kit expose things that you can use directly from C++.

So we haven't quite gotten to redoing delegate methods, but it might be possible that for every delegate method that you could imagine today, we might invent a block call-in such that you could set a delegate call-out from any language you want without having to have some intermediate object that calls into your C++ engine or something like that. So we really like the idea that you can take Objective-C code and stuff it into that low-level Q sort stuff or these other APIs or from C++, and it builds a sort of multi-language glue.

Before we close, though, I wanted to show you something for fun. So how many times when you've written code, you sort of need to do something when some other object goes away? So imagine being able to attach one of these new things, this block, attach it to an object such that when the object dies, your code gets executed. Well, here's the implementation.

Let's go through the implementation. First of all, we need a helper object, a death watcher. Whenever you need to attach a block, we're going to create a whole new object that's going to remember the block. So that's what we do. So we create an object. And its only purpose is to have a finalized method that calls that block that got remembered.

So the implementation is create a helper object, death, Assign the death block to be the thing that you passed in. Notice, I forgot to mention, the at property copy. Blocks are objects, and so the at property copy says copy the thing on assignment on the setter. So the death.death block actually performs a copy of the block coming in, which makes it safe to refer to anything that you want to use inside your block. And then you do that set association thing to say when this object dies, when self dies, Detach the death object. It's no longer interesting to have that association.

And so that death object will get detached. And since this is its only reference, it too will go away. The garbage collector will notice that right away. This actually almost works, or this will work under non-GC as well. We have a bug in the seed such that it doesn't work in the seed, but it does work.

Back at the office. So you make that association, and so that's how you can have a block of code, any block of code, multiple blocks of code executed whenever some other object that doesn't know you from anything dies. And so this, you know, this open, blocks open up some sort of metaprogramming opportunities here that we're still thinking about.

If this became a pattern that was very popular, we of course would implement it in much less memory, much less space, much less faster, you know, a lot of, much better. But, you know, this wraps together three ideas from this talk, the properties, blocks, and the set association in one slide. And so I could have spent the whole talk on that. just discussing this slide, but, well.

So for more information, you should talk to Michaelopoulos Jurowicz, the developer. He's not really Michaelopoulos, but he calls me Blaine Garst, so I call him Michaelopoulos. We have some documentation. This is the documentation for the Objective-C 2.0 features. There is some preliminary documentation on blocks and the seed, but you'll have to go search for it. I couldn't find the URL for it.

There are a lot of sessions that are going to talk about Blocks API. If you can go backwards in time, you would want to see yesterday's talk about Objective-C. What's New in Cocoa follows this talk in this room. Later this afternoon is Grand Central Dispatch, as I said before. Tomorrow, we're going to talk more about how you write garbage-collected programs, the do's and the don'ts of writing garbage-collected programs from the programmer's viewpoint.

On Friday, we'll talk about low-level runtime stuff. How do you get into the real nitty-gritty guts of what's going on, including a discussion on, say, our unified exception model between C++ and Objective-C. So, there's a lot more stuff coming up in the show. We have labs. There's a garbage collection lab on Thursday, tomorrow. If you come down to the labs right after this talk, the team will be down. We'll be down there to answer questions about this or any other Objective-C 2.0 or 2.1 feature.

So, before Q&A, I'll just answer some stuff that's going to get asked anyway. So, blocks are available in GCC 4.2. That's not available for the phone. We're putting support into this new compiler called LLVM Clang, the new front end to the new back end. And we'll get C++ done after we go back to work. Will blocks be proposed as a standard? We're certainly going to talk to people. We would like to see blocks used at this low-level level.

We're using Unix APIs because we think they make a lot of things possible. And so we chose the syntax we did. Ugly as it is, but it's possible for it to be a standard with ugly syntax. We had some other nicer ideas, but we really think that this might happen. We don't know.

So C++ OX has something they call closures. And what I just showed you, blocks, blocks are closures. They're full closures. The C++ standard thing called closures aren't quite closures. They only work as iterators. You can't copy them. And they only work, as I understand it, within an STL context. So they're sort of closures. They're sort of like the first implementation of blocks in Smalltalk, but they're not as powerful as ours.