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

WWDC01 • Session 122

Using Cocoa

Mac OS • 59:10

Learn how to use the APIs in the Cocoa frameworks to swiftly enhance your application. A series of demonstrations teach both newcomers and experienced Cocoa developers how to take advantage of advanced Cocoa features.

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.

Well, good afternoon. So this is the Using Cocoa Talk. My name is Ali Ozer. I'm the manager of the Cocoa Frameworks Group. So the title of the talk is a bit vague. What are we going to do today? Let me just cover that quickly. We are going to cover some Cocoa programming topics, which is good. We are also going to be discussing some APIs.

And we're actually going to write some code using those APIs. We're actually also going to try to compile and run those apps on stage, so keep your fingers crossed. And probably the overall goal is to demonstrate that Cocoa is powerful and easy. You heard this on Monday. You saw some demos. And we want to show you with more stuff that that's indeed the case.

Now note that this doesn't mean-- that if you're a newcomer to Cocoa that within two weeks you've written the world's greatest app and you're shipping it. There's still a significant learning curve. You still have to learn all of the functionality available, all of the classes, and so on.

But again, Cocoa has got a consistent set of APIs, some nice small number of conventions and paradigms it uses. And as you become familiar with those and as you see those, you're going to become an expert in hopefully a short period of time and overcome that learning curve. And today we're going to cover some of the representative topics that you might encounter in your Cocoa programming.

So before I dive into APIs and some code, I want to give you a quick overview of Objective-C. Very quick, just so that those of you who are not familiar with Objective-C aren't lost in the code examples we're going to be showing you. I should also note that a lot of the examples I show you do have one-to-one correspondence in Java, and it's actually pretty straightforward to use them from Java if you're programming in Java.

So Objective-C is a small superset of C, and it's ANSI C. So everything you love about ANSI C and everything you hate about ANSI C, there's probably some, is present in Objective-C. There is some additional syntax, and there's a few additional types in Objective-C, and we'll cover them very soon. Objective-C also has a dynamic object runtime.

I mean two main things by that. One, it's that messages are bound to objects. That is, when you call functions or when you call methods in Objective-C, the binding doesn't happen until runtime, which gives you a lot of flexibility. Second, it's a very complex environment. It's a very complex environment. It's a very complex facility. Secondly, objects are able to ask questions about other objects, about what messages they respond to.

Basically, Objective-C has these facilities that allows you to ask at runtime, "What messages do I respond to? What messages do you respond to? Who am I? Why am I here?" Et cetera, those kinds of questions. That dynamism actually adds a lot of power. It's responsible for a lot of the power and flexibility of Cocoa, as you'll see. Just very quickly, cover some of the syntax in Objective-C.

Here's how you define a method. This is probably the weirdest syntax. This is just like a C or Java function, except that the header line looks a bit different to you. You'll see that it's set width:height:. Again, it's a void return and two floating point arguments, except the name of the method is broken up into two pieces because you can use keywords in front of the various parameters to identify them more easily. The rest of the body is sort of like C or Objective-C, a mixture of those two. Now, to use this in a method, here's what the line looks like. You use brackets to indicate that you're making a message send or a method call.

MyRect is the receiving object. Then you say set width:height: with the parameters appearing in the appropriate places. Now note that when you--this keywords that appear in front of parameters really allows you to make the code a lot more readable, especially when you have multiple arguments which all are identified by the keywords. Now, one other interesting thing in Objective-C, is this ability to refer to these methods.

As I said, Objective-C is fairly dynamic and one reason for that is this selector type. The SCL or the cell type allows you to reference methods and then use them. So for instance, to reference this method set width:height, you can say at selector and then put the method name in there. And then this variable, myCell, can be used in a variety of contexts. For instance, you can apply it to an object. It's sort of like a function pointer but a lot more dynamic.

Because depending on the object you're sending to, it'll actually execute a different body of code, depending on how that object is implemented to this method. We'll see a use of this later on. To refer to object, here's how you declare it. Rectangle* says that this object, MyRect, is instance of a rectangle or subclass. Again, this is the C syntax. It's a pointer to rectangle.

ID is the generic term for an object, which means that some other object is actually an object of any type. So these are how you would refer to objects. Referring to this object, meaning the object in the context of a message send, the receiving object, is self. This is very much like this in Java or this in C++. And finally, the object is self.

This feature of constant strings, strictly speaking, this is not a feature of Objective-C, but something we've added fairly early on in the compiler. Using this @ quote business, you can create constant strings, which are instances of the NSString class. The NSString class is an object that's used very heavily in Cocoa API. This allows you to get instances of those very efficiently because they're at compile time.

Okay, so much for Objective-C. Hopefully now you're armed with just enough to understand some of the stuff we're talking about. So, how is today's talk format? We thought you would ask questions and we would answer them. And just to make sure it's all clean and not so messy, we thought of the questions you want to ask. Okay, so let's just start, dive in. Question number one.

I wrote a small app for my school project, but for full credit, I need to support Undo. The app is so simple, I don't know if I can. Help me. Okay, there's a great question. Now, typically, when you think of Undo, you think of a large app, and if you were here for the Cocoa overview session, you heard Chuck say that Undo is easy to add to applications that are designed using model view controller paradigm. And which implies, you know, well-designed, large application, not necessarily a small application that someone in kindergarten wrote. So, let's see how we can do this.

So, now, the reason you can add Undo to any kind of application is model view controller is actually fairly easy to achieve, at least at a superficial level, and that's sometimes enough. In addition, the Undo is really not all that hard, so you can actually try to squeeze into any kind of application. The main object you use to do Undo is the NSUndoManager.

