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: wwdc2004-404
$eventId
ID of event: wwdc2004
$eventContentId
ID of session without event part: 404
$eventShortId
Shortened ID of event: wwdc04
$year
Year of session: 2004
$extension
Extension of original filename: mov
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: ...

WWDC04 • Session 404

Fundamentals of Cocoa

Application • 1:13:42

The object-oriented Cocoa application framework makes it easy to take full advantage of Mac OS X's innovative technologies—whether you are building a new application from the ground up, creating a next generation Mac OS X application, or even if you are new to the platform. This beginner-level session will provide the fundamentals for anyone new to the Cocoa framework and its primary language, Objective-C. We explain how to take advantage of common Cocoa programming paradigms such as target/action, delegation, bindings and the document architecture, and show you, through example source code, how to use the power of Cocoa to easily create Mac OS X applications.

Speaker: Ali Ozer

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 morning ladies and gentlemen. At this time would you please welcome Matthew Formica, Cocoa and Developer Tool Evangelist. Good morning and welcome to you all. This is session 404, Fundamentals of Cocoa. My name is Matthew Formica. As was said, I'm the Cocoa evangelist here at Apple. I would love to, throughout the week and through the coming weeks, get your thoughts, ideas and comments on Cocoa. You can email me at [email protected].

After yesterday's keynote sessions, today and for the remainder of the week we're going to dive down into some of the technologies that make Mac OS X the great platform that it is. One of those technologies is Cocoa, Apple's object-oriented, rapid application development framework. And here to talk to us about that today is Ali Ozer, who's been involved with Cocoa and architecting Cocoa for over 15 years. He really knows his stuff. Please join me in giving a warm welcome to Ali Ozer, manager of the Cocoa team.

I'm glad you found your way to session 404, Fundamentals of Cocoa. I'm Ali Ozer, manager of the Cocoa Frameworks team. So, we have a lot of material to cover today, and just to give you an overview of what we're going to talk about, quickly cover what's Cocoa, then give you a quick overview of Objective-C, which is the background you need to see some of our demos.

And the rest of the talk, the bulk of the talk, will be demos where we're going to see the wonder of Cocoa. We're going to do demos, write some code, and get some applications running right in front of your eyes, so you can see what Cocoa is all about.

So first of all, what is Cocoa? It's an object-oriented API for doing full-featured application development on Mac OS X. When we think of Cocoa, we think of frameworks. Now, frameworks on Mac OS X, frameworks are basically just libraries. You're probably familiar with frameworks. There's the Carbon Framework, QuickTime Framework, etc. Frameworks are just libraries which have come bundled with resources, headers. Frameworks also, in the traditional Mac OS 9 definition, like Mac App, include a programming environment where the application doesn't run the event loop. It's not actively running the event loop, but instead it's getting callbacks from the framework environment.

And Cocoa certainly fits that definition of framework, where it is giving callbacks to your application, and we'll see that in action today. So the two frameworks that make up Cocoa, the core Cocoa, our foundation and application kit, and there are many other frameworks that participate in making up what's Cocoa.

And there are the languages, Objective-C and Java. Objective-C is the native language of Cocoa, what Cocoa is written in. We'll see a little bit of Objective-C in a while. And Java is also, is dynamic language as Objective-C, and it works well with Cocoa. And tools, Xcode and Interface Builder, you've seen probably both of them yesterday in the keynote and other sessions, and we'll see those today as well, and how they're crucial in developing Cocoa applications.

So let's now talk about the two frameworks: Foundation and Application Kit. Foundation is the core Cocoa framework for non-UI functionality. If you are writing an application which has no graphics user interface, no menus, windows, etc., you would use the Foundation framework. Foundation includes the root class, which is NSObject.

Almost all Cocoa classes are derived from NSObject. It includes operating system facilities, so you don't have to drop down into Unix layer if you don't want to. You can use the classes in Foundation. It's got classes for internationalization. Of note here is NSString, which is a class you will encounter a lot. It's the class that represents character strings in Cocoa.

We have collection classes in Foundation. Classes for scripting, both making your applications scriptable and being able to drive scripting for other applications. And also classes for XML processing and web access. Some of these were added in Panther, and new ones are also coming in in Tiger. And there's a rich set of classes now in Foundation for letting you do XML and web-related stuff. And much more. AppKit, also known as the Application Kit, is the Cocoa framework for creating applications with user interfaces.

[Transcript missing]

So the strengths of Cocoa, as I said earlier, Cocoa is full-featured and powerful, which means it's got features that let you create applications that you can ship on Mac OS X. As you know, as you probably know, most of the applications that ship with Mac OS X are Cocoa, and there are also a lot of third-party powerful applications out there. So it lets you create full-featured applications. They're not just toy apps.

It's also consistent and easy to use. The APIs for Cocoa are, it's not a very huge API, and it's consistently named. There's also a few design patterns that we use over and over. So once you learn a little bit of Cocoa, once you start getting into some aspect of Cocoa, you start being able to predict what other Cocoa functionality looks like. In addition, you're able to develop your own functionality that's consistent with what Cocoa delivers, which makes everything fit together much better.

Cocoa is also customizable and extensible because it's object-oriented. If we didn't provide the exact functionality you need, you can usually subclass and extend the classes we gave you to do what you want to do. It's also tuned for Mac OS X. Cocoa is not some layer on top of some layer on top of some layer held together with some weak object-oriented stuff. It's a powerful object-oriented environment that plugs well into Mac OS X, takes advantage of Mac OS X's strengths, and the various other libraries available on Mac OS X, you know, Unix, Quartz, et cetera.

And Cocoa plays well with others. Cocoa is written in Objective-C. Objective-C, as we'll see in a minute, is primarily a C-based environment. And so with Cocoa, you can use any C or C++ or Unix library that's available to you on the system. In addition, a lot of open source packages, a lot of other Unix packages out there can be used from Cocoa applications with absolutely no difficulty whatsoever. There's one thing we like to say about Cocoa, and that simple things are simple and complex things are possible.

The things that most applications should be doing, things that are straightforward and simple, it's just, you can go ahead and do it and it's almost no work or a few lines of code. But if you want to do unique things in your application, things that are out of the ordinary, things that will distinguish your application, then Cocoa makes that possible as well.

Okay, so let's just do a quick demo of your first application. I say application because it's not going to involve much code, or any code. So you see an Xcode. I'm going to go ahead and create a new project. When you say new project, you get a bunch of choices, one of them being Cocoa application. There's also other choices for document-based apps, etc. And we'll give it a name: MyApp.

