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

WWDC00 • Session 125

Core Foundation: Advanced

Mac OS • 52:33

This session delves into the rich set of services available from the Core Foundation APIs such as bundles, plug-ins, XML, and preferences management.

Speakers: Becky Willrich, Doug Davidson

Unlisted on Apple Developer site

Transcript

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

Ladies and gentlemen, please welcome David Wright, Mac OS X Applications Technology Manager. Welcome everybody to session 125 Core Foundation: Advanced. So after the last session I heard two comments. One developer said this stuff was blowing him away and someone else said they can't wait to get their hands on it. So I'm really pleased to introduce more of it in this advanced session. So at this time please welcome Becky Willrich from the Cocoa Framework team.

Thank you, David. Wow, well welcome back. So we're just going to continue what we started talking about this morning. I'm Becky Willrich still. And what we're going to do today in this afternoon is continue where the Core Foundation basics picked up. So for those of you that maybe did not make this morning's talk, we're going to repeat some of the initial parts where we talk about where Core Foundation lives inside of Mac OS X. And then we're going to move on and look at some of the subsystems and services that Core Foundation provides.

So, in particular we're going to be looking at CFBundle and CFPlugin and what they can do for you. Then we're going to talk a little bit about preferences and how CF can make it easier for you to manage user preferences in your application. We're going to look very briefly at what XML services are provided. And then finally we're going to spend a moment talking about advanced memory tricks that you can use when working with Core Foundation.

As I mentioned in this morning's talk, Core Foundation lives right above the operating system layer. And at that layer we are very concerned with performance because we impact most everything that runs on the system. That's reflected in the APIs and there are a number of advanced tricks you can use to reduce your memory footprint.

So just to repeat what I said this morning, Core Foundation is the lingua franca on Mac OS X. It is the language by which you can communicate across both Carbon and Cocoa. So there's the same definition you saw this morning: a medium of communication between peoples of different languages. And for us, we're interested in communicating between Carbon and Cocoa.

So there's the architecture diagram for Mac OS X. And there we are inside the core services layer. The core services layer, remember, is that layer where many common application needs are provided that are below the graphics layer. So in particular, you'll see the file manager inside of there, the alias and resource managers as well. And beneath all of that is Core Foundation providing the basic types used throughout the system.

So this morning we talked about the basic data types in Core Foundation: strings, arrays, dictionaries, and then finally how it all came together as property lists. Now we're going to focus on the second half of the equation: the non-graphical services that Core Foundation provides to all its clients. In particular, we're going to look at localization support as provided by CFBundle and user preferences.

So what do I mean when I talk about a CF service? What I'm talking about is an integrated subsystem that's built based on the CF types that we saw this morning. There are several inside Core Foundations, some of which you are likely to never use yourself. CFRunLoop is an example.

We use the RunLoop to maintain the main event loop in both Carbon and Cocoa applications. In all likelihood, you'll never need to use it, but it's silently doing its work inside of a Carbon application or a Cocoa application. Others you will find yourself using directly. CFBundle is an example of that.

So this afternoon we're going to be talking particularly about CFBundle, which you will use to get at the different pieces inside your applications package. CFPreferences, which you can use to store user preferences. CFPreferences will do the work of reading and writing to file and creating the persistency format for you.

And CF XML Parser, which if you're interested in parsing XML files, obviously we'll give you a hand. So with that, I'm going to turn the stage over to Doug Davidson, my colleague, who's going to spend some time talking about bundles in the app packaging scheme. Doug? Thanks, Becky.

So I'm Doug Davidson. Good afternoon. I'm here to talk to you about bundles. What they are, what they do, how to use them. By the end of this talk, you should know what bundles are and what they can do for you, how to use the CFBundle APIs to deal with them, and where to go to get more detailed information about them.

So, first of all, what is a bundle? A bundle is a way to treat all of the files associated with an executable as a single object. Now, once upon a time, an application was a single simple thing, just an executable file. But, applications need resources. So you add a resource fork and you put resources in there and you still have a single file.

But, applications have a way of accumulating other files with them. Additional executables, libraries, text files, quick time content, HTML, all sorts of stuff. Now, with Mac OS X, it gets even worse because you might want to have potentially different versions of this stuff, one for Mac OS 8 and 9 and one for Mac OS X.