So, let me just show you a slide with the APIs, and then I'll jump right into a demo. NSUndoManager is the main class you use. There's usually one per window. There's usually one per window or one per document. Typically, you would have one per Undo context you want to support.

So, if you want to have Undo that applies to the whole application, meaning the Undo stack applies to the whole app, you would have one instance of this instead of one per window. The way you use this is, whenever the user does an undoable action, you know, they type some text, they do something, You register a callback, and the callback is the callback you want invoked to undo that action. The way you register this callback is to call this method. So note that this is, again, those of you uneasy with objectivity, this is a method with three arguments. This method you provide, first of all, the callback target, who's going to be called.

And a selector, basically the method that will be called. There is the use of that SEL type. And finally, the single argument into which you can shove anything you want. So this is basically what you do. Okay, so this is pretty much all the API I need to show you at this point, so let's go and do a little demo.

So for demo, I'm going to be using the dot view application. Those of you who were at last year's Cocoa overview probably know that the dot view application was born there on stage. Then it made its way into the examples folder on your 10 machine. It also makes an appearance in the learning Cocoa book by O'Reilly. So this program has gotten around. And here it is again, once again, for us to fiddle with. OK, so the program is very simple. It's got one subclass of view. Let me just run it.

So there it is. You probably have seen this in one of those contexts. You can make the dot larger. You can click anywhere you want. You can also click on this color well object, which brings up the standard color panel. And you can change the color. So if you go look at the Edit menu, which is standard in Cocoa apps, you notice that nothing is enabled. You can't cut, copy, paste. You also cannot do undo or redo. So our goal is to try to enable those. So let me quit this right now.

This app only has one object. It's the dot view object. It's a subclass of NSView. As you know, NSView is the main object used for drawing. I'm not going to go through all of this implementation. There's actually only about 25 lines of stuff here. You init it. You dialog it. This is the way views draw themselves. The interesting methods are mouse up. This is what moves the dot. Set radius, which is what changes the radius. Set color, which is what changes the color.

First, let's concentrate on making the color undoable. Now let's look at this method here. It takes an ID sender. This is a typical target action method. If you remember from Monday or from some of the other talks, target action is how Cocoa UI objects communicate with the back end objects. In this case, whenever I fiddle with the color well, I mean the color panel, the color well ends up sending me a message. The argument here is the color well.

Here, I get rid of my old color. Dot color is the current color. I hang on to the new color by asking the sender what its color is and hanging on to it. I tell myself I need to display. That's all nice and good, but this is not really an MVC. This is not a very good MVC method. It mixes the model, the view, and controller all into one. Let's see if we can make it a little better.

Now I'm tired of typing, so for the rest of this session I'm going to use this magic typing assistant and I might explain to you how it works later on. So now you'll see how it works. I'm just going to go like this and it's going to type for me. Okay? Okay, so let's get started. First I'm going to add this method. This assistant is really very good. Okay.

Okay, so I've just added this method, which is very much like this method, except instead of taking a sender, it takes an sColor. And instead of obviously asking the sender for the color, it just retains that color. So this is more of a model method. You know, it sets the instance variable directly. That means somebody else can call it to set the color.

That's nice. Let's go ahead and rewrite setColor to work in terms of setColor value, and that's fairly obvious as well. Hopefully it's fairly obvious that this old method is equivalent to this and this. So this method just calls setColor value with the color. Right? You're convinced it's about the same thing. So let's get rid of this.

So the beauty of doing this is now we have a method here which we can use for our undo purposes. So the first thing I'm going to do here is create an undo manager. Now it turns out that every window by default has an undo manager. So if you don't do anything, undos work within the context of a window. And why don't we take advantage of that right now? So I'm going to ask myWindow.

For its Undo Manager. By default, it's not created. It will be created lazily and returned to you, and that's what we're taking advantage of here. And now we're going to make use of that code I showed earlier. Just to make sure you understand this, I'm telling the undo manager to register undo with target, self, self being this object.

Selector is the set color value selector, and object is the old color. What happens is whenever I change the color, I tell the undo manager to call myself with the old color if it needs to undo this. Okay, so this is pretty much all that's needed and let me show you that that's indeed the case. I'm going to save, I'm going to build.

and run. Let's hide others. So the Edit menu is disabled. I can click around. It's still disabled. But I can bring up a color well. I can change the color. Let me actually make this bigger. Change the color. And Undo is enabled. And I can undo. There you go. Now notice that one more magical thing. Redo is also enabled.

So what's happening here is any time an undo is being done, any time we do something, the undo methods are recorded. Now when the user does an undo, that same method is called. When that method is being executed, it also registers undoes. So any undoes that are registered as a part of processing an undo are recorded as redo actions for that undo.

So it turns out that one line actually is enough to do both the undo and redo. Another magical thing, if I actually change this around so that I'm changing the color all the way, when I undo, it doesn't go back to the-- incremental color. It just goes back to the last color when I started clicking.

That's because undo manager is smart enough to know which event mode we're looking at and ignore all the events-- not actually ignore them, but call us all the events that happen when we're actually in tracking mode. This helps you get rid of methods that happen like this or when a slider is moving and so on. That's the right thing to do. And again, you can override this behavior, but typically it is the right thing to do.

Okay, so let me quit this. And next thing I want to show you is let's also make the dot moving undoable. Okay? So here's mouse up. Here we look at the event location. We convert it to the current coordinates, convert it to store in the variable called center, and then we set needs display. Well, now we know how to fix this. We want to go ahead and add a method that does a set center for us.