Now when you create an application in Cocoa, it gives you the few files to get started. You'll notice that one of those files is main.m. It's basically a boilerplate, just calls the function to get things started. We almost never modify this file. Another file of interest is the nib file, mainmenu.nib.

This is the file that contains your application's user interface. Very small apps will have one of these. More sophisticated apps will typically have two, three, dozens, maybe many dozens of interface files. These are created in Interface Builder, and this is where you define your user interface. I'm going to double-click on this now to open it in Interface Builder. You'll notice that Interface Builder launches.

You have a design window. You have a window that represents your menu. And you also, down here, have your document window, which represents the various objects that are in your interface file. This is your palette window, where you have various standard controls that come out of the box, and you can also add your own custom controls here as well.

For instance, one thing we can do is drag out a text object. Notice that as you're placing it down, Interface Builder is helping you

[Transcript missing]

You bring up the info panel, the inspector, to a To modify the characteristics of widgets you drag from here. For instance, in the case of the text object, you can choose its color, you can change its border, you can say that undo is allowed, you can choose various other options here. I don't know if you can read that. I'll show you some others. For instance, I can drag out a slider.

With a slider you can choose its minimum, maximum, etc. Things you might expect. Now back to our text view. If you look at this tab here, you see there's connections, outlets, target actions. We'll talk about that in a few minutes. There's a size inspector, which lets you change the various sizing behaviors. If you choose the internal springs, that means the text object will size as the window sizes. And let's also drag out a button.

Now, typically in Interface Builder, one way you do your development is to put your UI together and you go ahead and say "test interface." When you do that, Interface Builder starts running your application using the interface you created. Now note that the slider is alive, it's not doing much, the button is alive, it's not doing much, and in fact the text object is alive.

It even has features like a font panel. I can change the font here. And it even has undo, because earlier I said that undo should be allowed in this text object. I can resize the window. Notice that the text object resizes, and these buttons stay relative to the lower left corner.

So typically, you use this test interface to test your interface if you have objects in the palette. Now, if you develop custom objects that are not yet in the palette window here, then you can't do them in test interface. You typically build your application to take your application to the next level. Of course, you can always build your own palette objects, put them here, and that way you can test your interface in Interface Builder.

Okay, so one other thing to note about NIV files is when we did this, we generated the NIV file. Note that the NIV file has no source code in it. It just saves, archives the objects that we're using here—the window, the slider, the button—and it archives the connections between them, as we'll see in a few minutes. So there's no source code generated. Okay, now let's go back to slides so we can play some with source code.

Thank you. Okay, so in that demo we saw window, slider, button, text view, font panel are some of the objects we saw. As you see here, a standard theme in Cocoa objects is the prefix NX, which lets you recognize that these are standard AppKit objects. Now let's talk a little bit about Objective-C so we can start writing some code. Objective-C, as I said earlier, is a small superset of C. It's a superset of ANSI C. It's got some additional syntax and a few additional types.

It's got a dynamic object runtime, by which I mean metadata that goes with the objects and introspection facilities that allows making decisions at runtime about the objects. We'll see the power of that in our demos. It's also a single inheritance object-oriented language. In Objective-C, here's how you define a method.

A method is, of course, an entry point into a class. Here we have a method called setWidth colon, height colon. One thing to note here is that the name setWidth colon, height colon is split such that each parameter, the W and the H, have a name in front of it. So setWidth colon, height. The full name of the method is setWidth colon, height colon. But the name is split, which, of course, for longer method names with four or five arguments makes it a lot easier to read. Here, this is how you define a name.

What it seems to be doing is taking those parameters, assigning them to maybe instance variables. To invoke a method, which we also say sending a message, you use the braces. So here we're sending a message to an object, myBox, the setWidth colon, height colon method. And in the line below, you see a nested method call, where we're first making a call to array object at index, which presumably returns a box or some object. And then we send that, in turn, setWidth colon, height colon. You can nest these method expressions just like you can nest function expressions. expressions.

One other thing is you can refer to a method, and this goes along with the dynamic object runtime. There is a type, SCL, cell, short for selector, which allows you to refer in this case to the method, for instance, set width colon height colon by using this at selector syntax. Once you do that, you have a variable which represents this method name, and then you can apply this to different objects.

Note that this is not a function pointer, because depending on what object it's sent to, it will invoke different pieces of code. A little more Objective-C. You can refer to an object by either saying "box*" or an "id". The difference here is that these help the compiler determine what type of methods are valid when sent to that object. "box*" indicates an instance of a box class.

"id" indicates any object. You can refer to this, meaning yourself, in the context of an object with self. And finally, off-note, is constant strings. As I mentioned earlier, we have the NSString class. We have a construct for creating constant strings in your code, and that's @, here you see the constant string @helloWorld being assigned to a variable of type NSString. Okay, now that we're armed with some Objective-C knowledge, let's go ahead and write some code. And the first thing we're going to do is do some custom drawing.

To do custom drawing, you use a class called NSView. It's an abstract class, meaning you almost never use it as is, but you subclass it. And you would subclass it to do custom drawing. And you can also subclass it to do event handling, drag and drop, and plenty of other stuff. It's an abstract class with a lot of functionality. To do custom drawing, you override this method called drawRect.

To do your custom drawing in the context of a view, you would use these classes. An NSBazierPath class that represents paths. NSColor, which represents colors. It's actually a fairly rich class. It's not just your garden variety RGB, but it can also represent CMYKs, patterns, and a whole other set of color spaces. The NSImage class, which represents images. And note that you can also use the full functionality given to you by Quartz. Quartz is a C-based API.

It presents a PDF-based drawing environment. And Cocoa uses Quartz. So the drawing environments are compatible. And Quartz is written in C, so the APIs are in C. So from Cocoa, you can call into Quartz all you want. So in cases where you can't call into the NS functions for some reason, Quartz is also available. So let's do a demo of custom drawing.

Okay, so now we're back in Xcode. The thing I'm going to do now is I'm going to add a new class, a subclass of view, because as I said we need the subclass view. We're going to say new file. And note that there are some pre-prepared entries here for you: Objective-C class, Objective-C document subclass, so on. View is one of them. We'll choose that.

This is just a little convenience. You can just choose the basic class and do some of the things it does for you. But we'll choose NSView subclass. We'll call it .view. We'll create .view.h, which you'll see in a minute, and we'll add it to our target. As you see, .view has been added here to our project.