Plus, this is a multilingual system, which means that you would want to have different versions of these things. Some of these things for the different languages or language regions that your application is localized into. So, how are you going to hold all this stuff? The answer is in a bundle.

Now what can a bundle hold? First of all, a bundle can be wrapped around many different kinds of executables. Application packages are bundles. Frameworks are bundles. They have loadable bundles. Plug-ins can be bundles. A bundle can contain multiple executables and it can potentially contain different versions of these executables, one for Mac OS 8 and 9, one for Mac OS X, if you like.

[Transcript missing]

Some characteristics of bundles. Bundles are cross-platform. That means Mac OS 8 and 9 and Mac OS X. Anything in them can be versioned. One version for Mac OS 8 and 9, one version for 10. They are multilingual. You can have different versions of your resources for particular languages or particular language regions. Cross-binary. On Mac OS X we have both CFM and Mac OS binaries. And CFBundle will automatically know the difference, understand both, and automatically provide the glue to allow you to call between them.

Self-contained, movable, renameable. They're designed to be a single object to the user so that, for example, your install procedure can just be drag and drop in the finder. For your uninstall procedure, just a drag and drop, rename them. As long as the internals are not touched, the bundle does not break. And finally, file system independent. That means they're designed so that it is not necessary to use resource forks.

So that these bundles can be moved to file systems that may not have support for different forks. So you could store them on arbitrary file systems, arbitrary servers, CD ROMs, etc. And without loss of fidelity, you can just drag and drop from them. Or maybe you don't even need to install them. Maybe you just launch it directly from the server.

Now, I should say one thing that bundles don't provide is they don't provide a space for you to write on. If you have files that you need to write out, those should probably go somewhere else. And one reason for this is because your application might have been launched from a server, from a CD-ROM. It's kind of hard to write to a CD-ROM.

The next section of this talk about CFPreferences will go into a little more detail about possible places where you can write out files. Now, I thought that probably the best way to explain about bundles would be to just show you one. So, if we could get demo two here.

I've made up a simple bundle. This happens to be an application package. I called it simple test. And so here's my install procedure. Notice I'm copying to the desktop. That's it. Here's my uninstall procedure. Let's see that again. Okay, now just one thing. It's obvious how to launch it. Double click on it. And here's my application. Okay. But suppose I prefer to work in French. Okay. Now I'm working in French. Here's my application in French. Or maybe I prefer Japanese.

One thing I want you to notice is that this is not just picking a single language, this is a preference order. So suppose I prefer Italian, my second choice is German, then French. Now, I didn't actually localize this application into Italian. So when I launch it, what I get is my second choice, which was German.

So now let's take a look and see what's inside this bundle. Now what I want to say is this is a typical bundle layout, but I don't want you to pay too much attention to exactly the details of where things are located. There are several variations, just look at the various components and how they work together. See a bundle understands all the variations, past, present, and future, and it will deal with them for you. So what do we have? First of all we have our executables. This is the executable for Mac OS plus 10.

And there is another version of the executable for Mac OS 8 and 9 called-- labeled Mac OS Classic. What else do we have? We have the metadata that I talked about. There's a special file called package info, just holds the type and creator information. and then we have the Info.plist which holds our info dictionary. So I want to take a special look at that. And here that is. And I have set up a number of keys in here. I just want to point out some of them. CFBundle executable. That is what tells CFBundle what your primary executable is for this bundle.

[Transcript missing]

There are a number of others. Let's see. See a bundle identifier. This is supposed to be a unique identification string for your bundle. We recommend that people use the format that may be familiar from Java, which is com.yourname, and so forth, a reverse Internet name. Just so that you'll get a unique string so that each bundle can be uniquely identified.

Now, there are many possible keys that could go in the info dictionary. And, um, I'm going Some of them have significance to see a bundle. Some of them have significance to other things like the finder. There is going to be another talk tomorrow, I believe it is, on application packaging in particular, which will discuss in much more detail what the finder wants to see. Some of these entries in the InfoDictionary can be localized themselves and I'll go into that a little later.

So what else is there in the bundle? Well, we have the resources. And first of all, let's take a look at the non-localized resources, those that don't depend on which language you're using. And simpletest.rsrc. This contains the non-localized resource manager resources for this application in the data fork of that file. And there is also another version of that, SimpleTest Mac OS Classic, which contains the same thing but for Mac OS 8 and 9.