Okay? Okay, so hopefully this is a pretty obvious method to set the center. Pretty straightforward. That also means I can now get rid of these two lines.

[Transcript missing]

OK. Suddenly, though, you notice one thing. That mechanism I showed you takes as an argument an object. However, the setCenter method takes as NSPoint.

And NSPoints are not objects. They're structs. They're passed by structs. So we can't use that method we showed you, because it is really geared towards passing objects around. So what's the solution to this? Well, it turns out there are two solutions. One is you can go ahead and wrap the point, or wrap any structure, any C type, any data structure you want, in this magical thing called NSValue. And then it becomes an object.

However, note that this is really not very pleasant, because suddenly you have a setCenter method, which takes this NSValue as opposed to taking a point. And that's not very natural. You really want to take a point, because that's the argument. So we're going to not use this method.

And we're going to use some other method. Let me just show you the slides for it. Can we go back to the slides? So for sophisticated callbacks in Undo, you would use this method. Prepare with invocation target. Suddenly you're all going, what's that mean? This name doesn't make much sense. The word register doesn't appear there. So it turns out this is quite a sophisticated way of doing this, and the name will make a little sense maybe when I explain it.

But basically, this call makes the Undo manager go into a state where then you can make a call into it, and it freeze-dries that call you made. And then you can later on add water and execute that call. This is a great way to basically freeze-dry any call, no matter how complicated. It can have five arguments. It can have an argument with a huge data structure. It remembers it all.

And I'm going to show you exactly how this works so it makes more sense. The reason the word invocation appears here is because we use a thing called NSInvocation, a class called NSInvocation, to implement this. And of course, most of this owes a great deal to Objective-C's dynamic nature, being able to do all this packaging. So let me show you how this works.

Here's the call you make. First you call, say, undoManager, prepare with invocation target self. Again, we want this object to be called when this happens. And then we actually make the call. Set center, center. That's it. You make the call. And it just remembers the call instead of executing it. It wraps it up and puts it away. So I'm going to save. I'm going to build and run.

[Transcript missing]

Okay, well enough for undo, let's go to the next question. How do I add a toolbar to my application? I don't see the interface builder. Ah, that's a good question. Toolbar, as you know, is a new object. We added it very recently in 10.0. Most people when they're doing Cocoa programming are used to just going to Interface Builder and dragging their objects off.

Well, Toolbar doesn't make an appearance in Interface Builder yet because it's brand new and we haven't added facilities for Toolbar to properly be archived in Interface Builder. And so it's not there. In addition, Toolbar is new so it's not very well documented either, so you might be getting confused about how to do it. So let's just show you a simple example of how to do this.

Now, the toolbar, as I said, is new in 10.0, and there are two classes that implement the toolbar: the toolbar item class and the toolbar class. ToolbarItem is the class that represents the individual items on the toolbar. So the various buttons you see, each one is a toolbar item.

The attributes you can assign to these toolbar items include stuff like what the image is, what the label is, what the help tag is, what the menu is. So when you click on the menu drops, you can assign that menu. In addition, of course, very importantly, you assign a target and an action. As you know, target actions is how UI elements do their thing. So you assign it to the toolbar item and that does its thing.

Note that toolbar items can also be custom. They have a custom view, which means if you have some sophisticated view you want to put up there, not just a button-like thing, you would use that mechanism. For instance, the mail search field, which has a pop-up, a search field, some text over it, is implemented using this. You can do all sorts of crazy things with this. You can put a clock up there, a CPU meter, stock ticker. You can sell ad space on your toolbar. So it's all very useful. Thank you.

Now, toolbar items, each one has a unique identifier, which is an NSString. This unique identifier is used to reference the items. So typically the toolbar talks in terms of identifiers, not toolbars. And when I say unique here, I don't mean a globally unique credit card number-like thing. It's just a string that's unique within the context of a toolbar.

And we also provide some built-in identifiers from Cocoa so you can use the standard items in your application. The toolbar is the class that represents the whole toolbar. Again, it has attributes, and these include stuff like whether it's visible, whether it's customizable, whether it's display mode is, and so on. And toolbar also has an identifier, and this identifier is used to, one, save the state of the toolbar, because remember, you can customize those toolbars. And when you customize a toolbar in an app, and you quit the app, and you restart it, you want the same state.

In addition, this identifier is used to keep the toolbar synchronized with other toolbars. So if in your app you have seven document windows and you change the toolbar on one of them, you want all the other toolbars to change. However, if you have two kinds of documents, for instance in mail there's a compose window and there's a mailbox window, you would assign two different identifiers so that when you change the compose window's toolbar, the mailbox one remains distinct.

So that's what the identifier is used for. Now, interesting thing about the toolbar is is quite a lazy little class. It relies on its delegate to do most of the work. This is a paradigm we use in other places in Cocoa. For instance, a table view would ask its data source for the data and not really store the data itself.

This allows it to be lazy. It doesn't store the data. It can create it later. That's a pretty good paradigm, and that's what Toolbar does. Given that's the case, let's just look at three of the delegate methods. These are the important delegate methods because these need to be implemented.

So the first one is how a toolbar finds out what items can be placed on the toolbar. Now note that this is sent to the delegate, so the delegate gets a reference to the toolbar. So if the same delegate is being used for multiple toolbars, it can make different decisions.

That's always the case with delegate methods. And this returns an array of identifiers, indicating here are all the items that can appear in this toolbar. The next method is similar, except it returns the initial set of items, meaning when the user first time brings up that toolbar, what do they see? And finally, there's a method to create the toolbar item. Because remember, all of these methods are working in terms of identifiers.