Hide that area. Now, we're looking at .vue.h, which describes the interface to your application. For this demo, I want to edit the .vue implementation file. So I'm going to click this button here, go to counterpart, which switches to the .m file, which is where your class definition, where your methods sit.

So if we look at .vue.m, here are the methods. As a convenience, again, only as a convenience, it generated the source code for us, an init method, thinking we might want to initialize, and a draw method, thinking we might want to draw. So for now, let's just look at our draw method.

And let's just customize it. Now during this demo, I'm going to be taking advantage of a demo assistant I have hidden in the computer here. And as I type keys, it will type for me. Let me show you. There's a comment. And there's three lines of code that magically appear. These three lines of code, I ask self-- that's the NSView for the bounds. Bounds represents the area of the view. And that's assigned to this bounds variable, which is NSRect. NSRect is nothing magical. It's just a C structure that represents a rectangle.

Then we do a nested method call here, method invocation, where I ask NSColor class for the white color. That returns an instance of color, which represents white. And I tell it to set. So that sets the current graphics context color to white. And I call a function rectfill to fill the bounds with white.

So that clears the view to white. Note that I could have used BezierPath here, but I'm using this function rectfill to show you Cocoa is not limited just to classes. And in the classes and methods, Cocoa also has some functions where appropriate. So this clears our view. Now let's go ahead and draw something interesting, which in this case is a dot. For that, I will first put a comment, and then create a rect that represents the bounds of the dot. Whoops.

Then I will set the blue color, just like up here. And then now I will use a Bezier path, creating a Bezier path that represents an oval with this rectangle. In this case it's a circle because the rectangle is a square. And I will tell it to fill. So this will create a circle and then fill it with the color we created here. So let's pretty up this a little bit. Let's save. And now I'm going to hit build and run, Oh, I forgot one very crucial thing. Let's go back to our demo here.

The thing I forgot is to go into our interface builder and actually create the UI for my application. In interface builder, I want to go ahead and get rid of these three things, which we used earlier. And instead, I'm going to be using, under the containers tab, a custom view. Again, let's follow Ivy's guidelines, drop our custom view here.

Let's make it size centrally, just like we did with the text view. And the one more thing we're going to do now is tell Interface Builder that this custom view is of type dot view. And to do that, under the classes in the document window here under classes, I can either go ahead and subclass this NS view. I can hit the right mouse button or control mouse here to do that. Another thing I can do is do read files.

[Transcript missing]

So I can come to my custom view here, and in the inspector, I can specify its class. Note that the various NS classes are listed here already. I want it to be a subclass of—I want it to be an instance of dot view. So I do that, I save here, I hide interface builder, go into Xcode, and let's run.

As you see, we now have our window with the dot in it. And as I resize the window, the dot stays fixed relative to the lower left corner. That's because the drawing, the coordinate system is fixed relative to the lower left corner of the drawing environment. You can see the white area here, which represents the view. And the view resizes and redraws automatically as I resize the window.

So, one of the things you do get just by implementing that few lines of DrawRect is you get a lot of other functionality available to you. Let me show you what I mean. I can come to dot view and I can say make subview of scroll view. Okay, and let's make that scroll view also sizable. I'm going to save this.

Go back to Xcode and let's build and run again. So notice that suddenly our scroll view, our view is contained within a scroll view and we can scroll it. And again, just because you've implemented your view so it knows drawing, you get scrolling for free. Let me show you one more thing you get for free. Let's undo this scroll view.

Oops, don't want to undo that much.

[Transcript missing]

I want to show you how you can make it print. We haven't talked about target/action, we'll talk about it in five minutes, but one way to make it print is to connect the print menu item to our view and just select print here. I'll talk about this in a few minutes if you haven't seen this before. Let's save.

Back in Xcode, let's build and run again. If I now were to go in here and say print, We have a print panel, and one thing you'll notice, of course, we don't have a printer. If we were able to print, you would get something like this. This is the artist's concept drawing of what the dot might look like on paper. But if you don't believe me, I'll do preview, which is always something you can do when you're testing your printing. And the preview application will launch, and you see that the dot is printed on that page. Okay. Okay, so let's go back to slides, please.

Next thing we want to do is add some excitement to our demo and do some event handling. As I mentioned earlier, NSVU is your class to do most of your event handling. You override methods such as mouse down, mouse drag, key down, depending on what you want to do to handle the appropriate kind of events. There is also another class called NSEvent, which represents various kinds of events, and you get event-specific information out of it. For instance, the mouse location, the keyboard, what key was pressed, what tablet pressure, and so on.

You override methods such as mouse down, mouse drag, key down, depending on what you want to do to handle the appropriate kind of events. There is also another class called NSEvent, which represents various kinds of events, and you get event-specific information out of it. For instance, the mouse location, the keyboard, what key was pressed, what tablet pressure, and so on.

Now one thing I want to do now is parameterize some things about our dot. So we go back to the interface file, .u.h. And I'm going to go ahead and add two instance variables. Instance variables are declared in the header file in this area here. So I'm going to add an instance variable that represents the center location of the dot. And this point is a structure that represents a point.

I'm going to have a radius for the dot. It's a floating point number. Note that coordinates are floating point numbers, so we use floating point numbers where appropriate. Okay, so now we added two instance variables to our dot. I'm going to save this file. I'm going to switch to .vue.m.

Back in the initWithFrame method, the place where I said we do our initialization, I'm now going to initialize those two instance variables. So I'm going to delete this code, and I'm going to set the center to 100, both the X and Y locations of my center, and I'm going to set the radius to 50. So this is—we're initializing these variables to their initial values.

Now here we are drawing our dot using fixed numbers, so let's replace this line to use the new instance variables. Now here we are drawing our dot using fixed numbers, so let's replace this line to use the new instance variables.

[Transcript missing]

To the view's own coordinates using this method, convertPointFromView. This gives us our center.

This is where we clicked. This is what we want our center of our dot to be. And note here that we're just assigning to our center variable. Pretty straightforward. One more thing we want to do now is, given that now we have a new center location, we want to redraw our view.

Instead of calling drawRect, for instance, to draw our view, we just basically tell the view that it needs to be redisplayed. And then AppKit will redraw your view at an opportune time later. What this allows doing is coalescing various drawing needs to the top of the event loop, so you're not drawing things over and over, things aren't overlapping, and so on. So to do that, you tell the view that needs to be displayed. Self.sit needs display, yes. So we take the event, we assign our center, and we just tell it needs display. So let's save and let's run.