You don't actually have to have that, but if you have separate versions, you can have separate versions for different platforms. Then I have an icon and, oh, maybe a movie. Anything you want, you can put in here. Now we also have localized resources, and these are in separate folders for this type of languages or language regions. So for English, I have in localized.rsrc, I have the localized resource manager resources for this application.

And then I have the simple test.nib, which is one of those interface builder, interface definitions. And if you haven't seen interface builder in action, you really should. Then I have info-plist.strings. This is what contains the localizations for any of the items in the info dictionary that need localizing.

and localizable.strings, which contains localizations for other CFStrings. We give special attention to the localization of CFStrings because the CFString is our basic Unicode-savvy, internationalization-ready string, and so it's the most common thing you want to localize. and then anything else that I want to put in here. And then you'll see that the French The CFBundle automatically picks the right one based on the user's language preferences and on the platform. There is also another talk tomorrow afternoon specifically on Mac OS X localization which will go into this in much more detail.

Now, if we could get the slides back on... So, now we just talked about bundles as they live on the disk. Now what is a CFBundle? A CFBundle is a programmatic object that represents the bundle on the disk to your application. So, if you know that there's a bundle, how do you create the CFBundle that corresponds to it? A canonical way to do so is simple. You take the URL that points to the bundle's location and pass it into CFBundleCreate and you get a CFBundle.

If the code for the bundle is already loaded and running in your process, then there are other ways to get a hold of the CFBundle. For example, the bundle for the application itself is called the main bundle. And you use CFBundle get main bundle, and it gives you the main bundle, no arguments.

If you have some other bundle, perhaps a framework or a loadable bundle, plug-in, and you know that its code has already been loaded, then you can look it up by its identifier. Remember, the identifier was that unique string that we talked about. This is very useful, for example, if you have a framework, a loadable bundle, you don't necessarily want to know exactly where it's located on the disk, but once it's loaded inside its code, you want to be able to find its bundle. So you look it up by the identifier.

Now then, once you have a CFBundle, what can you do with it? Well, probably the most important, most common thing is to look up these bundle resources. Again, it's very simple. CFBundle copy resource URL. You pass in the name of the resource you want, and CFBundle returns you a URL that points to it.

It will look up the correct version based on the user's language preference again on the platform. Another thing you may want to do is to look at the metadata from the info dictionary and see if bundle get value for info dictionary key. You pass in a key, it passes out a value correctly localized if that particular value happens to be localized. And there's also a CFBundle get package info which gets the type and creator that I mentioned in the package info file.

What else can you do with CFBundle? Well, as I mentioned, you can have resource manager resources. And so there is an API CFBundle, OpenBundleResourceFiles, which simply opens both the non-localized and the localized resource manager resource files for that application. Now, in case of an application bundle, you actually don't need to call this. It will be done automatically for you on launch.

But if you have some other kind of bundle, framework, loadable bundle that have resource manager resources in it, you might need to call this API yourself explicitly. And as I mentioned, we give special attention to the localization of CFStrings. And so we have a number of functions starting with CF copy localized string to get the localizations of CFStrings out of files like the localizable.strings that I mentioned.

What else can CFBundle do? Well, CFBundle can also load code. And again, it's very simple. All you do is tell your bundle, load, CFBundle load executable, and it loads the code. CFBundle automatically figures out on OS X whether the code is CFM or MachO and uses the correct procedure to load it. Of course, on OS X and 9, it's just CFM.

And once you have the code loaded, then you can, if you want to call into it, and you can look up functions by name, and CFBundle will return you a function pointer. And, um, Whether you are calling from CFM or MacO and whether the bundle you are loading is CFM or MacO, CFBundle automatically figures out and returns an appropriate function pointer. In either case, it can be used to call.

One caveat though. On DP4, if you are calling this from CFM, then in order for it to work correctly, you must have turned on the new Carbon vector libraries. This is described in the Code Fragment Manager release note on your CD. In future, this will be available all the time to everyone.

Okay. Now the next thing I wanted to talk about is CFPlugin. If you're writing plug-ins, perhaps the functionality I just talked about is perfectly fine for you. All you want to do is be able to load code and look up functions by name. Or maybe you already have your own plug-in model that manages the interface between the application and the plug-in yourself. In that case, you may not need to look at CFPlugin at all.