Eventually, somebody has to create a toolbar item from the item identifier. And that's when you call this method, passing in the toolbar, the identifier, and a flag indicating whether it's being added to the toolbar or it's being created for some other reason. And that would determine whether the thing should be live or not, for instance. So again, these are sent by the toolbar to its delegate.

Again, demo. For this, I'm going to use the TextEdit application. Most of you probably know about the TextEdit application. It is the app that ships in the applications folder. The source is also available on your developer CD in the app examples directory, so you can take a look at it yourself.

So TextEdit is not a document. It's not an NS document-based application. It's a custom document-based application. It was done before NS documents, so it implements its own document functionality. And it turns out the most convenient place to create a toolbar is right after we create the window for a document, because a toolbar goes hand-in-hand with the window, so you basically want to create that toolbar when you create the window.

And where that happens is in the init method. I'm not going to explain all this code here. There's a lot of code. But here's the init method, which initializes the document. That also goes in and creates the window and all that. Now, this is not a super great example of MVC, so don't look at TextEdit for MVC hints. But anyway, it works. So at the bottom of init, we're just going to go create our toolbar here. If you look up here a little bit, we create the window and so on, so ignore all that mess. But here, let's create our toolbar.

I'm going to open a brace. One thing people get confused about in Objective-C is they think they can declare variables anywhere, like in C++. That's not the case yet because the C standard did not allow it, but it's been added to C99. We might actually see this in Objective-C someday. For now, to create new variables in the middle of your stream, in the middle of your code, you have to open a new brace. Again, I could have done this at the top, but that would be messy. We have a toolbar, Instance.

We create the instance. Note that this is the class, alloc init is what you normally use to create instances. Here I'm creating a toolbar myself by hand because I can't do it in IB. This is work that I wouldn't have to do sometime in the future, hopefully. I give an identifier. Again, it can be anything. Texted toolbar sounds good. Now let's go set a few attributes on this. We make the delegate to be self.

We tell it that it allows user customization, meaning the user can change it. We say that it can save it. We say that it can change its configuration. We also tell our window that the toolbar I just created is the window's toolbar. Set toolbar. NSWindow has a method called set toolbar.

Finally, because I gave this toolbar off to the window, I no longer need it, so I release it. This is the memory management stuff in Cocoa where you create it, you hand it off to somebody, you don't need it anymore, you get rid of it. That's it. That creates the toolbar. Step two here is to implement those delegate methods. Let's find some empty space here and let's do our delegate methods.

The first one was allowed item identifiers. Here we're supposed to return an array containing the items, the item identifiers. Let's go whole hog and do this. You're going, "Whoa, what's that?" These are all the items that Cocoa gives you out of the box. The color dialog, the font blog, the separate item, et cetera, and the customized item. We create an array and we return that.

Next, we implement the second delegate method, default item identifiers. Let me finish implementing this. Here we return an array of just one item. This is the default toolbar. It doesn't matter what it has. The user will customize it, hopefully. Let's just put the customize item in there. Finally we have to implement our third delegate method. Remember that was to create the items. In this case, because we were using all the built-in items, that method actually doesn't have to do any work. It turns out it has to be implemented. We just go ahead and implement it.

Note, it's got three arguments. We implement it and we just return nil. We don't do anything in this case because this method will never be called. It just happens to be implemented. It turns out most of the time when you implement a toolbar, this method will be implemented and I'll show you that in a few seconds. That's it. We implemented our three delegate methods and we wrote a few lines of code to create the toolbar itself. Let's save and let's run this thing.

Let me hide others. Well, and there you go. There's a toolbar and it has a customized item. And it does all the things toolbar does. Appear, disappear, that's good. Note that the content area down here doesn't know anything about the toolbar because that's part of the frame. Now I can do the magic stuff, bring it down. Now I'm customizing it. I can put the colors panel, I can put fonts panel. Let's put this customized item away. Put a flexible space. And there we are. So that's a toolbar that I can live with. If I create a new document. Thank you.

Again, creating a new document also gives me the same toolbar because I told it that it has an identifier and so on. So, again, this is interesting, but you really want to create your own toolbar items. I mean, you're not going to be content with just those items. So let's just do that one extra step and show how we would do this.

To allow that, in the allowed item identifiers method, we want to return a new identifier. Again, creating this identifier is, you just pick it out of thin air. You don't have to do anything magical. Let's call that find dialog because let's say we want to add a button that brings up the find dialog. It turns out that's one of the ones that's not standard in Cocoa.

So we're changing our allowed item identifiers. The default item identifiers we don't need to touch. We can add it there, but we don't need to. And finally, we have to change this code here, the toolbar, the creation method, because now we are creating a custom item. This method will be called with that find dialog here. So let's see how we do that. First, get rid of this return nil. A series of demonstrations teach both newcomers and experienced Cocoa developers how to take advantage of advanced Cocoa features.

Ali Ozer Now we're going to set some attributes. If it is equal to the find dialog, because as I add more identifiers, I will get called with other ones too, so I'll just make some conditional code. So if it's equal to the find dialog, I will go ahead and set the label.

Label is the string that appears under it. I want the label to be set to find. Typically you would say set label find, but that would be a non-localizable program, and that wouldn't be very good the moment you want to sell in Japan or other countries. So why not make it localizable by calling this function, which allows this string to be localizable.

You can also set the palette label. That is the string that appears in the customization window. It can be different than the string that appears in the toolbar. I'm also going to set the target. Turns out to bring up a find panel. You contact this object called text finder. This is in TextEdit.