Okay, now as I click around, the dot is redisplayed at the new location. Note that when I do this, because I told the whole view to redisplay, the whole bounds of the view is redisplayed. In the performance talk on Friday, you can learn about how you can do this much more efficiently, of course. Here, we're just taking the easy way out. Let me show you one more thing.

I talked about mouse-dragged as well. Mouse-drag is an event you get when the mouse is clicked and it's being dragged. We'll have mouse-drag do the same thing as mouse-down, so we'll just call self-mouse-down. Let me save. Let me go ahead and run the app again. Now, as I click around, the dot moves, but if I click and drag, again, the same thing happens. So the view is being constantly redisplayed, being told to set needs display and so on. So that is fairly straightforward to go ahead and make our dot dance in any way we want. Okay, let's go back to slides, please.

Okay, again, drawing, event handling, those are pretty mundane things. How about some UI elements, so your app looks like a Cocoa app or a Mac OS X app? UI elements are represented by a class called NSControl, which is a subclass of NSView. The daily controls you see, the regular controls you see include text field, button, slider. We also have some new controls, a little more sophisticated, token field, date picker.

These are new in Tiger. We also have some really sophisticated controls, table view, browser. Browser represents the multi-column view you see in Finder. Table view represents a table of elements. These are typically made up of other UI elements and they're fairly sophisticated, but they're still subclass of NS control.

There is also this other class which we won't talk about today. But let me talk about it. It's the... Every control has a cell, and in some cases, controls have multiple cells. Cells are basically what controls are made up of. Typically, you just use a control. However, if you were subclassing control for your own purposes, you might find that you need to also subclass the corresponding cell, and that's why we're mentioning it here. And in some cases, like table views and browsers, you might be using multiple cells to get what you want to get done, to represent multiple pieces of that. Cells are not subclasses of views.

They are lighter-weight objects. The main design pattern for UI elements in Cocoa is this target action design pattern. Target action controls have a target and they have an action. Target is the object they send a message to when they're fiddled with. So you play with the slider and it says "I've been fiddled with." The action is the message they send, so what they actually say.

They don't actually say "I've been fiddled with." An action is a single argument method, always, and we'll see that in our demo. And note that these are not magical things. They're really just instance variables of controls. Okay, so armed with that knowledge, let's go to our next demo.

Grab some water. The thing we want to do now is want to be able to change the size of the dot. Okay, now, to do that, we're going to be adding a method called changeSize, which is an action method. So this is an action method, change size colon, and it has one argument, sender. And this is what makes an action method. And the argument, sender, is the UI element that sent this message.

Now we're assuming that the sender is something that's capable of producing a floating point number, because we want to change the size of the dot. So what we're going to do is ask the sender for its float value. Most controls have a method called float value that represents the floating point value that they've been set to.

We're going to ask for its float value and we're going to assign it to our radius instance variable, which we had added last time. And again, just like we did up here, we just need to self set needs display. So we're going to change the radius, self set needs display. Now I want to do one more thing.

We've been adding methods to our m file, but we have not been adding them to our h file, to our interface file. For methods that you expect to be called from the outside world, it's a good idea to do that. So let's go ahead and add the change side declaration to our dot view. As you can see, it's straightforward. It's just change size colon with a semicolon representing that this has this method. Now we'll see why this is useful. I'm going to switch back to interface builder.

Now, in Interface Builder, I'm going to tell Interface Builder to re-read that .vue.h file. So, if I go back into Classes tab here, I can say, either by right-clicking here or by going to the Classes menu, "Read files" or "Read .vue.h." So this will cause Interface Builder to re-parse .vue.h, which means it now knows that, um, Should know, okay, let me go back and see one thing.

I'm sorry, this was, this is all proper, I'm wondering what I could have—let me get it to read the file again, just in case. Let me say parse here, go back to interface builder, hide other stuff. "It's fairly straightforward stuff, it shouldn't be..." "Okay, I will change this to IB action, which is often not necessary.

IB action is another defined for void, and what it says is it marks this method explicitly as an action for IB to recognize. Let's see if this actually does the trick. It often is not needed. So if I go ahead and reparse the file, and let's make sure the file is coming from the right place, it is, dot view dot h, parse.

Okay, I'm sort of perplexed by this. Luckily, at this point, I will quit Interface Builder, I will go to the oven and pull out Okay, so there we go. Pull out a finished version of that. And that has always worked, so I'm sort of curious as to what's going on there. But if we look at .u.h in the prepared version, it looks exactly the same.

And let's go ahead and Click on mainmenu.nib to open up our main.nib file. Notice that if you look at dot view here, Oh, I know what's wrong. I'm sorry. I was just getting confused there. It's not even... In fact, I will go back to our original one and we'll open that up. Go back, show that there's nothing magical going on here. Let's open up our main menu.nib.

So, if I look at the class now, dot view, you'll see that

[Transcript missing]

The time you see your action is if you have a slider, for instance, and you make a target/action connection. You make target/action connections by actually control-clicking on the UI element that's going to send the target action and dragging towards the element that you want the target to be. So in this case, dot view. Now I'm still not seeing that there. I don't know what that's about. Let's just go back to our pre-prepared version.

We are back in the interface builder. We will open our main menu.neb. So if I make a connection from here to here, you'll notice that the actions this represents includes printfacts, which were already there, and now a change size method. And I just make that connection by choosing change size and hitting connect. And this establishes a target/action connection between my slider and the view. So sorry about that. I know what was going on over there. But now let's save this. Let's hide. And let's go ahead and run our demo.

As you can see, we can still move the dot around. We can resize the window. And as I move this, the dot will actually resize. That's because as I move this, it's sending change size colon over and over. And the change size method is changing the radius, if you can remember back that far. And then it's redrawing the view. So that's what's going on.

The other things we did here, by the way, when we did the slider was in the attributes of the slider, we changed so that we could change its minimum and maximum values, the current value. And we also specified that it continuously sends action while editing, which makes it send its action over and over again. Okay. So that was our slider. So let's go back to slides to talk a little more about target action. So, what we saw there was that the slider's target is the dot view.

Slider has two instance variables, target and action, and our dot view has a method called ChangeSize. The target is set in InterfaceBuilder to point to the dot view, and the action is specified in InterfaceBuilder to be selector of ChangeSize:. So this is where you see that selector business come in.

