CF-226 - 10.1 “Puma”#
The following came from the ADC release notes page.
CFBundleGetFunctionPointerForName from CFM Apps#
Due to a bug in Mac OS 10.0.x, in some cases CFBundleGetFunctionPointerForName, when called on a bundle corresponding to a Mach-o framework, would return a non-NULL result for a symbol defined in a framework other than the one specified. Some CFM apps incorrectly relied on this behavior. For backward compatibility, when CFBundleGetFunctionPointerForName is called on a Mach-o framework bundle from a CFM executable, it will continue to return function pointers for symbols defined in other frameworks. The same applies to CFBundleGetFunctionPointersForNames. Calls to either function from Mach-o executables will now return only those symbols actually defined in the specified framework.
Note that there is a significant performance penalty whenever CFBundleGetFunctionPointerForName must look up a symbol defined in a framework other than the one specified. We strongly recommend that CFBundleGetFunctionPointerForName be called with a bundle argument corresponding to the framework in which the desired symbol is actually defined. If the desired symbol is defined in one of the subframeworks of an umbrella framework, we recommend that CFBundleGetFunctionPointerForName be called with a bundle argument corresponding to the umbrella framework.
If a CFM executable wishes to use a version of CFBundleGetFunctionPointerForName without the backward compatibility behavior described above, there is a simple workaround. Using CFBundleGetFunctionPointerForName on a bundle corresponding to the CoreFoundation framework, look up CFBundleGetFunctionPointerForName itself. The resulting function pointer can be used to call the version of the function that would ordinarily be used by Mach-o callers.
None of the above applies to the case in which the bundle argument refers to a bundle whose executable is a Mach-o loadable bundle or a CFM executable. In these cases CFBundleGetFunctionPointerForName has always returned, and will continue to return, only those symbols actually defined in the specified bundle.
CFBundleDevelopmentRegion for Unbundled Applications#
Some unbundled CFM applications may specify their development localization using both a traditional ‘vers’ resource and a CFBundleDevelopmentRegion key in a ‘plst’ resource. In this case the two resources should specify the same localization. If they do not, the behavior of the application may be undefined or inconsistent in certain situations. In most cases, the CFBundleDevelopmentRegion will take precedence, but for backward compatibility with Mac OS 10.0.x there are some cases in which the region specified in the ‘vers’ resource will take precedence. In addition, there may be existing tools developed prior to the advent of CFBundle, that understand only the ‘vers’ resource, and so would not necessarily give correct results if the CFBundleDevelopmentRegion and the ‘vers’ resource disagreed.
Some unbundled applications may be able, through mechanisms of their own, to support multiple localizations. In this case, it is possible to specify to CFBundle what localizations the application will support, by supplying an array of localization names for the key “CFBundleLocalizations” in the ‘plst’ resource. This is not a recommended solution; it should be used only under exceptional circumstances in which it is not possible to make use of the normal CFBundle localization mechanisms, namely bundling the application and supplying .lproj folders.
Generating UUIDs#
There is now a command-line tool, /usr/bin/uuidgen, for generating UUIDs.
New Standard Headers Included#
CFBase.h now includes <stdint.h> and <stdbool.h>.
Standard Headers No Longer Included#
CoreFoundation.h no longer includes <assert.h>.
Missing CFM Exports#
Some CarbonLib 1.3 functions were missing from the CFM stub library in Mac OS X 10.0. You cannot use these from a CFM app brought to Mac OS X.
CFBundleCopyLocalizationForLocalizationInfo
CFBundleGetDataPointerForName
CFBundleGetDataPointersForNames
CFBundleGetLocalizationInfoForLocalization
CFURLCreateStringByAddingPercentEscapes
CFXMLParserGetTypeID
kCFPropertyListImmutable Ignored#
CFPropertyListCreateFromXMLData() ignores the kCFPropertyListImmutable flag, and gives you the effect of kCFPropertyListMutableContainers in 10.0 and 10.1. In the next release, we intend to change this so that this flag will NOT be ignored, so you should not depend on this behavior. You can test for this by setting the environment variable “CFPropertyListAllowImmutableCollections” (to any value) in your (Terminal) shell’s environment, exporting the variable if the semantics of your shell require that, and running your application from the shell.
CFStringGetNameOfEncoding()#
Despite what the documentation has described in earlier releases, the function CFStringGetNameOfEncoding() does not and will not localize the string encoding names that are returned. We will look into providing additional CoreFoundation API for localized encoding names; but in the meantime, Cocoa’s Foundation framework offers the NSString method +localizedNameOfStringEncoding:.
Crasher in CFTimeZone Fixed#
A potential crasher bug related to getting the abbreviation from a time zone object that doesn’t have any in has been fixed.
Equality Tests for Some Run Loop Types Changed#
Equality testing of CFRunLoopSourceRefs now includes the criterion that the info pointers of the two objects must be the same.
Equality comparison of CFRunLoopObserverRefs and CFRunLoopTimerRefs has been changed to report true only when the two compared objects are the same object (reference equality). The previous comparison algorithms were broken in that they did not satisfy the hash-equals invariant, and when we went to fix this we realized that it doesn’t have a lot of meaning/value for two distinct observer or timer objects to be equal. So the problem was fixed by changing these types to use reference equality.
CFTimeZoneCreateWithName() with the Default Allocator Parameter#
CFTimeZoneCreateWithName() no longer complains and aborts when running against the debug CoreFoundation library and given NULL or the kCFAllocatorDefault as the allocator parameter.
Global variable added#
A declaration for the global variable kCFCoreFoundationVersionNumber has been added to CFBase.h. This variable existed with the same type in Mac OS X 10.0, so no special handling should be required to use it.
There are also two symbolic constants to declare the version numbers of shipping CoreFoundation frameworks, one for 10.0, other for the CoreFoundation shipping with Tier 2 language package version:
#define kCFCoreFoundationVersionNumber10_0 196.4
#define kCFCoreFoundationVersionNumber10_0_3 196.5
In general you should compare against a version number which you know fixes the problem you are checking for, rather than the exact version of the last external release. As you can see above, since 10.0 there was a very minor update to CoreFoundation with a bump in the version number.
Notification Center#
In 10.0.x, if you register to receive all notifications sent to a particular object (that is, with name parameter nil for global matching), you will not be able to remove that registration unless the remove operation also uses name == nil. This has been fixed.
CFCharacterSet#
CFCharacterSet now internally supports UCS-4 non-BMP ranges that is accessible using Unicode surrogate pairs in UTF-16. That is, the supported Unicode range is from 0x00000 to 0x1FFFFF. We’re not publicizing any new API to query the membership of characters outside of the currently supported 16-bit range (planned for Jaguar), but this internal surrogate support results in some semantic changes in the existing API. The functions that take CFRange arguments interprets the range in 32-bit. For example, the CFCharacterSet created with CFCharacterSetCreateWithCharactersInRange(NULL, CFRangeMake(0x20000, 0xA6D7)) covers the new Unicode 3.1 CJK Ideograph Extension-B. The format of the bitmap representation used by CFCharacterSetCreateWithBitmapRepresentation and CFCharacterSetCreateBitmapRepresentation is also extended to support non-BMP ranges. The format of the first 8K bytes is not changed from the previous release, it represents the characters in BMP range. It now can be followed by 0 to 31 addition 8K bitmaps. Each 8K bitmap is preceded by a plane index byte. For example, the bitmap representation for Plane 2 (0x20000 ~ 0x2FFFF) is the plane index byte 2 followed by the 8K bitmap.
CFReadStream and CFWriteStream#
Two new CFTypes have been added to CoreFoundation, CFReadStream and CFWriteStream, which allow clients to use data providers (CFReadStreams) or data consumers (CFWriteStreams) without loading the entire data into memory at once. Both types are non-seekable, one-way streams; once the data has been provided or consumed, it cannot be retrieved from the streams. CFRead/WriteStreams can be created from any of a number of different sources including files, sockets, or memory, and can either be used directly (blocking) or be scheduled on a run loop (non-blocking via client callback).
In general, you use CFRead/WriteStreams in much the same way as UNIX file descriptors; you first instantiate the stream from any of a number of possible sources, then you set any special configuration options you require. Next, you open the stream, read or write any number of times, then ultimately close and dispose of the stream. At any point during the stream’s lifetime, you can ask it for information about its properties, where a property is any interesting information about the stream or its source or destination that is not part of the actual data being transferred. If you are using the stream without blocking, you would set the stream’s client and schedule the stream with a CFRunLoop (note that it is your responsibility to make sure the run loop is being run), probably before opening the stream.
Using a stream (blocking):
To use a stream in a blocking fashion, first create the stream via one of the CFReadStreamCreate or CFWriteStreamCreate calls:
CFReadStreamRef myStream = CFReadStreamCreateWithFile(NULL, sourceFileURL);
You may then want to do some extra configuration on the stream; if the stream is on a socket, for instance, you may want to set the size of the socket’s buffers. There’s no configuration available on file streams; see CFHTTPStream.h in the CoreServices framework for an example of a configurable stream.
After instantiating the stream, you open it; this is when the stream reserves any system resources required, like the file descriptor needed to read from a file. CFReadStreamOpen and CFWriteStreamOpen return FALSE if the open fails for any reason; they will return TRUE either if the open fully succeeded or if opening the stream is being done asynchronously (for instance, if completing the open requires a response from a remote server). In the blocking case, the difference between these possibilities does not matter - when you attempt to read to or write from the stream, the stream will block until the open has fully completed. If you wish to know whether the open has fully completed, you can retrieve the stream’s current status with CFReadStreamGetStatus or CFWriteStreamGetStatus; the returned value will be kCFStreamStatusOpening if the open is still on-going, kCFStreamStatusOpen if the stream has successfully opened, or kCFStreamStatusError if the open has failed.
if (!CFReadStreamOpen(myStream)) {
CFStreamError error = CFReadStreamGetError(myStream);
reportError(error);
}
Once the stream is open, you can begin reading and writing bytes. CFReadStreamRead() and CFWriteStreamWrite() parallel the UNIX read() and write() functions - they take a byte buffer and length, and return the number of bytes read/written, 0 if at end-of-stream, or -1 if an error has occurred. (Note: there is a known bug in MacOS X 10.1 such that occasionally CFReadStreamRead will return 0 although the stream is not at its end; you can work around by checking the stream’s status after receiving the 0 return. The status code will be kCFStreamStatusAtEnd if the stream has truly been exhausted.) They also both block until at least one byte can be produced or consumed, at which point as many bytes as can be produced or consumed without blocking are handled (this matches the default behavior of UNIX read(), but differs from that of UNIX write(), which blocks until the entire user-provided buffer is consumed).
while (!done) {
UInt8 buffer[BUFSIZE];
CFIndex bytesRead = CFReadStreamRead(myStream, buffer, BUFSIZE);
if (bytesRead < 0) {
CFStreamError error = CFReadStreamGetError(myStream);
reportError(error);
done = TRUE;
} else if (bytesRead == 0) {
// work around bug in MacOS X 10.1
if (CFReadStreamGetStatus(myStream) == kCFStreamStatusAtEnd) {
done = TRUE;
}
} else {
handleBytes(buffer, bytesRead);
}
}
Finally, close (which releases all system resources) and release the stream.
CFReadStreamClose(myStream);
CFRelease(myStream);
myStream = NULL;
Using a stream (non-blocking, event-based):
To use a stream without blocking, you use exactly the same calls as in the blocking case. However, you will also set a client on the stream, then schedule the stream on a run loop; your client will be informed when key events during the stream’s lifetime occur, so you will know when calls like CFReadStreamRead() can be made without blocking. Note: not all streams support event-driven usage; you must check the return value from CFRead/WriteStreamSetClient() to determine whether the stream provides the necessary support. At this writing, only socket streams (and the streams derived from them, like HTTP streams) provide support for event-driven usage.
Instantiate your stream as above. However, before opening the stream, set a client on the stream, and install it on at least one run loop. Your client will be sent its callback as the stream’s state changes.
CFStreamClientContext myContext = {0, myPtr, myRetain, myRelease, myCopyDesc};
if (!CFReadStreamSetClient(myStream,
kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered,
myCallBack, &myContext) {
// stream does not support event-driven model; fallback to blocking model or fail
....
} else {
CFReadStreamScheduleWithRunLoop(myStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
}
Once the client has been set and the stream has been scheduled on a run loop, you open the stream and wait to receive your callback. In your callback, you can examine what event has occurred and react accordingly.
void myCallBack(CFReadStreamRef stream, CFStreamEventType event, void *myPtr) {
switch(event) {
case kCFStreamEventHasBytesAvailable: {
// it is safe to call CFReadStreamRead; it won't block
UInt8 buffer[BUF_SIZE];
CFIndex bytesRead = CFReadStreamRead(stream, buffer, BUF_SIZE);
if (bytesRead > 0) {
handleBytes(buffer, bytesRead);
}
// it is safe to ignore bytesRead <= 0 because these cases will generate other events
break;
}
case kCFStreamEventErrorOccurred: {
CFStreamError error = CFReadStreamGetError(stream);
reportError(error);
CFReadStreamClose(stream);
CFRelease(stream);
break;
}
case kCFStreamEventEndEncountered:
reportCompletion(...);
CFReadStreamClose(stream);
CFRelease(stream);
break;
}
}