And we set the action. Turns out the action to bring up the find panel is this thing, order front find panel. So again, we're setting action and target. Typically, you know, when you make connections in Interface Builder, you're implicitly doing this. Here, you're seeing how to do it by hand. Again, because we're diving a little deeper.

And now I'm going to set the image. This is the image that appears on the toolbar. Now, to set the image, I ask NSImage for an image. And then I just set that to be my image. Now, here I put a question mark because I don't have an image yet. Let me go ahead and find an image.

If I go into finder, go into my documents directory, I'll choose one of these images, this one. Okay. And turns out this is a picture of my cat. Turns out the reason for that is because my cat's always finding things under the couch, behind the refrigerator, inappropriate things. But he's a good finder, so let's just use that. the cat.

So to use that image, and this is pretty typical whenever you want to use an image in your application and you want to make it part of your application's resources, you drag the image into Project Builder under Resources. Project Builder asks me what to do with it. One of the important things you might want to do is copy it into your sources and add it to my target. So now that the cat is part of my resources along with all these other resources down here, I can actually use this call NSImageImageNamed.

Put the word cat here. So that method image named will find any image out of your application's resource package. If it's correctly localized, it'll find a localized version. So instead of having an Egyptian cat, you want to have a Norwegian cat. That's OK. So you can do that. We didn't localize it here, but that's fine. You get the idea. OK, and then we finish this because we're done, and we return the item.

It turns out that's all we need to do. Now, you might be asking, what's this auto-release business? Again, it's got to do with Cocoa's memory management. Because we created the item, and because we're returning an item that the client's going to take ownership of, we auto-release it, which means release it later. It's sort of like smart garbage collection.

And you will see this a lot, and it's a standard part of the programming. I'm not going to go into it anymore. So that's it. And note that if you had other items, you'd basically write code like this, or someday maybe use interface builder and not have to do this at all. So I'm going to save. I'm going to run.

Okay, hide others. Okay, there, note that when we ran it, it remembered the old toolbar, so that seems to be working. Bring down customize. There is the find dialog. There is the cat. Let's say done. And let's see if the cat can find things. There's the find panel, so there you go.

Okay, so, you know, we could play with the toolbar all day long, but that's okay. Let's go back to our next question. Question. How can I get a better frame rate out of my animating game? Oh, it's a performance question. That's great. And you've been hearing about performance all along, so let's see how, whether we can do this. Oh, demo again. Okay.

Okay, let's see about this animating game now. Let's see what it does, what it looks like. Conveniently, it's on our dock. Ah, it's a worm game. OK, so this game's been around for a while, I think since the '60s. And it even makes appearance on your cell phones. You probably have seen this game. Let's run it just to show you what it's about.

[Transcript missing]

Oh, yeah. And when he eats it, he grows by one dot. So as he gets longer and longer, it becomes harder to manage it. And eventually, it hits itself and dies. And obviously, the more you eat, the more you get. And typically, the worm, when it hits the edges, would die.

But we simplified the game here for demo purposes, because I don't want to spend too much time playing the game. So this slider here allows us to change the frame rate. And I'm setting the frame rate to a really high number. And you can see I'm getting 38 frames a second. Now, that seems good enough for this game. Well, I mean, this person's quite worried about performance.

But it's actually good to be worried about performance. Even if your frame rate is good, trying to speed it up means that there's more processor power in the machine for other things. And you can be doing other things in the back. So let's see whether we can help this person out. Now, one thing you'll notice here is that this whole area seems to be white. And we don't see any of the window background.

Typically, when you subclass a view, you either draw everything in your bounds or you draw less than everything. Now, if you draw everything, one thing you can do is tell the view subsystem that you really don't want anything behind you to be drawn whenever you're being drawn. And the way to do that is to implement one method in view.

Now, whenever you're doing a drawing, a subclass of view, a drawing application, this is the first thing you should look for. And that's to implement this method called isOpake to tell the system that you're opaque. That's the first thing we're going to do. Let's see if that will help us. Now, we go to our subclass of view, in this case, worm view.

And it's some fairly involved little view class. There's also a controller class to control the UI and the worm guts, which controls the logic of the game. Let me close this up. So this is, again, a standard view, you know, init with frame. There's a dialog. There's a method to set the string and get the string. Oh, yeah. If you notice, the worm's body was made up of a string. That seems odd, but, you know, there's a reason for that later in the demo.

There's a method that performs each frame of the animation. And finally, there is the drawRect method. drawRect, again, is the method you override to do drawing. Now, if we bring up our Find panel and search for isOpaque, we see that it's not implemented. So the first thing we should do is go implement that. It's implemented just like this isFlipped method. It's so easy, I don't need my typing assistant.

is opaque. So we implement it and we return yes, because the default implementation returns no. And this basically says-- that when this view is being drawn, don't worry about drawing anything that's behind it, because this view will take care of every pixel within its bounds. So let's run this little puppy.

Oh, look at that. 44 frames a second. So just that one little line got us up from 37 to 43, 42. That's pretty good. But I have a feeling this is not going to be enough. Let's hide the project builder. So next thing you might want to do is run Quartz debug, which you saw earlier in the week, I hope. Quartz Debug shows you which areas are being drawn. Let's kick it on.

OK, so again, obvious. This might have been a rigged demo, but it's obvious that the worm is drawing the whole view every time. The yellow area shows the area that's being updated. While you really want the worm to only draw The area that's being updated. So that's the next optimization we can do. So let's quit the worm. Let's hide Quartz debug and let's go back to our worm application.