But note that we didn't have to do this programmatically or anything. We were able to specify this in InterfaceBuilder, or we were almost able to specify it in InterfaceBuilder. In InterfaceBuilder, stored this information in that Nib file. As I said earlier, target and action aren't anything magical. They're just instance variables, and you can set them programmatically using methods setTarget and setAction if you want.

Okay, now I'm going to talk a little bit more about target/action, but before that I want to talk some about memory management.

[Transcript missing]

A freshly created object, whether you create it with alloc and init—alloc/init is the paradigm for creating new objects, you allocate it and you initialize it—or whether you create it by doing a copy of another object, a freshly created object has a reference count of one.

You call retain to add a reference count. You call release to remove a reference count. So this is just reference counting. Nothing really secret here. And the object is deallocated when its reference count reaches zero. And that happens by the object being sent a dealloc method, which is the opposite of alloc. So let me give you an analogy here, just to make sure this is clear.

You go into a restaurant and you tell the waiter you want a table. So the waiter goes and gets a table. That's allocating the table. Then the waiter puts a - well, it's a fancy place - he puts a tablecloth on the table, and even gives you forks and knives and plates. That's initializing the table. And you sit at the table.

So as long as you're sitting at the table, the waiter is not going to come take the table away. The moment you get up and leave, you've released the table, and the waiter will come and deallocate the table and make it ready for the next person. So that's getting rid of it.

Now, while you're sitting there, if a friend of yours comes by and sits down at the table, they also retain the table. Now, there are two references on the table. The waiter is not going to remove the table until both of you leave. If eight friends come, now there are eight references to the table. So until the last person leaves, the table is retained.

Copy Let's make an analogy for copy. You come into the restaurant and you see a table that looks gorgeous. It's got a bunch of people sitting at it, a good-looking table. You tell the waiter, "I want a table just like that." But you don't want to go sit at that table because you don't know those people.

So instead, the waiter creates a table just like that for you. That's like making a copy of that table, but it's your own copy. You don't care what happens at that table. You've got your own copy of the table. Now one more thing I'm going to mention at this point is auto-release.

I'm going to mention at this point, I'm not going to talk about it for the rest of the talk, but it's an important concept. It's like release, but instead of releasing the object now, it releases the object later. And this turns out to be pretty handy given Cocoa's object ownership rules. And you will see this and probably use this in your programming of Cocoa. Now having covered memory management, let's talk about object ownership in Cocoa.

Object ownership is important in dynamic environments, where object-oriented environments, where -- you know, you're constantly passing objects between things. People are allocating objects and returning them to you. And the question that's always asked is, who frees this object? In Cocoa, the answer is -- there's a consistent answer for 99.9 percent of the cases, and that's object ownership is not transferred across calls.

What this means is if somebody returns an object to you as a result of a function call or a method invocation, you do not free that. You do not release that thing. You just use it, you know, assuming you use it, and that's that. Same goes for when an object is passed as an argument. The "The person who got the object does not free it. They will use it."

If it turns out that an object is needed long term, you can retain it and then release it later on. So the rule is, whoever creates an object, or retains an object, or copies an object, whoever is responsible for incrementing the ref count is also responsible for releasing it. So, basically this summarizes that. You pass someone an object and you don't care what they do with it. They will retain it and release it if they want. So, with that, let's look at a little bit more of target/action and do something slightly more sophisticated.

If it turns out that an object is needed long term, you can retain it and then release it later on. So the rule is, whoever creates an object, or retains an object, or copies an object, whoever is responsible for incrementing the ref count is also responsible for releasing it.

If it turns out that an object is needed long term, you can retain it and then release it later on. So the rule is, whoever creates an object, or retains an object, or copies an object, whoever is responsible for incrementing the ref count is also responsible for releasing it.

So, basically this summarizes that. You pass someone an object and you don't care what they do with it. They will retain it and release it if they want. Let's go back to our source file, .u.m. and let's add our changeColor method. So, the changeColor method, we are going to ask sender for its color. We are assuming that the sender is something capable of generating a color.

We're going to call a method called "setColor" on ourselves. I could have done like here and just set the color in here, but I want to show you, just, you know, let's write an accessor, a set method to see how it's written. So we're just going to get this color from the sender and we're going to send it to setColor.

Note that here already you're seeing Cocoa's object ownership rules in action. We're getting a color from this thing, but we don't free the color. We just use it, we don't worry about what happens to that color. You know, if we wanted it, we will keep, hang on to it. Here we're sending the color to this method and again, we don't worry about what happens in that method.

That method will do the right thing. So let's go write that method and you'll see what I mean. setColor takes a color as an argument. If the new color is not exactly the same as the old color, that's both for optimization and for a few other reasons, but basically it's a good idea to do this. If the colors aren't the same, we release the previous color instance variable.

We make the color instance variable point to the new color, but we retain it, because we want to hang on to it. The third thing we want to do here, just like we've done in other places, like here, is say "set needs display: yes", which of course, because we changed state, we want the view to be redrawn.

So note, this is basically what your typical set method might look like. You release the old value, you retain the new value after checking they're not the same. So, now we know how to change the color, let's do a few more things. One thing we want to do is make sure our color is initialized.

So we initialize our color to the blue color, and we retain it, we hang on to it, because we're hanging on to it long term. In the drawRect method, oh, one thing we have to do now, one thing we need to add is, because we're hanging on to our color instance variable, when we're being deallocated, meaning when the view is going away, we want to get rid of our color instance variable. So for that, we implement the dealloc method I mentioned earlier.

And we release the color. So when we're going away, we make sure all the resources we hang on to are going away. And we call super dealloc. Super is a way you call your super class implementation. And that's it. So typically, you will have dealloc methods which do... We do one more thing. Instead of the blue color here, we now just set the color we have.

So we just say color set. So I'm going to save this right now. And let's go back to interface builder. Interface builder, I'm going to pull the slider aside. I'm going to drag this little control. This is a color well. It lets you change colors. Now let's go ahead to classes and keeping our fingers crossed, Let's read files.

Now, we're in, I believe we're in this directory right now. Let's just read that file and now we can make a connection from our color well here to the dot view and we choose change color connect so this is what should have happened before but anyway demo gods were taking a lunch break okay so now we've connected that let's save let's hide or let's bring up interface builder and let's build and go okay so as you can see we can still change the size and if i click on And here, the color panel comes up, and I can now change the color.