However, if you're looking for a sophisticated API for managing interfaces between an application and its plug-ins, CFPlugin may be what you want to use. And there are a number of Apple projects, which you may have heard about and various other things, that will use CFPlugin for their plug-ins.

I don't really have time to go into the details of CFPlugin right now. I just want to briefly describe what it does. The application will describe the interfaces that it was looking for, and the plug-in itself will advertise

[Transcript missing]

And then the application will then look at the plug-in, interrogate it, see what interfaces it supports. The interfaces are looked up by UUID, which is just a universal identifier.

And the interfaces are tables of function pointers. They are COM compatible if you use COM. In particular, we specify the IUnknown interface. We're not using COM, but they should be compatible in layout. There is an example of this, a detailed example, in the CFBundle and CF plug-in release note. And really, this is something that is best learned by example.

So now what I want to reiterate is, what you take home is bundles are a way to treat all the files associated with an executable as a single object. And CFBundle is the programmatic interface that you will use to deal with them. So I'm going to turn things back over to Becky.

Thanks Doug. Looks simpler than what you're doing today? Good. That's the idea. Okay, so I'm going to go on now to talk about the user preferences services that Core Foundation provides. and it provides it via CFPreferences. The idea was to give you a mechanism for easily storing and retrieving your user preferences without forcing you to define your own file structure.

You give every preference a name, then you assign a value to that name. The value can be any of the property list types we talked about this morning. You can even make it an array or a dictionary with a complex structure underneath. And then you hand it off to us and we handle everything else. And in particular, the data format and the storage is our problem, not yours. You will always be dealing with the CF types.

So the details. These are kind of internal implementation details, but I thought I'd share them with you anyway. We use XML for most of the backing stores. There are a couple exceptions to that, in particular when we have to share across the network. We cache preference values for efficiency so that every time you ask for a preference and every time you write a preference, we don't actually hit the disk. And CFPreferences, like all of Core Foundation, is a thread-safe system. The caveats: CFPreferences is not intended for hundreds upon hundreds of Ks of data.

Doug mentioned that if your application needs to write data, it should not go into the bundle. You can put it in preferences if it's of an appropriate size and nature. On the other hand, if you need to write hundreds of K of data, you have two solutions available to you.

If it's a temporary cache, write it to the temporary folder. If you need a permanent storage mechanism, you can still use find folder to find the preferences folder and write your own data there that way. However, we are using property lists. Property lists are XML and we have all the same serialization problems I talked about this morning. And it's best used for under 100 K of data.

Also, CFPreferences is not intended for app critical data. Well, what do I mean by that? Most people consider their preferences critical. Well, I simply mean that if it turns out that a preference value is not in the preferences file, your app should not crash. Likewise, if someone has gone in there and written a bad value, your app should not crash. We guarantee when we read the file that if the file itself is corrupted, we will fail gracefully and return null. Your job is just to catch that and make sure that you're not going to crash because we couldn't load your preferences.

So the API is very simple. Most clients are only ever going to need these three functions. CFPreferences copy app value will read a preference. CFPreferences write app value will take a preference value from you and write it out. And then CFPreferences app synchronizes what you use to synchronize the cache with the permanent storage.

If you call CFPreferences app synchronize every time you read or write a preference, then you've defeated the cache. The idea here is that you would write several preferences at once and then synchronize the cache, or perhaps even leave it to a lower library to do the synchronization for you.

So let's take a look at the three functions in detail. CFPreferences copy app value takes two arguments. The first one is the key or the name of the preference you wish to retrieve. And the second argument is a string which identifies your application. This is going to cause CFPreferences to go out and look at all the various places that preferences are stored, find a match for the key, and assuming it finds such a match, it will return the value.

The other half is writing, CFPreferences set app value. You supply the key, the new value, and again your application ID. Once you've written, make sure that at some point in the future you call CFPreferences app synchronize or you know that a library is going to do it for you. This is mostly of interest for Cocoa applications. The Cocoa frameworks will synchronize for you.

So what was that application ID in the API? Most of the time you're simply going to pass KCF preferences current application and that says this is a preference for the current application use CFBundles abilities to locate the correct place to store this information. In those cases where you are writing code that needs to write preferences to other locations, the apps are going to be identified by their bundle identifier. We chose the bundle identifier because the bundle ID, as Doug mentioned, is unique. As he showed you, the bundle ID should be set somewhere in the info.p list under the CFBundleIdentifier key and should be of the form com.companyname.appname.