Now there are two pieces to drawing less than your whole view. One is to make sure that when you tell the view it needs to be updated, you tell it what region or what rectangle needs to be updated. And the second thing is in your drawing method, pay attention to that rectangle. So we'll do both of these right now.

So the first one happens here. In the perform animation method, we say self set needs display, yes. That just says, I'm dirty, redraw me. And it gets drawn later on whenever appropriate. So instead of doing that, we have to compute a smaller, a tighter rect. And let's see how we might do that. Now some of the stuff here has to do with the worm's logic, but basically what we want Let me push these lines down a bit. I want to start with Erect. And I'm going to remember the old target position.

and I wanna for the length of the worm, I want to look at every position every body position of the worm I want to figure out what the rectangle is, I want to union into my rectangle so as a result of this operation I end up getting a rectangle which tightly covers the worm okay so again you know that little method there returns the worms body and so on so this allows us to create the tight body around the worm Then we update the game state, meaning we move the worm.

Now it turns out we know that the worm only moves one block per frame, so after the game has updated the frame, we can go ahead and union the new head position. Again, the worm's head moves, so we also want to bring that rectangle into the whole rectangle so our rectangle grows a bit. There's one more thing we need to do to compute the tight rectangle.

and that is to see if the target changed position, meaning if the worm ate the target, it moved. So let's also union that in. If the old position is not equal to the new position, we go ahead and union in the target position. So I hope this makes sense. Now we have a rect that includes the target and the worm.

It only includes the target if the target moved, which is rare enough, so that's OK. And the final thing we have to do is instead of set needs display, we call set needs display in rect and pass that rect. So now that tells the view machinery just draw the smaller rect. So let's save.

The next thing we want to do is modify our draw rect. Note that draw rect is taking a rect argument, but nowhere in here it's really being paid attention to. Again, sloppy programming. First thing we want to do is this is the background drawing, and we do a rect fill of the whole bounds. Self bounds means my whole bounds.

Here, self is the view. So instead of self bounds, we're going to do a rect fill of the whole bounds. So instead We really want to pass in rect.

[Transcript missing]

All the time, unconditionally, let's do it if the rect intersects the rectangle that was passed in. Okay, does that make sense? Again, we only draw it if the worm is in that region.

Again, pretty straightforward. This will intersect the two rects and return to you yes or no. Finally, the same thing with the target. If the target rect happens to be in that rect we passed in, we'll just go ahead. will only do it if the target is also in that box. OK? So I'm going to save. I'm going to run.

OK, here's the moment of truth. OK, so there's 85. I think that's fast enough. Do you think so? Agree? OK, good. So there you go. We've now made the game quite unplayable, but it's also very fast, so I think this person should be pretty satisfied with this performance. OK, so let's go back to the slides, please.

Okay, so what we learned from here is that you want to draw as little as possible. One thing that means is you should fine tune the drawing area, the update area, and you should also pay attention to that. You should also use isOpaque to prevent super views from drawing needlessly. Those are probably two most obvious things you can do to speed up drawing. And also use Quartz Drawing to see what's drawing.

Sometimes you might find something very surprising like some text field is being updated every so often and you don't even know about it. Or as Robert showed on Monday, even when paused, his application still kept on drawing. There are things like that that Quartz debug will tell you. Next question. But still not good enough for my boss.

Can I make it draw even faster? Okay. So that's interesting. We'll see if we can make it draw a little faster. I think the thing to do at this point is to see whether we can see what the problem is. So let's go back to demo again. And we'll now use another performance tool quickly. Let's run it.

Let's launch Sampler. Again, I think you heard enough about Sampler that I'm not going to go into details. With Sampler you can attach to a running program. I'm going to do that. I'm going to choose Worm and I'm going to say OK. You've seen samplers. I'm not talking too much about it, but what it's going to do is it's going to sample the target app every so often, every 20 milliseconds, and capture the stack.

That's going to give us an idea of where the application is spending its time. Let's put this down here. Let's say start. There you go. Let's say start sampling. Then you sort of pace back and forth a bit, get some coffee. It's sampling the worm. You can say stop. Let's stop the worm here.

Okay, so this is now the biggest stack. Now you can actually go down the tree here and look at all the things that are happening. But this turns out to be the biggest stack. Let's go back. And if you look down here, It's main, it's application run. These are the standard Objective-C things, standard Cocoa things, which are part of the frameworks.

And here the worm timer callback, this is your code. And here we're displaying the view. And it turns out the biggest thing we're doing in displaying, and you would expect the displaying to be taking the longest time. The question is, what is the displaying so slow? Turns out the biggest thing here is NSString, string drawing, draw and rect. So the most time, in fact, of all the drawing that's happening, 90 something percent of the time is spent in drawing that string. Okay? So maybe that's something we can look at. Okay, so let's go take a look at that.

To draw short little strings here and there, you would use this API in NSString. Draw and rect with attributes. It's the rectangle, and the attributes include stuff like the font, the color, everything else a style text would need to know. This method is very convenient, and you should use it if you need to draw strings here and there. But it does do a lot of work under covers. One of the things you need to do when you're drawing true Unicode text is that you need You need to take the Unicode string.

You need to convert it into what's called glyphs, and then you need to lay those glyphs out and you need to send them to the screen. So it's not just a simple matter of taking the Unicode characters out of the string and blasting to the screen. The reason for this is Unicode text is fairly complicated, and you basically need to convert those Unicodes into elements in the font, which are called glyphs. And sometimes, like a three-character Unicode string might correspond to five glyphs, or it might correspond to one glyph, because there might be a special glyph. Some languages are actually even more complicated. So that's why the text layout machinery actually does a lot of work.