You can use any mode of the thing. You can use the crayon, any mode in the color panel, as I said earlier, you can use grayscale, you can use CMYK and so on. All these colors are represented by NSColor and they work in the context of our application.

Having done all that, let's go back to Xcode, let's look at our dot view. One thing you might be noticing at this point with dot view is, it's getting a bit unwieldy. It's got like 25 lines of code. That's getting a little too big. So let's see how we can start cleaning things up and making this a better structured app. And for that, oh, can we go back to slides? I'm sorry.

We're going to talk about Model-View-Controller. Model-View-Controller is a... is not a Cocoa-specific design pattern. You've probably heard about it used in other cases. But it is used in Cocoa to create bigger, better structured applications, and it makes document-based apps and so on much more easy to do and much more of a joy.

So Model-View-Controller is a design pattern where you break your application into three pieces: the model, which is the back end of the application where your data is, the view, which is your user interface, and the controller, which is the middleman. The controller usually has a variety of roles. It usually manages the connection between the model and the UI, and it can have other roles. In some cases, controllers can actually be fairly thin, or you can cut out the middleman altogether in the very simplest cases.

It turns out that's one of these cases here. So we'll look at Dot-View and we'll see how we can separate the model and the view pieces. Here's what our Dot-View looks like now, before our surgical separation of the back end and the front end. The Dot-View has a center, a color, and a radius. These are the instance variables in our class. After we're done with our surgery, we're going to have a class called Dot, which has those three instance variables. So the Dot class is going to represent our dot.

And we're going to have a Dot-View class, and all the Dot-View class is going to do is point at the dot. So it's just going to have an instance variable that represents the dot. So this is fairly basic MVC, Model-View-Controller, at this point. The dot instance variable is going to point at our dot object. OK, so let's go to the demo machine for this piece now.

Okay, so let's go to the demo machine for this piece now. This time I'm going to choose Objective-C class because I just want a subclass of NSObject. The dot is just a simple data-bearing object, not a subclass of view, not a subclass of control. And let's call it dot.m, also create dot.h.

So, interface builder creates two files: ..h and ..m. In ..h, we're going to go ahead and put our instance variables. point radius and color so the same instance variables we had today okay now and let's go declare some methods for dot we're going to have a set center method to set the center and now you're thinking okay we need set radius and color for now i'm not going to do that we'll talk about that why and we're going to add a draw method and some of you are thinking draw dot is a model object why does it have a draw method well it's okay for model objects to know how to draw in this case it just knows how to draw but doesn't make any assumptions about where it's going to draw what view hierarchy it's in what the ui element is it just knows its you know location its size and its color so when told to draw it can draw itself so it's an appropriate thing to do here so we'll just go ahead and add a draw method so let me now save this and switch to the dot implementation file dot.m now in the implementation file of dot we're going to add an init method as i mentioned earlier in it is the way you initialize yourselves in the context of the view class we saw init with frame which was how views are created in the case of a simple and a subject subclass like dot we would use init so we do init we do super init and if that succeeds we go ahead and initialize ourselves set the center set the radius you've seen this before and set the color and then we return self so this is what a typical initializer might look like you know just like dot views now let's go ahead and add give ourselves a dialog method which is again just like dot view release the color and return now we declare two methods earlier let's go add those the draw method this is again taken from dot view dot view we create our dot rect we set the color and we draw the oval okay and finally we said we're going to add a set center method let's go ahead and do that set center is going to set the center instance variable to the point that's passed in here okay and for now this is all we're going to do we're just going to change the center note that in the context of dot view we were doing self set needs display yes but here we're not doing that because this is just a model object and it doesn't have any assumptions about views okay so this takes care of our dot class let's save this now let's switch to our dot view dot h in dot view dot h we want to remove our instance variables We want to remove these two methods. Now that's nice. Getting rid of code is always nice. Less maintenance, whatever. Your boss doesn't have to know you're doing less work. So now we can switch to .vue.m.

Okay, and do the similar modifications here to lighten the load a bit. Turns out we can get rid of init with frame. Okay? Because we don't need to initialize any of this stuff anymore. Turns out we can get rid of dialog. In DrawRect, which is how the dot view draws itself, we will leave clearing of the background because the view still needs to draw itself. But instead of all this code to draw the dot, we can now tell the—whoops, I'm sorry, that was a piece I forgot to show you in dot view, I mean dot view dot h.

In DrawRect, which is how the dot view draws itself, we will leave clearing of the background because the view still needs to draw itself. But instead of all this code to draw the dot, we can now tell the—whoops, I'm sorry, that was a piece I forgot to show you in dot view, I mean dot view dot h.

In addition, we want to declare the instance variable ".", which is two pieces I forgot. So the ".instance" variable. Note that we have an IBOutlet specifier here. This just specifies that this is an outlet, and we'll see what that means in a few minutes. An outlet is basically an instance variable that is visible to Interface Builder, and you'll see it in a few seconds. So let me save this again, switch back to .view. So back to DrawRect, back to the discussion there.

Instead of drawing the dot, we just draw the dot by telling the dot to draw. So all that code is simplified. Now let's leave our mouse down and mouse dragged in here, because these are still primarily view operations. And we can get rid of change size and set color and all this, because we're not in the business of changing size anymore. The view doesn't care anymore. It's the dot's responsibility to be doing those things. So get rid of this code. So suddenly dot view has come back to a manageable size. That's good. So let's save this file. Now we need to switch to interface builder.

Now one thing we want to do is tell Interface Builder about our dot object. Let's go ahead and say, "Read files." The moment we say that, Interface Builder now knows that there is a subclass of NSObject called ". ". Now, why did we do this? Why did we bring ". " into the picture? Because I want to go ahead and create an instance of ". ". Creating an instance of ".

" is just like what you did What you did here, when you dragged dot view out of here, you basically instantiated a dot view. Here I want to instantiate a dot, but dot does not have a visual representation on a window because it's a model object, so you instantiate with this menu item here, instantiate dot.

That adds it to our instances tab as a new object. You can see that here, dot. Okay, well that's good, so now we have a dot object. That represents the dot. Now I want to go ahead to dot view and I want to reread the dot view file. Let's go ahead and read the dot view file.

It's warning me that because we changed the interface of the file, it's not compiling with the earlier one. Let's say replace. So now the dot view has been changed. It now knows the new dot view, not the old dot view. As a result of this change, we can now connect dot view to our dot.