Now, if CFPreferences is asked to write a preference for an application that has not set this key, CFPreferences is going to default back to using the application's name itself. But, you know, as soon as you do that, we run the risk of conflicts, and that's why we're encouraging everyone to set the bundle identifier.

Okay, so there are a handful of convenience functions. A lot of times when you're reading a preference value, you just want a Boolean value, true/false. Other times you just need to store an integer value. So that's what these two functions are intended for, cf_preferences_get_app_Boolean_value and cf_preferences_get_app_integer_value. In both of these cases, it returns the value that it finds, or if it can't find the value or the value is not of the proper type, they default to different values.

cf_preferences_get_app_Boolean_value is going to return false, get_app_integer_value is going to return zero. If you're interested in the difference between a genuine zero value and the preference not being properly set, you use the second is valid argument, and we will tell you whether we found a valid preference or not.

CFPreferences, these functions will go through and check for several different possible formats. So by using CFPreferences get at Boolean value, we will not only check for a true CFBoolean type, but also check for a CFString that matches true or false, for instance, and also check for some number formats.

So where are these preferences coming from? I've mentioned, kind of alluded a couple different times to the fact that an application's preferences could come from several different locations. We call each of these locations a domain, and then we combine all the different domains that are relevant to a particular application into what we call the search list. And when we look for a preference for your application, we're going to just walk the search list in a fixed order until we find what, until we find the one we're looking for or we fall off the bottom of the list.

So what's a preference domain? A preference domain is uniquely identified by three things: the user, the host, and the applications identifier. So I could, for instance, talk about the mail preferences for the user Becky Willrich on the host, this particular host. But you can also simply refer to each of these as either the current or any. So I can look for the mail preferences for the user Becky on the current host. Or search for preferences set by the mail application for the user Becky on any host on a fully networked environment.

So in all cases, current means the user, host, or application relevant to the current process. That's the user who ran the application, the application being run, and the host that the application is run on. Any means that the preference is not specific to a particular application. So for instance, it's very rare to want to write a preference for an application that's specific to just this host. Most of the time those application preferences go to the any host domains.

So just some examples. Preferences for the current user on any host for the currently running application is the most common domain used. This is where 90% of your preferences are going to be stored, maybe 99%. But you can have other combinations. For instance, the current user on any host for any application. I might want to set a global font preference in that domain, for instance. I might also have a preference that pertains to any user on this particular host for any application. So printer preferences might be an example of that.

Once those domains are set, here's where CFPreferences looks. This is the search list. In general, we give preference to current settings over any settings. We consider the current users preferences the most important and we consider application preferences more important than host. So we start by looking at the most specific domain, the current user for the current application for the current host, and move downwards to the most general.

And you can see we're going to search all of the current users defaults before looking at the global user defaults. When we write preferences, by default, we're going to write it to that most common domain I was talking about. This user, this application, but not specific to any particular host.

So once in a while you find yourself in a situation where you're writing four or five different applications that need to share preferences. Perhaps they're sharing a library that defines some common functionality and there's a preference unique to that library. In these cases, you're going to want to use the suite preferences provided by C of Preferences. We consider the applications that are going to share the preferences to form the suite.

To do this, you'll need to simply choose an identifier for your suite. Usually, if you're using a shared library, we recommend that you use the bundles identifier for that purpose. And then all the applications that link against that library or that wish to partake in the suite simply add the suite preferences to their search list. They do this by calling CFPreferences add suite preferences to app.

The first argument is the app ID. That's the application to which the suite preferences will be added. The second argument is the suite ID, which tells us which suite should be loaded. Once this is done, all queries to CFPreferences will search the suite preferences as well. When you need to write suite preferences, you'd use the same API you would to write an application preferences. You simply substitute the suite's ID for the application ID.

Once in a blue moon, you may find yourself needing to access a specific domain without going through the search list. This should be an incredibly rare occurrence. In fact, I don't think any application needs to do this except for an application like preferences which itself manages the full preferences database.

In this case we have the same three kinds of functions. We have a read function, a write function, and a synchronize function. However, instead of specifying the preference location simply by app ID, you're going to have to specify the full triplet of user name, host name, application name, application ID I should say.