In addition, there's a lot of work that needs to set up just to start that operation and end it. So the string drawing is convenient, but because it's called with no context, it does a lot of work repeatedly. So here, we can try using the text system for some performance gain. And to do that, let's look at the text system architecture rather quickly. you might have seen this before.

The COCO-TEX system-- is a network of objects. Typically when you're using it in an interface builder, you're taking an instance of NSTextView, dropping it into your application, and it creates everything else for you, and then you're fiddling with that. But these other objects are also available for you to use directly. TextStorage is the model object. It stores the characters and their attributes. LayoutManager computes and stores the glyphs and their locations. So the LayoutManager is sort of like the controller in this picture.

Text container provides text area information. So in fact, in this picture, you can imagine a layout manager with multiple text containers if you are doing a multi-column text editing application, for instance. In the text view, the final piece, the piece you see in interface builder, the visible piece, is the piece that handles the display and editing.

Now note that, again, this allows us to do fairly sophisticated things. For instance, you can have one text storage, you can have three layout managers. What this means is you can have a same text document, but it has three different views on it. So one could be a wide view, one could be a short view, one could be a view without fonts, and so on. Similarly, multiple text containers, as I said, allow you to have multiple pages, multiple Turns out that here, the interesting piece I want to use is the Layout Manager.

It gets the text contents from text storage, as I said. It converts these characters into glyphs, and it computes the glyph locations. And what we really want in this case is we want to use the glyphs and the glyph locations, and we don't want this relay out to happen over and over. And then finally, you would get a layout manager to draw these glyphs by calling this method "draw glyphs for glyph range." And that's usually called by the text view layer.

So let's see how we can make use of Layout Manager in our application. First thing we want to do-- let's get rid of this-- is we want to go into our vrm_view.h, which is the header file which declares the object. And we want to create three-- we want to declare three new instance variables.

Text Storage, Layout Manager, and Text Container. So those are the three objects we talked about. So instead of using TextView, we don't need TextView. We're just going to use those three objects at the bottom. So we create them. Let's save our header file, and let's switch back. Now in the init method-- where we create various stuff. We now want to go ahead and create those three objects. And it turns out it's fairly easy to create them.

Text Storage Outlock In It, Layout Manager Outlock In It, Text Container Outlock In It. We don't want to provide any sophisticated attributes. You can do that. You can provide sizes. You can provide various other properties. We don't need to deal with this right now. And then we want to add to the Layout Manager the Text Container. We want to add to the Storage the Layout Manager.

Again, the API says add and not set because you can have multiple Layout Managers. You can have multiple containers. So this is all we need to create a subset of the text system. Again, note that when you create a text view programmatically or through Interface Builder, all this is done for you magically because that is really the most common use of the text system.

OK, now, because we created those three objects, we should remember to release them. So whenever our object goes away, we release the three things we created. That's fairly straightforward. Now comes the tricky part. Set string is where a new string is provided for the worm. Note that this happens very rarely, because the string is not really changing.

So what we want to do is whenever we get a new string, in addition to remembering that string, we actually want to pour it into this text system, that mini text object we created. And to do that, I would tell the worm storage object that its string is the worm string that I have.

In addition, I would tell the worm storage what the attributes are. Now, this worm text attributes comes from up here, and again, I said it's the style information. In this case, it just stores the fact that the font is Lucida Grande 16. I'm not going to go into that.

So, anytime a new string comes in, we reinitialize our text system with the new string and the new attributes. That's it. That's all we need to do there. Okay, so that takes care of that piece. And finally, in the drawing, instead of doing this string draw on rec, which we know is not fast enough, we want to use Layout Manager to draw. Okay, so instead of doing this character stuff, let's open up some space here. Let's push this to the top.

I'm going to work in terms of glyphs. I'm going to ask the glyph location. And I'm going to do draw on glyphs. Now ignore this code here. I could explain it, but it's really pretty straightforward. What's happening here is instead of drawing the character, instead of drawing each character one by one, as we would do down here, we're now drawing each glyph one by one. And for that, we're going to the layout manager.

And I can get rid of this code. So the old code and the new code are very similar. It's just that one is a slightly lower level talking to the layout manager. And it turns out this is all we really need to do. And again, I'm saying all we really need to do, but this is actually fairly sophisticated stuff because you have to dig down in there into the tech system and it could get messy. But let's see if it gives us the performance we want. Let's build and run.

Okay. Oh, look at that. So I think that's good enough for this person right now. At this point, I'm getting really sick of the worm. Hey, that's sweet.

[Transcript missing]

There's this big white area. That's quite boring, right? So we can add some color to it. Now, if we go back to our draw rect, which is here, one thing you notice is that we do an sColorWhiteColorSet.

That makes the background white. Well, obviously, we could add pizzazz in there very quickly by making this red or yellow or anything you want, but that wouldn't be enough pizzazz, I don't think, for this person. So let's do something fancier. Instead of color, we might want to use an image or a pattern.

One good place to find images or patterns--well, there's a cat, but other than that-- If you go into the desktop pictures folder on your disk, there's a bunch of nice pictures. Here's one. There's another one. So on. Now one that I like that will work probably in this case is one of these ripple patterns. Now I don't know if you can see it there. It's a nice little pattern with a little thing.

And so instead of using an image, let's use a pattern. And let's see how easy or hard that is. Now as I showed earlier, we want to drag this image into our-- let's get this back. We want to drag this into our application like we did earlier. Let's copy it in as resources. We want to copy it. We want to say add.