Well, what's this connection about? That's where we're connecting the dot instance variable of the dot view to our dot object. You can see here that the dot object is here. It's known to IB now because we read the file. So, we're going to use the dot instance variable to connect the dot view to our dot object. So, we're going to use the dot instance variable to connect the dot view to our dot object. You can see here that the dot object is here. It's known to IB now because we read the file. or bring up Xcode, I'm going to run this.

Oops, failed. We did one little mistake. Oh, yes, I forgot to finish some of the coding here. Sorry about that. If we look back at the By the way, that was a case of a build failure. When you click on this little one here, it tells you that center is undeclared.

Indeed, in my methods here in mousedown, we still refer to center. I needed to change the mousedown method. Instead of doing this, I just need to tell… The first step is to set the center. We tell the dot to set center, and we don't need to do this anymore. For now, I will leave the set-needs-display in place.

So with that, hopefully we're bug free, let's run our application, let's hide it. Okay, so, there's our dot, it still moves around, okay, and we didn't connect the slider or the color well, so we can't do much, so you're thinking, okay, we did MVC, now our app is even less capable, and it's not going to be bringing any more revenue, why did we do this?

Well, the magic of MVC is that the UI and the model are separate, and that means that you can tweak the UI in ways which the model doesn't care about. An example of what that means is I can now come here, drag a new window out, and I can I can come to my containers tab, drag a new custom view out. Let's just put it all the way there. Okay, let's make our custom view.

I can come to my containers tab, drag a new custom view out. Let's just put it all the way there. Okay, let's make our custom view. I can come to my containers tab, drag a new custom view out. Let's just put it all the way there. Okay, let's make our custom view.

Well, they are the same, it's just we got some connection problem here, where we got some synchronization problem. But, as you can see already, the promise of MVC is beginning to shine through. I have the same dot visible in two different windows and we're able to add a second view to our dot with, you know, no code.

It was just in an interface builder. So, what do we need to do to get these dots to synchronize each other? And so this now is when we start talking about a few other things that make, you know, expand the picture of MVC in Cocoa. So if we can go back to slides.

So now we're going to talk about KVC. Now, we have MVC, we have KVC. Don't confuse these with MFC or KFC. One's a fried chicken and one's something else. KVC is key-valley coding. It's a mechanism to set or get the properties of an object indirectly. What I mean by that is, You use these methods: setValueForKey and valueForKey. The property is the property you want to set or get. With KVC, instead of calling setColor on my dot, I would call this method setValue:forKey, pass the string color, that's the property, and I would pass the new value.

Now you're thinking, "So I have to go write a set value for key methods so that KVC works?" The answer is no. KVC uses an object's existing accessor methods or its instance variables to do its job. So, if you have an instance variable named color, or if you have accessor methods named setColor: and color, which is the way you would set and get the color, note that you can also have setColor: and getColor if you would like to name your things gets. In Cocoa we often don't do that, but that's another naming style. Assuming you have one of these two ways, and a bunch of other possibilities, your dot object becomes KVC compliant for the property color. And you don't have to write any code, it's automatic.

If you have a variable called color, you are KVC compliant for the color property. That's fairly simple. Now let's talk about key-value observing just quickly, because this is needed to complete the picture. Key-value observing, KVO, enables observing changes in KVC compliant properties. What I mean by that is, if you have a KVC compliant property color, you can observe changes to color.

Here's the method you use. This is the gnarliest method I'm going to show today. Luckily, we won't be using two of its arguments, so just ignore them. You can add You can observe some object's property by adding yourself—so this argument here, observer—for the property, and you send it to the object you want to observe.

That's what this does. And whenever that property changes, the observer will get this method: observe value for keypath, the property in question, and of object. Now, a whole lot of methods here. It's probably best to show this in action, and that's what we'll do right now. A little KVO to see our dots work better.

Okay, so it turns out this is just in Xcode now because our IB connections are all done and they should, you know, they're pretty close. Okay, in Xcode, Okay, so turns out this is just in Xcode now, because our ID connections are all done, and they should, you know, they're pretty close. Okay, in Xcode, we're gonna have to do this. So this is not like the init method, which is fairly early. This is a, this is called after everything's been created, so the world is a little more consistent.

So in AwakeFromNib, we establish our observations. And the way we do that is we tell the dot object to add observer self. So the view is becoming an observer of dot for key path radius, 'cause we wanna observe the changing of the radius and the arguments you're supposed to ignore. Now we do that two more times. Not like that. Click here and do it again. For center and for color. So we're observing the properties of the dot we care about. Okay? So pretty straightforward, just three observations.

Now the next step, as you may remember, is now you need to implement the method which gets called when the observation is done, when the thing changes. And that's this method. Observe value for key path of object change context. And in this method, this method will get called whenever the radius, the center, or the color changes.

But I don't really care which one changes. All I wanna do is redraw the view. So all we're gonna do is say self set needs display yes. That's all we're gonna do whenever we hear that there's a change. Now of course in other cases, you might wanna do more specialized things. Now for completeness, I will show you one more thing you should do, and that's to remove yourself as an observer at the appropriate time. This could be, depending on the application, it'll be different. In this case, we do it in the dialog method.

So as the view is being deallocated, it should remove itself as an observer for the three variables that it added itself for. So that's these three lines: removeObserver for keypath, removeObserver for keypath, removeObserver for keypath. Pretty straightforward. Now let's come down here. The drawRack doesn't change. There's one more change we can do right now.

Note that before we were cheating. In mouse down, in addition to setting the center, we were causing the view to be redisplayed. And that explained why the view itself updated when I clicked in it, but the other view didn't, because the other view did not know to display itself. But now that we have this great observer mechanism, I can remove this line of code here, because Here we just tell the dot, "Here's your new location." And the dot will hopefully tell us that its location changed and will redisplay everything.

So, let me save, let me go back to interface builder, I'm sorry, not interface builder, let me build and run. Click the application. Build and run. So now, if we did it right, the demo gods are with us. As I click here and move, the other dot also moves. They are perfectly in sync, and they are both doing self-statement display.

They don't know about each other, and the good thing is our dot object, the model object, knows nothing about these views. Absolutely nothing. It's just told to draw in each view by the view when the time comes. So, there you go. Now, one last thing I want to do is these dots here, the slider and the color, well, aren't connected, and let's hook those up. So back to the slides, please.

