Determining what a CFTypeRef is?
I have a function which returns CFTypeRef开发者_如何学运维. I have no idea what it really is. How do I determine that? For example it might be a CFStringRef.
CFGetTypeID()
:
if (CFGetTypeID(myObjectRef) == CFStringGetTypeID()) {
//i haz a string
}
The short answer is that you can (see Dave DeLongs answer). The long answer is that you can't. Both are true. A better question might be "Why do you need to know?" In my opinion, if you can arrange things so that you don't need to know, you're probably going to be better off.
I'm not saying that you can't do it, or even that you shouldn't. What I am saying is that there are some hidden gotchas when you start down this path, and some times you're not really aware of what all the unstated assumptions are. Unfortunately, programming correctly depends on knowing all the little details. Off the top of my head, here's a few of the potential gotchas:
To the best of my knowledge the set of Core Foundation types has increased in each major OS release. Therefore each major OS release has a superset Core Foundation types of the previous releases, and likely a strict superset at that. This is "observed behavior", and not necessarily "guaranteed" behavior. The important thing to note is that "things can and do change", and all things being equal, the easier and simpler solutions tend not to take this in to account. It is generally considered poor programming style to code something that breaks in the future, regardless of the reason or justification.
Because of Toll-Free Bridging between Core Foundation and Foundation, just because a
CFTypeRef = CFStringRef
does not mean that aCFTypeRef ≡ CFStringRef
, where=
means "equal to" and≡
means "identical to". There is a distinction, which may or may not be important depending on context. As a warning, this tends to be where the bugs roam freely.For example, a
CFMutableStringRef
can be used where ever aCFStringRef
can be used, orCFStringRef = CFMutableStringRef
. However, you can not use aCFStringRef
everywhere aCFMutableStringRef
can be used for obvious reasons. This meansCFStringRef ≢ CFMutableStringRef
. Again, depending on the context, they can be equal, but they are not identical.It is very important to note that while there is a
CFStringGetTypeID()
, there is no correspondingCFMutableStringGetTypeID()
.Logically,
CFMutableStringRef
is a strict superset ofCFStringRef
. It would follow, then, that passing a bona fide immutableCFStringRef
to aCFMutableString
API call would cause "some kind of problem". While this may not be true now (i.e., 10.6), I know for a fact that the following was true in the past: TheCFMutableString
API calls did not verify that "the string argument" was actually mutable (this was actually true for all types that made a distinction between immutable and mutable). The checks were there, but they were in the form of debug assertions that were disabled on "Release" builds (in other words, the checks were never performed in practice).This is (or possibly was) officially not considered to be a bug, and the (trivial) mutability checks were not done "for performance reasons". No "public" API is provided to tell the mutability of a
CFString
pointer (or mutability of any type). Combined with Toll-Free bridging, this meant that you could mutate immutableNSString
objects, even though theNSMutableString
APIs did perform a mutability check and caused "some kind of problem" when trying to mutate an immutable object. Flavor with the fact that@""
constant strings in your source are mapped to read-only memory at run time.The official line, as I recall, was "not to pass immutable objects, either CFStringRef or NSString, to CFMutableString API's, and further more, it was a bug to do so". When it was pointed out that there might be some security related issues with this stance (never mind the fact that it was fundamentally impossible), say if anything ever made the mistake of critically depending on the immutability of a string, especially "well known" strings, the answer was "the problem is theoretical and nothing will be done at this time until a workable exploit can be demonstrated."
Update: I was curious to see what the current behavior is. On my machine, running 10.6.4, using
CFMutableString
API's on an immutableCFString
causes the immutable string to become essentially@""
, which is at least better than what it did before (<= 10.5) and actually mutate the string. Definitely not the ideal solution, has that bitter real world taste to it where its only redeeming quality is that it is "the least worst solution".
So remember, be careful in your assumptions! You can do it, but if you do, it's more important that you not do it wrong. :) Of course, a lot of "wrong" solutions will work, so the fact that things are working is not necessarily proof that you're doing it right. Good times!
Also, in a Duck Typed system it is often considered bad form, and possibly even a bug, to "look too closely at the type of an object". Objective-C is definitely a Duck Typed system and this unquestionably bleeds over in to Core Foundation due to the tight coupling of Toll-Free bridging. CFTypeRef
is a direct manifestation of this Duck Type ambiguity, and depending heavily on the context, may be an explicit way of saying "You are not supposed to be looking too closely at the types".
If you want to find out what type a CFTypeRef
is during development, you can use the following snippet.
printf("CFTypeRef type is: %s\n",CFStringGetCStringPtr(CFCopyTypeIDDescription(CFGetTypeID(myObjectRef)),kCFStringEncodingUTF8));
This will print a human readable name for the type so you know what it is. But Apple makes no guarantees that they'll keep these descriptions consistant so don't use this in production code. (As is the snippet will leak memory but you should only use it during development anyway so who cares).
精彩评论