Now there's some caveats if you find yourself doing this. The first one is that not all of the domains are implemented. In particular, the defaults for any user, any host are not implemented yet. Likewise, on Mac OS 9, if you have not installed the login window or if you're not configured for multiple users, current user doesn't make any sense.

The intention behind the low-level APIs is to fail gracefully under these conditions and simply return null to you. However, you have to be prepared for that. The other promise we make is that the app functions always work and properly take care of using only those domains that are relevant.

Debugging Preferences. So you're an app developer and something's gone wrong. There is a command line tool on DP4, it's the defaults command, that allows power users to view and modify the preferences database directly. As I said, you run it from the command line. There are a number of different options. I'm not going to go through them now. But if you just go to the command line and execute defaults help, you'll get the full details. It's got a lengthy usage statement there.

and that's all I want to say about preferences. So I'm going to go on now to spend a moment to talk about the XML services provided inside of Core Foundation. There are basically three levels of XML support. The first one we've spent a fair bit of time talking about already. That's property lists. The second one is a handful of simple, quick parsing functions that parse XML to and from XML trees. And finally, there's a fine-tuned parsing API available through CFXML Parser.

So just a reminder, a property list is any tree built from the basic CF types. I've listed them again here. Array, dictionary, data, and string should handle 90% of your data. Date, number, and boolean make up the rest. Property lists have a flattened XML representation, and there's a very easy API to convert back and forth between the rich CF type tree and the flat XML data. If you're interested, CFPropertyList.h for details. There's also documentation on the web.

CFXML tree provides the next level of support. Well, what is a CFXML tree? A CFXML tree is a specialized form of a CF tree where each node, in addition to just the basic tree structure, stores a CFXML node. The CFXML node provides information to you about the XML structure itself.

So are you looking at a tag? Are you looking at a comment? Is this a data string? And what you find yourself doing is to walk the tree structure of the XML file, use the CF tree functions for CF tree get parent, CF tree get child count, and so on. To examine the XML structure of a particular node, use CFXML node. So from the tree, call CFXML tree get node. That'll give you the node. Then the node API can be used to extract the information specific to the XML structure.

The API looks like this, just a couple functions. CFXMLTreeCreateFromData. You pass it the allocator as with any create function. XMLData is the flattened XML file just read from disk or retrieved over the internet or whatever. SourceURL is the URL to use for any relative references inside the XML data. ParseOptions has to do with the different options available on the parser.

And then the version, this last field is very important. The version number tells us which version of the APIs you're working with as well as what version of XML you're working with. We can guarantee binary compatibility for a fixed version number. However, as XML is revved by the W3C and as the standards change, we expect to bump the version numbers and extend the API. live.

The other direction is to go from an XML tree to an XML data. Here the function is a little simpler. You just pass an allocator for where to create the data from and the tree in question. We walk the tree, generate the XML, and hand it back to you.

Alright, so that's all very well and good if you are happy with using the CFXML tree structure to represent the XML. However, since that can represent any XML file, it's fairly generic. Also, since we're parsing the entire file and generating the entire tree in one pass, it can be very memory expensive. You've loaded the entire file into data and then we've duplicated that in constructing the tree. For higher performing and more customized behavior, use CFXML parser.

CFSML parser is callback based. It's much the same as the SACS parsers. There are a number of options available for configuring the parser. That was that parser option field that we looked at in the earlier API. Just a word of warning: in DP4, not all of the options are implemented. The documentation has the full details and is up to date as to what's implemented in DP4 and what is not. And that's as much as I want to say on XML.

I'm going to move on now to talk a little bit about some of the advanced memory tricks you can play with CF. And I'm going to focus on CFString for those purposes. The reason why I chose CFString to focus on in this conversation is, well, for several reasons. First of all, it's the most common CF type. If people are going to only use one thing from Core Foundation, it's probably the String.

Also, it demonstrates many of the different memory strategies that are used throughout the system. So once you know how to use CFString's optimizations, the other APIs should look very straightforward to you. And finally, probably because it is the most common type, CFString supports the most different kinds of memory optimizations.

So, let's start with the simplest things you can do. Move towards immutable whenever possible. Immutable strings use the least amount of memory of any string, because we don't have to worry about the extra overhead necessary to grow the string, right? Likewise, fixed mutable uses less than fully mutable. Secondly, if you're going to have to walk the entire string, fetch multiple characters at once. And you can do that using the Get Characters API and simply pass a buffer of characters.