And that brings us to bindings. Now bindings was introduced—it's a technology introduced in Panther. It's a… It's a technology that is used to automatically keep attributes of different objects in sync, or to be a little more exact, it keeps model and view values synchronized and it eliminates the glue code you often need to create.

for that purpose. By glue code I mean, it's the kind of code where you respond to the slider changing, it might be an action method, and as a result of slider changing you wanna update your dot size, and maybe you wanna go update a text field, maybe you're gonna go change something in a database and so on. You're writing glue code all the time.

In many cases, even in Cocoa if you're using target action such, you find yourself writing glue code over and over. And this is what bindings illuminates. And we'll see of course how that is with another demo. Bindings leverages KVC and KVO, and it fits very well with MVC.

So, and again, we'll see how bindings can be added to this application easily. So exactly what bindings lets you do is it lets you bind attributes of views to properties of model objects. That's worth repeating. There you go. And it's worth showing a picture for. So here's our dot.

So the dot has the properties, it's a model object, that's properties, center, color and radius. Here's our slider. Now, I've removed the target/action methods from slider in this picture because now we're not dealing with target/action anymore. Instead, we're just looking at the value of the slider as an attribute of the slider.

With bindings, I bind the floating point value of the slider to the floating point radius of the property of our model. Similarly, I have a color well, and I bind the color value of that to the color value of my dot. So with bindings, this is the kind of connection you establish.

The good thing here is that what this implies is if I change the dot's color, the color well will change. If I change the color well's color, the dot's color will change. So it's a two-way binding. And bindings, of course, uses KVO, the observing technology we saw earlier, under the scenes to get this to happen.

If you were to call a binding method to establish the binding programmatically, you would use this method. Bind colon to object, blah, blah. Here's how it would look like. I would tell the slider to bind its value to object dot to its property radius. This is how you would do it. Luckily, you almost never call this method programmatically, because bindings are usually established in Interface Builder, as we'll see right now. Okay, if we can switch to the demo machine.

The good thing is, I'm talking about bindings and interface builder, but this part of the demo doesn't involve Xcode anymore. It's just interface builder. So let's switch to interface builder. So here's our window one, here's our slider. Now for this part, for establishing bindings, you go to the bindings pane of our inspector here. Okay?

And you'll see that slider has its max value, its min value, and its value ready to be bound. So these are the attributes of a slider you can bind. It's also got enabled, hidden, and so on. And it also has tooltips. So you can bind any one of these. In this case we're interested in binding the value so I'm going to click here. Now you can bind to any object that Interface Builder knows about in your Nib file.

Well, it knows about the dot because it's right there in our document. So I'm going to bind the slider's value to the dot object. I'm going to bind it to the keypath radius. That's the property. By keypath, I mean property there. So I've now established the binding between the value and the dot's radius. Let's do the same thing with the color well. I'm going to bind it to the dot. I'm going to bind it to the keypath color. So let's go ahead and save. And let's go back to Xcode and run.

Let's move this. So there you go. Just with two connections and interface builder, we're able to bind that slider to automatically track the dot and the two dots also track each other. And same with color. I can bring up the color panel and I can change the color. And again, this is now bound to the dot's color value and so on. And of course, bindings, this is not the only thing bindings gives you. Let me show you the little other magic of bindings. I can make my slider smaller.

I can go to the text here. I can drag out the text field, as you can see. And again, I can go to the value binding of the text view here. So if I go to, here's the attributes, go back to bindings. Here's the value binding. I'm going to look in there and set the value of the text field to be the same as the radius. Save. Go back to Xcode. Run.

As I move the slider, not only is the dot's radius changing, but now because this text field has been bound to the radius, the text field is also showing you the exact same value as the slider does. So as you can see, without any glue code, without writing any code to bind those things together, you know, and make sure they're in sync, we are keeping things in sync. The good thing here also is that in the case situation here, the dots, the model values from the dot, the fact that it's blue and the size is 50 and so on, are automatically also reflected in these elements at launch time.

Okay, so just to, if we can go back to the slides, just to show you a picture of what we did there with the text field, we added a text field and we bound the value to the radius of the dot. Again, you can establish multiple bindings to the same property and they will be kept in sync.

There's a lot of magic and a lot of great stuff in bindings and especially when you start using controllers, which AppKit has support for, you can actually use tables, outline views, browsers and so on in your applications with writing hardly any code or no code at all. And you can go to the bindings talk, which I think is tomorrow, to see more about bindings.

Okay, so what we saw today is an overview of Cocoa, Objective-C, Xcode/IB, and some fundamental Cocoa topics: drawing, event handling, target/action, object ownership, KVC/KVOMVC, and of course, bindings. And we also saw, hopefully, that simple things were simple, meaning very little code is needed, and even complex things are possible where we had to resort to maybe even using observers for our custom views. So hopefully, you know, that idea got through.

You can go to the Tips and Tricks talk, Cocoa Tips and Tricks talk, tomorrow to see more of this simple things simple and complex things possible theme with further demos and further advanced things. Now some things we didn't see. There's a lot of stuff in Cocoa and, as I said, a few good solid design patterns. And the few we didn't see include delegation, which involves objects helping each other. That's a powerful design paradigm that we didn't see, but you probably need to be aware of.

Notifications, which is where objects are telling other objects things. Categories, where you can add stuff to objects, add functionality to objects. Data sources, where again, objects are helping other objects. And immutability, where objects refuse to be changed. These are some other design patterns that you probably will become familiar with, you'll run across in learning Cocoa.

Other things we don't see include some features of Cocoa, and here is a not certainly comprehensive list, but document architecture, undo, localization, etc. A lot of great features. There are also other frameworks which are not in the Cocoa umbrella and are not core Cocoa frameworks, but provide Objective-C APIs that can be used easily from Cocoa. And one of those is, of course, the core data framework that you saw yesterday.

The resources for you to learn more, there's the Apple Developer Connection, which has the Cocoa page. And in there, there's Getting Started with Cocoa, which is fairly useful as it has links to many resources that you want to follow. The O'Reilly books for Cocoa, there are some pretty good books for Cocoa and Objective-C. And there are other books, including Aaron Hillegas' programming Cocoa book and a few other third-party books. They're all pretty good. Apple and Omni both have developer mailing lists.

And, of course, the sessions here at WWDC. There's a bunch of just core Cocoa talks and many other talks related to Cocoa and Objective-C API. So I hope you get a chance to see them and enjoy them. And that's it for my part. Thank you very much. Thank you.