So now again, this image, ripplesmos.jpg, is part of my application now. And now let's go ahead and use it. Turns out, to use a pattern, you can use NSColors. So therefore, this logic here of setting a color and drawing with it doesn't really need to change very much. Let's go back to VIRM view, and let's add an instance variable.

for the color. So now we have an additional instance variable on top of everything else we did called color. Now back in our VermView implementation where we create all this other stuff, Let's go ahead and create that color. That requires one somewhat long line, but there it is. What we're doing here is like we did before, we do an image named RipplesMoss, which takes that image that corresponds to that thing.

And when we make this call to NSColor saying color with pattern image, that says create a color using this image as the pattern. And then we retain it because we're going to hold on to it, and that becomes this new color instance variable we created. Now, again, being a good citizen, you want to release that when you go away. And finally, down in our draw rect, instead of this red color, which wasn't too creative, we'll use color. Okay? Let me save.

Look at that. Let's also set the speed down to something nice and let's run. Actually, you can set the speed to something high and you see that the frame rate actually isn't really suffering because of this. Maybe just a bit, but it's a whole pattern. Note that this is a pattern and it's being drawn continuously. If I make the window larger, you actually see the pattern again working there. Just by using an image for the pattern, just with one extra line of code, you're able to use patterns instead of colors in your application. That's fairly straightforward.

OK, let me say some words about NSColor. NSColor, therefore, not only represents colors, but represents meta colors, catalog colors, patterns, et cetera. And to paint with any one of those, you just do NSColorSet. And here are some of the interesting methods. We just saw a color with pattern image. Another one, which we saw, is white color. That just gives you the white color. You have red color, orange color, blue color, whatever.

You can create a color with your own RGB values, obviously. That's color with calibrated red, so on, RGB, alpha. That's what you're used to. And finally, we have meta colors, like window background color. This is the color that represents the background color of a window. It turns out that actually returns to the Aqua pattern if you need to use it.

So again, a use of pattern in colors. OK, question. Let's see how much more time we have for questions. So how does the magic typing assistant work? Glad you asked. Okay, the answer is services. Okay, and some of you might have guessed that if you were seeing the little menu flash.

Services allow applications to provide their functionality to other applications. And this magic code we're using is actually a modified version of the TextEdit application. All we did is we implemented this service where every time you invoke it, it takes the next line from the currently open document in TextEdit and pastes it into the current location I'm at. Now, I'm not going to go into detail on services here because the next talk actually has a little more technical discussion of services, so I'm going to leave it there. But let me just show you what I mean here by this little feature.

OK, I'm going to quit the worm. So here we are in Project Builder again. If I click here in text, if I go to the Services menu, you'll see that there's the standard services you're used to seeing. And then there's this TextEdit Plus item, which is the app I wrote, the modified version of TextEdit. In addition to the standard TextEdit items, there's a Get Next line, Move Down a line, and Move Up a line.

And it's been assigned key equivalents, again through services. That's fairly easy to do. So every time I go ahead and invoke the service, Get Next line, it gets me the next line. See, that's the line I was supposed to type, but I did it by hand. Again, I can do it.

and you can I can move up by hitting that thing and it does the same line again and again you know you can get very fast you can see that you can do anything you want and some services work by actually taking the current text selection and working on and returning a new thing and so on. And again I'll leave the discussion off to the next talk. Okay. Slides please.

Okay, so let's talk about what we did today. I think we're pretty much out of time. We added Undo to an unsuspecting app. We added a customizable toolbar to TextEdit. We made a game draw much, much faster that was like 8X. That's pretty good. And we also learned how to use patterns to spiffy up any app. And we saw how useful services can be. So again, a bunch of topics and a representative set of topics that apply to actually a lot of applications.

So what we really saw is how to enhance apps with just a few lines of code. And again, user-visible way. And you can apply this to any application. And you also saw how to do fairly sophisticated things with a little bit more of code. I mean that 10 lines of code you saw where we're using the text system in depth.

That's actually code you wouldn't normally use. But again, just by diving in a bit, learning a few more classes, and again, there's a learning curve. But learning those few classes, you're able to do a lot more than you can. And again, those text system objects, for instance, can be plugged in in very creative ways to create a lot of things. Thank you. fairly interesting text editors, not just text editors.

We have a saying in Cocoa where we've been saying this for many years and I think it's still true. We like to make simple things simple and complex things possible. So again, just with a few lines of code, you can come on stage and make a demo, but by writing a bunch more code, you can actually do fairly magical things that allows you to create a fairly large, very full-featured application.

Okay, as far as the roadmap for other talks, now if you missed these first two talks, I think they would be very useful if you're new to Cocoa and this is the first Cocoa talk you went to. I would definitely recommend seeing at least those two and maybe some of the project builder talks on video. They're quite handy. Interface builder is very, we didn't see interface builder today, but it's very integral to Cocoa development. And the Cocoa overview gives you a good overview of the things Cocoa provides.

Advanced Cocoa topics is right after this one and that's going to be somewhat more substantial talk than this where we're going to go into some of the advanced features in Cocoa, file wrapper, et cetera, drag and drop printing, you know, how to dive into those features. Our performance tools, if you're more interested in learning more about sampler, et cetera, is also at the same time or you can catch that one on video.

It's in room A2. And finally, we have a feedback forum. If you can't get your question answered today or you have something to say, it's tomorrow afternoon at 5. five. and Heather Hickman is our technology manager and the Cocoa feedback and the Cocoa development mailing lists are available for your use.