Objective-C is a super set of C language. The entire language is a preprocessor skin added to C language and a powerful runtime system. With this runtime system, one can have full featured object oriented programming interface, functional programming environment, and magical dynamic typing system.
In this post, I’ll go through common tasks you can do with Objective-C typing
system, including querying normal NSObject
types, packing static type with
NSValue
, testing core foundation references, and validating if a pointer is a
valid object pointer.
Objective-C type system
To determine an Objective-C object type is super easy. Just use isKindOfClass
method and it is done.
1 2 3 4 5 6 7 8 |
|
Why do we need this mechanism? One application is implementing key value coding with some known range of types. For example, Core Animation listed these properties are animatable:
- anchorPoint
- backgroundColor
- backgroundFilters
- borderColor
- borderWidth
- bounds
- compositingFilter
- contents
- contentsRect
- cornerRadius
- doubleSided
- filters
- frame
- hidden
- mask
- masksToBounds
- opacity
- position
- shadowColor
- shadowOffset
- shadowOpacity
- shadowRadius
- sublayers
- sublayerTransform
- transform
- zPosition
These properties are categorized in several types includes CGPoint
, CGRect
,
CGFloat
, CGImageRef
, CGColorRef
, and even BOOL
. Each kind of type
require individual implementation to operate its value. Thankfully,
Objective C dynamic type system allows us to pass-in the value with generic type
id
and determine the actual type at runtime. id
is simply a void
pointer. The objective c object itself is a struct which have a isa
pointer
points to actual class which defines its instance variables, methods, and class
inheritances.
Packaging static C types with NSValue
Objective C is a skin language based on C, so it is very often to use C types
like int, float, pointer to struct…etc. However, these static types violate
Objective-C’s dynamic typing idioms. Apple introduced NSValue
as a container
for a single C or Objective-C data item. It can hold any C types such as int,
float, char, pointers, structures, and object ids. It not only wrap the item
into an Objective-C object, but also encode the type information of the original
object.
To create an NSValue
object, you pass it a pointer to the item, along with the
encoded type information generated by @encode()
keyword.
1 2 |
|
@encode()
is a compiler directive which can accepts all types that can be used
as an argument of C sizeof()
operator. @encode()
returns a const char*
string encoding that type. The encoding is specified in
Objective-C runtime type encodings.
To illustrate this, see the following examples:
1 2 3 4 5 6 7 8 |
|
With this encoded type information, it only takes few steps to determine which type it is at runtime:
1 2 3 4 5 6 7 8 |
|
UIKit addition to NSValue
UIKit added a category for NSValue to represent iOS related
geometry-based data. You can use these method instead of encoding CGPoint
,
CGRect
, and else every time.
Bridging with Core Foundation objects
Though NSValue
covers many kind of types, in practice there are still some
types don’t fit this solution for dynamic typing. More specifically,
CGColorRef
, CGImageRef
and other Core Foundation types that can be treated
as Objective-C object through toll-free briding are the types we
don’t pack with NSValue
.
A core foundation references is also a void pointer as same as id
is.
To find out the type of an unknown CFTypeRef
, you can query it with C function
CFGetTypeID
.
1 2 3 4 5 |
|
A CFTypeRef marked as id
type can also accept basic objective C messages like
isKindOfType:
. Hence testing an id
typed object is quite safe as long as it
is either a NSObject
, NSValue
, CFTypeRef
, CGColorRef
or any other
Objective-C object/Core Foundation reference.
Testing if a pointer is a valid NSObject
There is a blog post on Cocoa with love about how to test if an arbitary pointer is a valid NSObject. In my point of view, programmer should pass in a valid object for sure. If it is not a valid object, just let it crash.
Puting it all together
This piece of code is part of my project FCAnimationFactory for the purpose of interpolating different kinds of value with respect to their types.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
|
Conclusion
It is amazing that a langauge so close to C can create such a rich type system without byte code, VM, or complex sybol tricks (like what C++ does). Though handling differnt types can be a bit painful sometimes, but it brings powerful polimorphsm to the language. Thus programmer can create highly abstract API and framework with differnt data types that share the same methods.