Similarly, there's a handful of functions that include the word PTR, pointer. The pointer functions ask that you check to see if the CFString has the desired type on hand. In other words, can you return this type to me in O without any memory allocations, without any work done? Now, of course, if the answer is no, we're going to return null, and you should be prepared for that.

So you might well see snippets of code like this: CFString get Pascal string pointer. If we have it, we'll return it. If not, you need to check for null, and if so, you need to allocate a buffer for the Pascal string, and then call the other The other API, CFString get pascal string, passing in your buffer and then we fill it.

A little more complex is to use an inline buffer. This case is useful when you're going to walk the entire length of a string or when you're going to be moving around a lot in a small area and then move forward and look at another small area. In other words, when your access is either sequential or highly localized. It keeps the memory overhead down considerably from either walking character by character.

[Transcript missing]

And it looks like this. Create a CFString inline buffer. That's a structure, a straight up C structure. And you can create it on the stack as long as you know that you don't need to pass the buffer out from its scope. Initialize it with your CFString. So you give it the CFString init inline buffer.

Give it the CFString in question. Give it a pointer to the buffer you allocated. And then pass the range of characters you're going to be interested in looking at. Now when you need to fetch a character, instead of going to the CFString, go to the inline buffer. CFString get character from inline buffer, pass a pointer to the buffer, and the index of the character in question.

Beyond that, the most complex level of memory management is the no copy functions. The no copy functions create CFStrings around your own storage so you are not running into any memory copy overhead or double data overhead. When you do this though, you are transferring ownership of the backing storage to CFString. That means you're going to tell CFString how to free the buffer when you're done. And if you're working with mutable strings, you'll also have to tell CFString how to grow that buffer.

Now, using this trick you can even use character buffers directly on the stack. But if you do that, there are a couple things you need to be very careful of. First of all, obviously if you pass the string outside of the scope of your stack buffer, you're going to have a memory exception, right? Because the buffer's been blown away.

Likewise, if even within the buffer's scope you pass the string down to a function that then retains the string for later use, you're going to have the same problem. You're going to exit the scope of your buffer. Whoever it was who retained the string thinks the string is still good, but it's not.

So it looks like this. CFString create with characters no copy. The first allocator there, KCF allocator default, explains how the string itself should be allocated. Then you pass it a pointer to the characters that you have, my buffer. You pass it the length of the buffer, and then finally you pass the buffer allocator. This allocator is what the string is going to use to manage the memory of my buffer.

When the string is freed, buffer allocator is going to be consulted on how to free the characters. If the buffer needs to grow, buffer allocator is going to be used to do that. So normally you're going to simply pass whatever allocator you use to allocate myBuffer in the first place. On the other hand, if you don't want CF to do any management over the myBuffer memory, pass kcfallocator null.

Okay, so these final slides are mostly a repeat from this morning, where you can go to get help. There's a lot of documentation online. It's available on the DP4 CD at System Developer Documentation Core Foundation. It's available on the web at developer.apple.com/techpubs/corefound ation. Example code in System Developer Examples Core Foundation .

Release notes: System Developer Documentation. Core Foundation.html There's a little additional information for bundle services, however. Inside the release notes directory, you will find several different documents of interest. CFBundle and CFPlugin provide sample code on how you would use CFBundle and CFPlugin to create a plugin model. Info.plist describes the keys that we define inside the bundle's information plist. That's where we configured all the finder information for the app package. And localization.html provides information on how to use the app packaging model to localize your app as simply and effectively as possible.

Here are some of the other talks that you might be interested in. Core Foundation Basics already happened. Hopefully you all had a chance to see that. Carbon Low Level is Friday and you can see some of the other pieces that comprise that core services level. The App Packaging and Document Typing talk is going to go into the details of just exactly what the structure inside the app package is. The Localization talk will give you the details on how to go about localizing your app. And then the Localization Tools talk will tell you about the tools available on the system to help you.

With that, oh yes, one more thing. Who to contact? David Wright is our technology manager. He's the gentleman who introduced me and he'll be coming back up for the Q&A. And we also have an external feedback list, [email protected]. We'll reach all of the developers involved in Core Foundation.