Mac OS • 1:06:34
This session provides an in-depth discussion of Cocoa topics, such as text, document classes, scripting, localization, Java, and Carbon interaction.
Speakers: Ali Ozer, Mike Ferris
Unlisted on Apple Developer site
Transcript
This transcript was generated using Whisper, it may have transcription errors.
please welcome ali loser afternoon okay so today's uh... talk is Cocoa in depth and uh... The purpose of this session is to build upon the Cocoa Overview session, which you might have attended on Tuesday, to give you somewhat more detail into some of the topics that Becky touched upon that day, and also to give you a flavor more about how Cocoa works and how it's designed. So the in-depth discussions, the topics that we're going to provide you are the document object, scripting, and undo, which are three topics that go in hand. The text system, we're going to give you a little detail on that. Then we're going to give you a bit of performance tips and tools and how it impacts Cocoa in the context of Mac OS X. We're going to talk a few minutes about using Carbon from Cocoa and also how to use Java to program Cocoa. OK, so for the first part of the talk, documents, scripting, and undo, I would like to invite Mike Ferris, who is a member of the engineering staff, on stage. Mike. Hello.
So I'm going to talk about three topics here, which all have a bit in common. Am I holding this wrong? So the document architecture and the AppleScript support and the undo support in the app kit all have some stuff in common. Of the features in the kit, a lot of the stuff in the kit is basically widgets and things that you use in your application but don't necessarily affect the design of your application.
Traditionally, with the kit in the past, you've basically been able to structure your app however you feel you want to do that. And it hasn't really impacted you in your ability to use the kit. But as we try to add some of these higher level features, we find that-- If your app has a particular structure, it's a lot easier for us to do a lot more for you. The structure that we've kind of chosen to hang all this off of is the model view controller design pattern.
And I want to mention that basically all these features are separate, but they work really well together and sort of with the document stuff at the center of the equation. So if you're not familiar with the model view controller pattern, I want to spend just a moment talking about that. This is basically a design pattern. It came from, I think, Smalltalk originally. And it's a way of structuring user interface applications and basically splitting up your object model. Now, it divides models into sort of three-- objects into three categories. There's your model objects that are basically data bearing. They have behavior, but that behavior is basically concerned with how your model, the thing that you're trying to represent in your application, works.
And so you have behaviors here that help ensure the consistency and the invariance that you need in your model. The views-- are basically the things that you use to present data. Most of the objects in the kit are basically view-type objects. They help you to present your data to the user. And then finally, the controllers kind of sit in the middle, coordinate everything. Controllers are a large amount of the code that you end up writing in an application.
So here's a little picture. We can look at this two ways. There's a couple of dotted lines, which you actually can't see very clearly here. Actually, they don't even look dotted anymore. The little, the black lines that kind of go down the middle of the box and divide everything into three spaces, on the far left, we have the model area in the middle where it says NS Document and NS Window Controller. That's the controller layer. And then on the far right, you've got the view layer. Now the two boxes that sort of span and enclose different parts of this, these represent a couple of objects that are key in the document system and show where they kind of fit into this model. In the document system, NSDocument objects tend to be the controller objects that own your model. And NSWindowController objects are the controller objects that own your UI. So that's just a little brief introduction to Model-View-Controller.
So let's dive right into the document architecture. Basically, the intent here is to provide a structure where we can provide a lot of the common behavior that all document applications need, and provide you with a place to put your behavior and easily customize for the kinds of documents that your application deals with.
Now, the, uh, the classes in the kit have a lot of sort of built-in implementation to do things like running the open and save panel, uh, loading nib files, and managing the objects that come out of them, and all the sorts of things that you always have to do.
And they also provide subclassing and overriding points where you get to plug in your specific stuff. Now, the document architecture is also heavily tied in to the application packaging, and in particular, the same data that the finder uses to know what kind of documents your application opens, the document system uses so that your application will know what kind of documents your application opens.
There's three main classes involved in the document system. The first and most obvious one is the NSDocument class. Basically, each instance of this class represents an open document in your app. It's responsible for loading and saving the document. It also is responsible primarily for owning the actual contents of the document, the model objects. you pretty much always subclass NSDocument to provide your specific kind of document, a text document, an image document, whatever it is that your app is gonna do.
And just to sort of look forward a little bit to the other two topics in this part of the session, documents are typically the primary entry point for scripting in an application. And then also, each document usually has an undo manager. So we'll get into those two aspects a little bit later. Now, the next object is basically a singleton. There's one NSDocumentController in your application. It knows about all the open documents. And it also handles behaviors which are not associated with a particular document, like new and open.
This is also where you would go if you actually needed to query the metadata about your document types that's in your info.plist. It parses all that when the application launches and provides you with some queries that you can make. Now, you rarely have to subclass this object. You can, it's easy, but mostly you don't need to bother. The majority of the kinds of things that you're gonna want to customize, you can actually customize through the application's delegate object.
And then finally, there's NSWindowController. Basically, this is a class which manages a window that is typically loaded out of an IB file. And it usually manages a window on behalf of a document. Now, documents can have multiple window controllers. So you might have a CAD application that had four windows, you know, that was showing a top and a left and a front view, and, you know, maybe a preview wireframe of the 3D or whatever. You might also have an application that allowed multiple views of the same document to be open so that you could look at it at different scales or whatever. So documents can have multiple window controllers.
And although you don't have to subclass NSWindowController, it's very common to do so. And one last point I'd like to make about the window controllers is you can actually use them without NSDocument. You can subclass NSWindowController to provide controller objects for your auxiliary panels and everything else, and they provide management of the nib file contents.
So if you were at Becky's talk a couple days ago, you saw this picture. This is a non-document-based Cocoa application. And you may be wondering what the document system sort of changes this picture into. Basically, the delegate objects, the window delegate objects, become-- and it's window controllers. Now, a window controller often is the delegate of its window. It doesn't have to be. but if you're gonna subclass it, it may as well be. And then there are NSDocument objects, right? And you can see that one of these document objects has a couple of window controllers. And other than that, everything's about the same. You can see also the NSDocumentController object that knows about all the open documents.
So that's a very brief introduction to documents. Now an equally brief introduction to scripting. Scripting is based on key value coding. We'll talk about that in just a moment. And the two main areas of the scripting support is classes that support you defining your script terminologies, and then classes that support you handling script commands.
First of all, let's talk a little bit about key value coding, 'cause this is a totally crucial concept in the scripting framework. We get key value coding from the Enterprise Objects framework. And it is basically a standardized way of accessing pieces of an object. What you do in your object is implement accessor methods, and they basically have a naming pattern. You just, you know, you have a get method that's just the name of the key and then a set method that goes along with it. Keys, for example, are things like the line width of this circle and the line color and so forth. And so some of the methods might be, you know, line width and set line width. Width and set width, height and set height, whatever.
The standardization part of this comes in in that there are two methods, value for key and take value for key. So value for key, you just give it the name of a key, like line width. It figures out, oh, look, okay, this object has a line width method. I'm going to go off and call this and return the value of that.
And then take value for key similarly works for setting. But this allows a subsystem that knows nothing about your specific objects to nonetheless be able to, you know, get at the individual pieces of your object and, and deal with them. And so the scripting system makes heavy use of this. How? Well... We use key-value coding to provide automatic evaluation of object specifiers. If you're familiar with AppleScript, you know that... Basically, the receiver of an AppleScript command is usually an object that's qualified along the lines of, OK, well, tell the first graphic of the front document of the application sketch to do something. The first graphic of the front document of application sketch, that's an object specifier.
we pretty much provide automatic resolution of these things using key-value coding. So if your document object has a key, which is the graphics that that document contains, and your application has a key, which is the documents that the application has open, then we can automatically find the first graphic at the front document.
In addition to the simple ones, we can automatically do, say, arbitrarily complex whose clauses in this way. We also use key value coding to provide default implementations for a lot of the common script commands. You can see that, you know, set and get, those are pretty easy to do with key value coding. We can also do stuff like move and create and, you know, count and exists. So a lot of the core scripting suite commands actually have default implementations, and your objects won't have to specifically support them at all. All they need to do is claim that they can support them, and they'll automatically support them.
So let's talk a little bit about terminologies. In AppleScript, your scripting terminology is usually broken up into suites. And so this is the granularity that we have for defining these things. And script suites are defined in frameworks or applications or loadable bundles. They are property lists that live inside of your frameworks or applications or bundles. And at runtime, Cocoa is going to go out and find them all.
All of the frameworks you're linked against, all of the bundles you've loaded, your app itself, will find all the script suites and then combine them all together, and that produces the terminology for your application. Now, the contents of a script suite is basically descriptions of the classes that this suite defines and descriptions of the commands that it defines.
The classes are represented by NS Scripting class description objects. And basically, classes have keys, both properties, or if you're used to databases, sometimes those are called attributes, and elements, sometimes called relationships. And then also a list of the commands that the classes support. Classes can inherit from each other, just like in an object-oriented programming language. And then the script command, the scripting command description class is used to define commands in the script suite. And commands basically have arguments and they have return values and the things you'd probably expect. There's a property list definition for all of this stuff so that you just sort of describe in a property list the various classes and commands, and then at runtime, those will all be parsed in, and these instances of these objects will be created to represent them.
Now, once the script editor, say, knows your terminology, Somebody might write a script and then tell it to run, and at that point, you're gonna start getting script commands in your application. So script commands come in as Apple events, just like they do for any application in Mac OS X, but we take them before you ever see them and turn them into NSScript command objects.
So these basically, NScript command objects are the things that you'll see if you have to implement a handler for a custom command or if maybe your object knows that it can do, you know, counting better than the default implementation of the count command. And... Basically, the script command allows you access to all the important pieces, you know, of that command. So it'll let you get at the receivers of the command, which is basically an object specifier. It'll let you actually get at the real receivers, which is the result of evaluating that object specifier.
And subclasses of NSScriptCommand, are actually where the default implementations are provided in cases where we can have a default implementation. So you'll see in the framework, you know, NSSet commands and Get commands and NSCount commands and so on and so forth. when you define your own commands, you can choose to have a subclass or not based on whether you're gonna have a default implementation and possibly based on other things as well.
And then the other object that we have here is the NSObjectReference class. This basically represents one chunk of an object specifier. So again, graphic one of the front document of application sketch would turn into three object references that are all chained together. And again, object references know how to evaluate themselves using key value coding. Object references are found as both the receivers of a script command and also possibly as arguments to that command.
So this is all, you know, sounds pretty complicated. It's actually pretty easy to get started with this. We provide a lot of the behavior for you. We have a lot of the core suite and tech suite implemented in the frameworks that come with Cocoa. So the application AppleScript class is implemented by NSApplication. The document AppleScript class is implemented by NSDocument. You know, the window class is implemented by NSWindow, et cetera, et cetera. You can see where that's going. The tech suite is pretty much fully implemented by the NSTextStorage class.
So what that means to you is if you wanted to expose in your object model, something that conforms to the text suite, you just have an NSTextStorage object. And so, for instance, in Sketch, right, Sketch has a text graphic type. And you can, you know, type some text in, whatever, right? And the text graphic type exposes one of its instance variables, which is an NSTextStorage, to AppleScript, and then, boom, automatically, the text graphic in Sketch will support the text suite.
So there's a lot of this stuff built in there. You can incrementally start exposing your own stuff. You have to do a little bit of work to start exposing some of your model objects. But often, with just a very small amount of work, you can get a lot of basic scripting functionality almost for free.
Finally, even briefer than the documents and the scripting stuff, I'll talk a little bit about undo. So. First, let me make the point, undo should be implemented in your model. You don't want to implement undo in your interface layer, because then if something else comes along, like for instance, AppleScript, and starts talking directly to your model layer-- that's not a good thing if you're going to support multiple undo levels.
You know, if somebody, you know, changes the font in, through the user interface, and then a script comes along and, you know, deletes that whole paragraph, and then later the user says undo, well, okay, what are you going to undo at that point, right? You've kind of lost the synchronization of the undo stack in the model. So it's very important that undo be implemented in your model. It's also the easiest place to put it. So that works out.
Again, for multiple undo, every change to your document has to be undoable, because if not, you're going to start getting out of sync again. Now, the class in the kit, actually in Foundation, that supports undo is called NSUndoManager, and it basically represents a stack of undo operations. As I said before, a document object typically owns an undo manager, so in document-based apps, undo stacks are per document.
If you're not a document-based app, then by default, undo stacks are per window. Now, NSIndoManager is based on another foundation class called NSInvocation, and what that means is that basically to implement undo, all you have to do is do exactly the opposite of what you're gonna do every time you're gonna do something. Right? Clear enough? Right?
So the code in yellow here is all you have to do in order to make setStrokeColor in Sketch undoable. Now, this is one of the primitive model methods in Sketch, right? The graphic object has this method. This is the way that the stroke color changes no matter who's changing it. And therefore, this is the appropriate place to put this undo logic. We know that the stroke color's never going to change unless it goes through this code. And every time it does change, the first thing we're gonna do is tell the undo manager to prepare a new invocation, which will basically, if undo is, is chosen, end up sending the same graphic object another set stroke color message with the old stroke color, that underbar stroke color, that's our instance variable that, you know, contains the stroke color. So as long as we just say, oh, okay, if I need to undo this, just call it again with the old value, everything is gonna be set up and it's gonna work just fine.
Now, a lot of these undo invocations, because they're implemented in the primitives in the model, might happen during an event, but NSUndoManager, by default, will group all of the, the invocations that get told to it during a single run of the event loop into one thing, and they'll all undo together. So let me give you a little bit of a demo. Mostly this will be a scripting demo. And if we could switch-- ah, we've switched. OK, good.
So I have here a script editor. And I have a little script which I've written that does a very small amount of Sketch stuff here. So this script is targeted at the Sketch application. And perhaps-- I wonder if I can make that, no, bigger. All right, never mind. So you don't need to read this, but basically this is going to-- this is going to tell Sketch to create a new document and then put some graphics in it and so on and so forth. So if I run that, you can see that it creates a new Sketch document, puts a little content in there, and so forth. And then the only other thing I want to show here is Sketch was implemented prior to scripting being available. so scripting was kind of put in later. So nobody was thinking at the time about the fact that somebody might be coming in and changing things besides the user interface. But because undo was implemented in the model, that all just works. When we added scripting, it's fine. You know, things that are done through a script are undoable the same as if they were done through the UI. So, okay, that's the end of the demo. And I believe I have one more slide here, which is just where to get some more info on this.
So there is a chapter of the programming topics document. I think it's called App Design, and it basically goes into a lot more detail on exactly these three concepts-- document, scripting, and undo. There's new for, I think, DP3 is an FAQ on NSDocument. People have questions. A lot of them tend to be commonly asked. These are some answers.
There's pretty good scripting documentation that tells you how to do script suites, how to handle scripting in your app, so on and so forth. There's various release notes that you might want to look at. And then of course there's the Cocoa reference documentation. So okay, now let's bring Ali up and he can talk about text.
is if you were at the Cocoa overview session, you saw Mike Ferris actually put together the text edit demo, and now we want to talk a little bit about the text system that he used in that demo. The text system, we have a fairly sophisticated text system in Cocoa. There are three major high level features. First of all, it's international. It was designed with being international in mind. It's Unicode based. It supports inline input methods. It bidirectional text or vertical text in there. However, because it's Unicode-based and because the API encourages use of Unicode and other proper methodologies, if you use the text system, it's highly likely that your application will just work with bidirectional and even vertical text. It's also flexible. It's got multiple replaceable classes in there, and we'll just talk about the major ones. And it has a wide range of intended uses.
All text field all the way up to a large text view, for instance, the text area of Project Builder, which has colored text and it has a lot of specialized behavior, or the text area in HTML edit, which has even a totally different object model behind it, is built using the text system. It's also powerful. Even if you don't want to subclass and add stuff on your own, it has a lot of features out of the box, as Mike showed during the So there are two, you know, you can approach a text system casually and just use it in your application without doing anything else than just a simple usage. And I'll just talk about two of these. One of them is NSTextField. This is a class that represents, you know, all of those elements in that little window.
The editable field, the password field, the non-editable field, those are all text fields. It's usually one line. Could be multiple, but it's usually one. It's usually non-rich, but could be rich. And it's Read only or editable, you see both examples there. And these are subclasses of NS control. What it means is when the user enters data and hits return, a target action method is sent so you can take action based on what the user changed. So it's obvious what the use is. It's for text fields and stuff, you know, little fields that appear.
The other use is TextView, the other class. This is for more sophisticated editing. Typically they're rich. You know, they have rulers, they have color panels, font panels, you can drop graphics in there, and so on. It's got a lot of, lot of the additional powerful features of the text system accessible directly in there. The good news about both of these cases is they're both available in Interface Builder. You know, you can say new application, drag a text field, drag a text view, and you can check a few boxes and most of the features are available.
Now, so that's the TextView class. Let's look at what's beyond TextView. We have the LayoutManager class. The LayoutManager is the piece of the text system that coordinates the layout of text and so on. And we have the text storage, which Mike talked about earlier, which is basically the back end of text, where the text is actually stored. And in fact, these three pieces form the model, view, and controller, which is what Mike just talked about. The the text, the layout manager lays text out and TextView displays it.
OK. So the NSTextStorage class, which is the model, the back end, it basically stores the text as an attributed string. There is a class in, in Foundation called NSAttributedString, and this is actually a subclass of that. NSAttributedString is basically a NSString, meaning it has Unicode characters in it. Becky talked about this on, on Tuesday. And then it's got on every character an NSDictionary of attributes. And for efficiency, we put these on ranges of characters.
Let me show you a picture. Here is a string which contains, I believe, eight characters. Now, because this is Unicode, we can say there's just eight characters in the, in here. If you're not talking Unicode and you're talking classic one or two byte type encodings, you know, this could be maybe ten bytes or whatever. You know, I don't know the exact byte count, but it would depend on what characters are being displayed. So there are eight characters here.
Now, the first one is an H with an underline. So its attributes for that range of one character is that it's got a color of light blue, it's got a font of Courier 72, and it's got an underline style that's solid. Now, if you look at the next five characters, the color there is light blue and the font is Courier 72. We don't have an underline attribute in that case. By default, if there's an underline attribute, it means there's no underline. Finally, the last two characters, they don't have a color attribute, which would mean that we're using the default, which means white, actually, probably black, assume it's reverse. And the font there is Osaka 72. Now, so there are three ranges here, three bags of attributes, and they apply to these ranges. Now, the, the, one other thing to say about attributes is you can have your own attributes in these dictionaries. For instance, if you want to have a special indexing attribute you add onto text, you can just add them into these dictionaries, call the attribute whatever you want, and you can add any value you want. These attributes will be maintained at the text is edited, pushed around, et cetera and so forth. So it's, you know, you can overload these things and add your own information on any range of character you want.
So the layout manager is the middle piece, the controller. It coordinates typesetting of text. It reads text from the text storage. It lays it out into lines. And then it gives it to TextView to display. So it's the middle coordinator guy. The other interesting thing about layout managers, it also stores information about the laid out text. So if you want to ask, where is line one? Or where are the characters one through 20 displayed on the screen? And so forth. or what's the bounding box that this text took up, this is the place to go. This has APIs to give you that.
One thing to note about Layout Manager, in fact, the whole text system is, you'll note that there are APIs that are in terms of characters and APIs that are in terms of glyphs. Most of the time people use the term characters to mean both. But one, the distinction, the important distinction in our APIs where we sort of have to be exact is that characters refer to the elements in the text storage. Each Unicode character is a character.
And when we say character range, we're talking about the range in the text storage. Glyphs displayable elements. Glyphs are usually elements of fonts, and they are computed as a result of laying the text out. And the characters and glyphs are distinct. And in fact, one character might lead to two, three glyphs, or maybe several characters can be combined to show one glyph. And which glyphs get shown, display, which glyphs are shown basically depends on the font.
Let me give you an example. Most good text systems would take an F and an I, the two characters, and put them together to display the ligature FI. So the, the ligature is really just one glyph. So the character range that contains two characters corresponds to a glyph range that only contains one glyph. So, you know, if you look at the NSLayoutManager API, keep this distinction in mind, because it's important.
Finally, a little more detail about the text view, the view part of the whole picture. It's the user interaction layer. It displays the glyphs. It also handles all the events, key up, key down, et cetera, all go to text view. It also deals with the ruler, the font, and all the other color panels, and all the other user elements that basically the user is doing. It also turns out that if you want to have multiple columns, multiple pages, multiple weird areas, you just have multiple text views that are attached to the same layout manager.
Now one thing to note here is that nstextview is a subclass of a class called nstext, which in turn is a subclass of nsview. And nstext is a sort of a legacy class that's in there. nstextview is a class you should use, but for the whole picture, the whole API, you should really look at both textview and text, and, you know, think of those as the combined API for textview. It turns out you can do most things with the text system using textview, but for more sophisticated usages you would go beyond textview and talk to the layout manager or, or text storage. So just to recap, text storage stores the text, layout manager lays it out, and TextView displays it.
And two other interesting text system components are the text container class. Text containers display, they basically describe the areas that text should flow into. In fact, you can use a text container to describe a doughnut shaped area, whatever you want. You know, it basically describes the area. It turns out there is one text container per text view. And in fact, it turns out that you can rip out the text view from this equation and just leave the text container and have a text system that doesn't display, but instead can be used to layout a newspaper, for instance, in the back end, without ever displaying it, because it's going to the printers or whatever. So text container is the part of text view that describes the area. And finally we have typesetter. There's usually one instance of this. Typesetter purpose is to get the glyphs and put them on a line and basically do all the justification. So typesetter is invoked by layout manager as a part of laying out the text.
OK, so so much for text. Now I want to talk a bit about performance. It turns out there's a performance session going on right now, which you're all missing, so we figured we might, we'd give you some of the interesting aspects of, of that talk here, especially how it relates to Cocoa.
So in the context of a Cocoa application, and in general, actually, Mac OS X as well, there are certain causes for performance issues. Memory, or the lack thereof, network or disk operations, or too much of them, too much drawing, and polling. So these turn out to be major concerns. It turns out that pure CPU usage is rarely the problem. I mean, typically, if your application is slow, you're rarely going to find that it's because of lack of CPU power. But, of course, it happens. Bad algorithms can strike anywhere.
So let's talk about memory. One cause for problem is maybe one problem that you might not have on Mac OS is window backing stores. Both Carbon and Cocoa apps all have backing stores corresponding to every window that's on the screen. In fact, every window that's even not on the screen. So if you have a 1K by 1K window that's 32 bits deep, that's, I think, 4 megabytes of memory right there for that window. And of course, this window store is something that's used to give you a really nice display, flicker free and so on. Another reason is the custom heap management. A lot of applications might be doing their own heap management, especially on Mac OS, and this turns out to cause, this turns out to be a potential problem for apps running on Mac OS X. Finally, failure to release memory. You know, just leaking it or holding onto it too much turns out to be a potential problem. So window backing stores, as I said, windows are by default buffered. Also, NSImages, which is the image displaying component in Cocoa, NSImage will sometimes use caches, which are basically window backing stores, to cache images in.
And again, you know, you load hundreds of images, that could add up to a few megabytes in your application. Now, there are, there are tools to look at these, and I'll mention these in a few minutes, but there are a few solutions in this area. You can make your windows one shot, which means when the window is no longer on the display, its memory is released, and every time the window comes back on the display, a memory region is created for it, and then the window is displayed. And you can assure that NSImages are not creating caches, and there are several ways to do this.
So one other solution that you might think about is why use the buffering at all? And it turns out that using buffering allows windows to be flicker free. If we didn't have buffering, when you do that lovely window moving on top of other windows, all the exposed regions would have to constantly be drawing themselves to keep up with, you know, keep up with the users moving. And, you know, that's a bit Windows-like maybe, and it's a bit, you know, not in the spirit of Aqua.
Custom heap management, again, a lot of apps, you know, in order to assure that they have enough memory to complete an operation might be allocating large blocks of memory which they carve themselves. Maybe the memory that they used before in Mac OS 9 was not quite enough for them. It didn't do the right interrupt handling, whatever. It turns out that this is bad because it just causes fragmentation. If every subsystem allocated one megabyte for no use and carved it out, there are all these little pieces of memory that are not being used in the system. and perhaps they're being kept hot because the custom memory management ends up touching all of those bytes. And another disadvantage here is that standard tools to track memory, which we'll show you one of, you know, it won't work. It doesn't know how this memory is allocated. So the solution here is just to use the standard memory system, the standard malloc that comes with the system. It's pretty powerful. It covers a wide range of uses. It should be good enough for almost all uses on Mac OS X. Finally, failure to release memory is the last point here. As Becky mentioned, Cocoa introduces reference counting where objects can retain each other and so on. So one problem is that, you know, you simply retain something or you have a copy of it and you don't release it. Well, that's an obvious memory leak, you know, you have to release it. Another problem is sometimes one object can retain another object and that object in return retains me, so these two objects are retaining each other. They might be totally useless, but they're just sitting around, not getting freed. And to solve that, you really need to be consistent in how you do retains and releases. One, one guideline we use is, if you have parent-child type things, you know, where you have a hierarchy of objects, typically the top level objects retain the lower level objects, and the lower level objects, even if they point to the higher level objects, do not retain the higher level objects. cases, loose connections. For instance, in Cocoa we have delegates, outlets, targets. Those are typically entities pointing at each other. Typically those loose connections do not involve retains, meaning an object does not retain its delegate or its targets. Because those are all entities that are objects in their own regard. So the performance tools to track malloc memory, there's object alloc and malloc debug. I believe we'll show you object alloc in a few minutes. To track all memory, meaning everything an app might be touching, that includes all the shared frameworks, et cetera, there is tools like TOPS and PS, which you run on the command line.
Now, if you run these and you see V size, which means virtual size, you might at first be shocked. You'll see numbers like 80 megabytes. That includes all of the shared frameworks and all the other things that are shared across the system. So that's not really the number that's-- what your app is actually occupying. But looking at number and seeing whether it's growing smaller and looking at trends at that number is still interesting.
Finally, the track window backing stores, there's no tool on DP4, no obvious, no easy way to do it, but Chris will show you a tool that's, I think, going to be released pretty soon. So quickly, the other reasons, network or disk operations. These take a long time, relatively speaking, and you probably all know you've had to deal with these things. These might have an unpredictable effect on performance because sometimes the disk is slow, sometimes network is slow. One thing to watch out for is on Mac OS X, user directories can be either on a local machine or they can be in a network. And we go to the user directory to read stuff like preferences or you might put caches there or other documents. If you always test in the context of a local user, and then you might find that on the network user case where the network access is much slower, you might find that things are just way slower and you weren't prepared for network accesses. So... And the thing here is obviously you should avoid or delay or call us operations that touch the disk. For instance, if you're writing preferences out, don't write them out every time something gets changed.
Maybe wait to write them out at the end. Cocoa applications actually write out the preferences when they're quitting, so you could just let the Cocoa system do it for you when you update preferences. There are tools, Sampler, I believe Chris might show that, and FS usage. FS usage is a command line tool which will show you all file system calls that a process whole system is doing. It's pretty entertaining if you haven't seen it.
I mean, entertaining in a sad sort of way. Okay, and drawing too many times is another common problem. Sometimes in object-oriented environments, because multiple subsystems might be doing drawing, you know, two different subsystems might cause update of an element, and you just might not notice. It might not be obvious. NSView, the abstract drawing class, actually provides facilities for managing and coalescing dirty regions. So instead of causing a display immediately, you should call set needs display, which records in its view that this region updated, and eventually it will display at the appropriate time. We have various tools, and again, I think Chris might show one of them I mentioned, that show you displaying as it happens, so it gives you an idea of what's going on.
Finally, polling. Polling is bad. Sometimes polling is used, I think sometimes Mac OS 9 applications use polling more than, say, on Mac OS X. Polling is bad because it keeps applications awake, the kernel has to schedule them in, the application's memory is kept hot, and it also interferes with power management and with scheduling. I mean, the kernel is trying to schedule applications and there's an application that every tenth of a second says, anything happening, anything happening, anything happening, you know, you eventually, you know, you can see how it could be a problem. So the solutions are, there are pretty good solutions in the system. There are event mechanisms both in Carbon and Cocoa that let you catch events when interesting things happen. Mouse down happened, mouse move happened, et cetera, rather than you sitting there polling the mouse location. And there are also notifications that will tell you that certain things changed. Another thing is, even if you can't avoid polling, maybe you can do polling at key points. For instance, when your application becomes active, you could update the state of some certain thing that you're displaying. Perhaps when your app is active, the chance that it's being changed behind you are very small, so it's not interesting to update. And Sampler and TOPS will again show you the cases for polling. Okay, so I will now invite Chris Kane on stage for a demo of some of these tools.
Let's see. In the short time we have, I don't have nearly enough time to go into any detail on these apps as far as how you go about to use them to solve particular problems. I just want to show that the apps exist and generally what they're about. The first app here I've launched is Sampler. And I am going to choose-- let's see, text edit. And I'm going to sample the launch of text. And so what I've done is I have just hit the button that said launch and sample. And now it's done.
And what we have is a call graph on the left to the center of the window. And this is showing the call stacks that Sampler found the app in, if you will. What Sampler is doing is every, in my case, 50 milliseconds, it's going out to the other app. It's stopping it. It's going and plundering around in the application, finding out where all the threads are, what they're doing. and it's recording that information. And so what it's displaying in the call graph is the sort of coalesced call tree view of that, of what was going on.
Um, over in the call stack, uh, list, over on the far right, what's going on is Sampler has chosen the hottest, um, frame, hottest node at every frame in this coalesce call graph, uh, to show you, you know, what, what's the hottest thing going on. Um... What we're seeing here in this case is the app spent most of its time while it was sampling. It launched very quickly, and then I let it run a little bit.
And it has spent most of its time just blocked waiting. So that's what this is doing. We see NS application run. It's looking for a next event. And a lot of internal routines here. We see CF run loop is being run. So we're going down into the layers of the system. And eventually we trap into the kernel and we're blocked in the kernel waiting for an event to come in, a user input event. Now I haven't clicked over here so no user input events have come into the app, certainly not while I was sampling.
So one thing I just wanted to point out while I was here is that it's very normal, very normal to see this sort of backtrace. That if you find your app in the situation where the Mach message override trap is stuck in, essentially, that is normal. And that is good. And we want apps to be very quiet, not doing anything while they're in the background, like text that's in the background. If I sample, again, here, let me crank this down to one millisecond.
So every one millisecond, stopping the app and sampling. Now, one thing to note is that I have not built a special text edit here. This is just the text edit that comes on the system. I don't know if there's any way for me to prove that. I guess you'll just have to trust me there. But to sample an app, you do not need to specially build a profile version of the app or anything like that. And so what we see is while I was sampling, all the samples, all 5,439 of them, are stuck in Mach message override trap, waiting for events to come in. So that would wake up, for example, when I click the window there, and go and do some drawing and stuff like that.
So that's Sampler, that's just a very quick look. The best way to find out more about these applications is to go in and play with them and see what they do. The next app I'm going to show you is Object Alloc. Object alloc is a way of looking at the memory usage of an application. I'm going to choose text edit again. And I'm going to choose the binary.
So here we see the Object Alloc window. The first button over here is the Start button. I'm going to start up the task. Now it's launching TextEdit. What we see here is the histogram of various categories of allocations. Object Alloc is seeing all the allocations that are done by the application and is categorizing them as best it can. In some ways, it's more sophisticated, and in some ways, or in some places, it really doesn't know how to categorize an allocation, so it just gives it a generic moniker.
Now, the font is probably too small for you to see there, but the first column is a category, and the category name in the first row here is Zone Malloc 22, and what that means is just a generic allocation occurred of size 22. We have a current column. That's the number of blocks, which are 22 bytes in length, that are currently still allocated. And the other most interesting column is the total column, which I've selected here and we're sorting on. And that shows that 2,075 of these blocks, blocks of length 22, were allocated while the program was running. One interesting feature we've added since DP3 to ObjectAlloc is that we understand some of the CF types. So in the second row, you see CFStrings collected, and it identifies these particular strings as the immutable ones. And I'm going to go over it in the second tab here, and if I click on CFString immutable, here it's showing me a list of all the CFStrings that are currently allocated in the application. So if I click on one, it's showing me the contents of the string down here, for example. There's a string with.globalpreferences. There's a string with a path in it. Thank you.
Um, so, uh, that also works for very, um, other types of, uh, objects. So CFArray, the mutable variable kind of, uh, CFArray. Here it's showing the debug description. Uh, this is an empty array. The count is zero. Um, let's see, is there one with something interesting? A lot of empty arrays are pretty common. And there's an array with one thing in it, which looks like a string, a path to the, uh, application I launched, in fact. And so on. So here's CF set, and here's a description of a nasty CF set with a bunch of stuff in it. So that's object alloc.
The last thing, and there are other things you can do with object alloc. I suggest, you know, running it, playing around with it a little bit. You know, here I'm going to demonstrate this in a moment, what's going on when this happens. But you can scale the view, for example, sort by the different columns and so on. The last thing I want to show, let me stop that.
is, let's move it over here, this new tool Ali alluded to called Quartz Debug. It might not be called Quartz Debug when we release it, but hopefully it will be released soon. It's not in DP4, but it goes in and talks to the core graphics server, the Windows server, Window Manager, and tells it to turn on various graphical debugging features.
So I'm going to-- let's see, what-- Let's turn on all of these. So what we see, what we saw briefly there, is the bits of the window that are being redrawn are first drawn in yellow. And then they're drawn with their true content. So if I click on a button, for example, We see--it's hard to see that the button is actually being redrawn since it's so quick. Let me turn on the delay. If we turn on the delay, We see the very labor-intensive process that graphical drawing goes through to produce a window as complicated as Object Alloc's window. So here we see the tab view being drawn. There, that background pattern's being blatted onto the screen. Da da da da da. Here come the tabs. Yada yada yada.
The outline view, every row. and so on. So turning on the delay allows you to see where you have redundant drawing. I'm going to turn that off. That's just too painful. I'm not going to lie to you. Okay. So we've recovered. But I've, I've, I've, so I turned off the delay, and now we're just, you know, every time the window has to become inactive, of course, we have to redraw all the controls because they're now gray instead of blue, and, and things like that. One thing I've noticed as far as debugging here is that if I choose a different column, You can see, if you look closely, that the whole outline view is being redrawn. And that's probably correct and okay. If I, you know, change the view around, the outline view is just drawn once. But if I change the slider here... Do you see that? It's being redrawn twice.
Really obvious if I do it out here. Nope. There. Twice. And in fact, the slider is also being drawn twice, so that's a little more subtle, and you might not be able to see that going on up in the screen. So I probably have a bug here that when I'm readjusting the scaling, I'm over-displaying the system. I might have a direct call to the display method in there, for example, rather than doing set needs display and letting the AppKit display system take care of that for me. I'd like to call Ali back. Where did he go?
And if we could have the slides back. Thank you, Chris. Okay. So we still have a few minutes, and I'm going to talk a little about using Carbon from Cocoa. Now, as you know, Cocoa and Carbon are actually two separate programming environments, and they don't share the same windowing structures, the responder chains, events, and so on. But our goal is for developers to be able to use Carbon features from Cocoa and Cocoa features from Carbon, and that's the ultimate goal we have. And I'll just talk about where we are in that. Now much of the plumbing under Carbon and Cocoa apps is the same. Calling conventions, we use the same compiler, it's C, so no problem there. The application packaging, you've probably heard about NSBundle, CFBundle, the application packaging, it's exactly the same between the two apps. The low-level APIs, POSIX, etc., the same. And basic data types, an int is an int in both worlds. So that's all very good.
So fundamentally there's no problem calling Carbon between Carbon and Cocoa. You can call a Carbon function from Cocoa, no problem. But there are some restrictions as to what you can do. Now if you look at the umbrella frameworks picture, which you've seen about 40 times I bet, you see that we have the various levels below the two. I think in general we can say that you can invoke Carbon services from application services and core services frameworks without any problem. So examples of Carbon APIs you can use from Cocoa include Quick Draw, Apple Events, File Manager, Resource Manager, Text and Coding Converter, Speech, and anything else that comes from low-level Carbon or application services. And of course, Core Foundation.
Now, high-level toolbox APIs, the high-level toolbox is the fundamental drawing stuff that is in Carbon, and it sits in the Carbon framework bucket. Those in general are currently not available. You can try to call them, but in general, events might not work, the drawing might not be right. So at this point, this is not something you can do in the context of a Cocoa app.
Now, I said that you could pass basic data types back and forth, so obviously C types and structures, no problem. Those are all the same between the two environments. Now, there are APIs to convert other types. Some of the more fundamental types you might be using in your app are file paths or FSREFs or URLs. And, for instance, File Manager, a Carbon Manager, and CFURL, which is in Core Foundation, both provide ways to convert URLs and paths back to FSREFs and FS specs and so on. So you could actually intermix those two APIs by doing these conversions. And also, some Carbon APIs still take Pascal strings, and in the context of a Cocoa app, you might have NSStrings or CFStrings, and then you can use CFString or NSString conversion facilities to call down to those APIs. Now, I'm saying NSString or CFString. This brings us to toll-free bridging, which enables passing, which enables treating those two as the same. What toll-free bridging does is some CF types are the same as their Cocoa counterparts. CFString is the exact same as an NSString. Array, dictionary, data, URL, the same thing. And there are a few other classes this is true for. What this means is, if you have some Cocoa API and it gave you an NSString, and you have to call a Carbon API that takes a CFString, just cast away. It's the same thing. No problem. OK? So this is -- thank you. So I can just pass these back and forth.
Okay, so, so much for Carbon. Now, I mean, as I said, our intent over time is to improve the bridging, and currently you might find that some areas are not properly bridged yet. And if you hit upon a wall, if you hit something you need definitely, please let us know. And finally, the last topic for today is programming Cocoa with Java. As you know, Objective-C and Java are both the languages with which you can program Cocoa. And here I'm going to talk about some of the design flaws of what happens when you use Java to program Cocoa. You can access most Cocoa APIs using the Java language, and I'll explain why it's not everything. Cocoa objects can be subclassed in Java. So if you have an NSView, which is a Cocoa object with an Objective-C, and you want to have a subclass of it in Java, you can do that. You can also pass objects back and forth between Java and Objective-C. So NSView is an Objective-C class. It wants, you know, some object. It wants to have a window pass to it, you can pass a Java version of it back and forth. It's all automatic. And your applications can either be hundred percent Java, or you can make them hybrid, meaning parts of them can be Objective-C, parts of them can be Java.
So the philosophy we have in the Java APIs is that we want developers to be able to use the Java language to program and to create Cocoa applications. However, we want to preserve and expose the Cocoa programming model because, you know, we have these, this Cocoa application model. It's, you know, one way and you should be able to use that in the application. But we also want you to be able to use Java paradigms and objects where possible and where it makes sense. One example is the Java string class. It's very, it's all over the place in Java. And in the Java side, you might, you might, it might be a better idea to use the Java string rather than use the NSString class.
And function, functionally they're pretty much equivalent. The other goal we have is that you want to be able to integrate and use Java libraries as much as possible. Java network libraries, whatever else you might have. Java utility libraries. You should be able to use them in the context of your app. And this is pretty much confined to the lower levels, meaning at the UI layer it might not work so good, but the lower levels, networking, et cetera, those APIs are all available to you.
So I talked earlier that, I said earlier that most APIs were exposed. It turns out almost all of the app kit is exposed, using this name, com.apple.beep.application. OK. And then we have some of foundation exposed. When I say some, it turns out to be, I think at least half of it, com.apple.yellow.foundation. Now the areas that are not exposed is basically areas where, you know, we, we decided that Java has an equivalent. And finally there are additional frameworks, com.apple.yellow.foundation. And then there scripting has the scripting functionality that is added onto the Cocoa frameworks. Now, we will rename these things at some point soon, so.
OK. So most objects are exposed directly. We call these wrap. So NSView in Objective-C is the class com.apple.yellow.application.nsview in Java. Now I talked about the string class, which is not exposed. Those are morphed, meaning any time it goes to the other side, it goes to a change, it basically changes into an object of the other language. So NSString instances are converted to the string class in Java. And there are a few other classes to which this happens. And we repackage some functionality. For instance, categories is an Objective-C feature which lets you add methods on top of an existing class. It's not available in Java. So in those cases, we either take the methods that are in a category and fold it into the base class, or we repackage them into a separate object. And C functions, which Cocoa has some of, are not exposable directly in Java, so we usually package them into a utility class, much like the math class in Java.
And Cocoa also has a bunch of structs and selectors, and Java doesn't have a concept of structs and selectors, and those are converted to Java objects as well. And as far as method name mapping, in Objective-C, method names can have arguments, keyword arguments, or keywords for each argument. For instance, post event at start, there are two arguments there, one between each colon. In Java, we convert that to post event, meaning we drop the other argument names, because often the main part of the name is good enough. And Java also allows overloading, which means you can have various versions of that. when the name would be not very obvious, or sometimes when the second argument is as important as the first one, we tend to keep the whole thing, so set object for key becomes set object for key. Finally, init methods, which are the constructors in Objective-C, in Java are just converted to constructor methods. For instance, the NSView init with frame method, which is how you create an NSView, in Java is simply NSView, which is how you create objects in Java. So those of you who are familiar with Java probably have tools to inspect and look at Java APIs, like JavaP and so on. One tool we provide on the system is this app called Java Browser. It's really handy. It just shows you a browser up there of all the classes it knows about, which includes the Cocoa classes plus all the other classes, including the Sun classes. And you can choose any class, and down below it will show you a nicely formatted API for that class. So it's a fairly handy tool if you're, if you want to browse through the APIs. advice.
OK, so I think Becky went through the documentation. There's documentation on DP4, release notes and so on. The release notes are really quite handy. There's documentation in system developer documentation, Cocoa. You can also go to the website to see if there's updated documentation in any of these areas. We have example code in both system developer examples app kit and system developer examples Java app kit for Java applications written with Cocoa. And if you have a chance, if the facilities will provide to review them, you can choose to do so. Let's see, the localization talk is happening right after this. I think that's Mike talking about how to localize Cocoa and Carbon applications. The Quartz talk is tomorrow, where it talks about Quartz APIs that you can use directly from Cocoa applications if you want to. And the performance talk, that is pretty much wrapping up right now, I believe.
So, okay, let's see. So I think we have about ten minutes. I would like to invite Mike and Chris back on stage for some QA, and Becky, I think I see her there too. Now one thing, we have a brand new address, [email protected]. It works. So if you have comments and feedback on the APIs, new features you'd like to see added to AppGate and Foundation and such, please send us mail. Thank you.