ko(9)Articles on (mostly) iOS development, by Nick Randallhttps://ko9.org/enSun, 5 Apr 2026 14:53:53 +0200Sun, 5 Apr 2026 14:53:53 +0200250https://ko9.org/posts/swift-runtime-3-availability/Runtime in Swift: Exposing Unavailable ClassesSome lessons learned while building a Swift API for the ObjC runtime functions, part 3.https://ko9.org/posts/swift-runtime-3-availability/Tue, 17 Sep 2024 12:27:00 +0200This is the last post in a series about building a Swift wrapper of the Objective-C runtime. It’s about how I created Swift APIs for two runtime-related classes which are unavailable in Swift.

  1. The lifetime of strings passed into C functions
  2. Getting my Swift wrapper to be as fast as calling into C directly
  3. Exposing some runtime-related classes that are marked as unavailable in Swift (this post)

When I had almost finished wrapping all the runtime functions in a Swift API, I realised there were two classes I also needed to support: NSInvocation and NSMethodSignature. While these are not strictly part of the runtime C functions, they are closely related; Apple even categorises them under “Object Runtime” in their documentation.

What do these classes do?

NSInvocation is an object that captures all the details of a method call. If you think about what any method call involves—a target object, a selector, parameter values and a return value—those are literally all properties on an NSInvocation. It’s like a snapshot of any method call, except all those properties can be modified. The parameter values can be changed before you “invoke” the method call and the return value can be changed afterwards. The method can be called on a different target object or even invoked multiple times. It is mainly used with the forwardInvocation: method on NSObject, but you can also create one from scratch and invoke it manually.

NSMethodSignature is a closely-related class which encapsulates all the parameter types and return type of a method. It is created from a type encoding string and essentially wraps all the method-related runtime functions like method_getArgumentType and method_getNumberOfArguments.

Both of these classes are very useful for working with the runtime, which is why I wanted to add them to my Swift runtime wrapper.

Creating the Swift API

I started writing a Swift extension to NSInvocation thinking “this shouldn’t take long”.

⛔️ ’NSInvocation’ is unavailable in Swift: NSInvocation and related APIs not available

Oh. Damn.

The exact classes I needed turned out to be the only ones that are just flat-out not available in Swift with no suggested replacement. I don’t see any particular reason they’re blocked apart from “you probably don’t need to deal with this stuff in Swift”.

Well that’s not going to stop me. I guess I’m going to have to call these runtime classes dynamically using... the runtime.

Sending messages

Here’s how [NSInvocation invocationWithMethodSignature:] can be called without referring to the class or initialiser directly:

func createNSInvocation(with methodSignature: Any) -> Any? {
    let cls: AnyObject = NSClassFromString("NSInvocation")!
    let sel = NSSelectorFromString("invocationWithMethodSignature:")
    return cls.perform(sel, with: methodSignature).takeUnretainedValue()
}

This uses the “perform selector” technique to send a message to the class object. Note that while NSClassFromString returns AnyClass, there are no functions on the type itself so it has to be cast to AnyObject before calling perform(). The method signature parameter and returned object also both need to be of Any since the real types can’t be used in Swift. It’s ugly but it works. So now let’s create the needed method signature with [NSMethodSignature signatureWithObjCTypes:] which takes a type encoding C string:

func createNSMethodSignature(with types: String) -> Any? {
    let cls: AnyObject = NSClassFromString("NSMethodSignature")!
    let sel = NSSelectorFromString("signatureWithObjCTypes:")
    return cls.perform(sel, with: types.utf8CString).takeUnretainedValue()
}

Except this time the perform() call crashes with a curious message:

Thread 1: “NSGetSizeAndAlignment(): unsupported type encoding spec ’±’ at ’±Ó.]¯ˇU0000001dU00000001HB/]¯U0000007f’ in ’±Ó.]¯ˇU0000001dU00000001HB/]¯U0000007f’”

This is clearly a problem with the type encoding parameter. Maybe types.utf8CString is not correctly being mapped to the const char * that the method expects?

Actually the real issue is that perform() only accepts object parameters, so it can’t even be used with methods that take scalars or pointers. Ironically, the documentation says that if you need to pass anything else, use NSInvocation:

This method is the same as perform(_:) except that you can supply an argument for aSelector. aSelector should identify a method that takes a single argument of type id. For methods with other argument types and return values, use NSInvocation.

It appears that it’s impossible to even create an instance of NSMethodSignature using this technique then. Time to go deeper.

Calling method implementations

Instead of sending a message to the NSMethodSignature class, it’s possible to ask the runtime for the actual function pointer to that method and call that manually. After getting the class and selector as before, the function pointer (IMP) is determined like so:

func createNSMethodSignature(with types: String) -> Any? {
    let cls: AnyClass = NSClassFromString("NSMethodSignature")!
    let sel = NSSelectorFromString("signatureWithObjCTypes:")
    let method = class_getClassMethod(cls, sel)!
    let imp = method_getImplementation(method)

But then how is this IMP called in Swift? It needs to be cast from an untyped pointer to match the expected signature of the signatureWithObjCTypes: method. As with all Objective-C methods, this is a function which takes the object that the message is being sent to (the NSMethodSignature class), the selector and any other parameters. In Swift this looks like:

    typealias FuncSig =
        @convention(c) (AnyClass, Selector, UnsafePointer<CChar>) -> AnyObject?

The @convention(c) indicates that this is a C-style function signature instead of a Swift function.

The IMP can now be cast to this function type and called like any other closure:

    let fn = unsafeBitCast(imp, to: FuncSig.self)
    let obj = types.withCString {
        fn(cls, sel, $0)
    }
    return obj
}

So finally this returns a valid NSMethodSignature, which can be used to create an NSInvocation.

Exposing the full API

Getting an NSInvocation as an Any is not particularly useful, since I want to use all the methods of the original class. I decided to wrap the original object in a new Swift struct:

struct Invocation {
    let nsInvocation: AnyObject
}

Now how to expose all the original methods and properties on the NSInvocation? Calling perform() will not work unless all the arguments and return type are objects, but the method IMP technique will work for any method. For example here’s argumentsRetained which is a simple read-only boolean property:

extension Invocation {
    var argumentsRetained: Bool {
        let cls: AnyClass = NSClassFromString("NSInvocation")!
        let sel = NSSelectorFromString("argumentsRetained")
        let imp = class_getMethodImplementation(cls, sel)!

        typealias Sig = @convention(c) (AnyObject, Selector) -> ObjCBool
        let fn = unsafeBitCast(imp, to: Sig.self)
        let result = fn(nsInvocation, sel)
        return result.boolValue
    }
}

Now I still need to do that for every other method? What a pain!

There must be a way to tell Swift “these methods exist on this untyped object, trust me, let me call them!”. That sounds a lot like what a protocol is for.

Calling via a protocol

If I take the public header of NSMethodSignature, generate a Swift API from it and wrap that in a protocol, this is what I get:

@objc protocol MethodSignatureProto: NSObjectProtocol {
    static func signature(withObjCTypes types: UnsafePointer<CChar>!) -> MethodSignatureProto?
    var numberOfArguments: UInt { get }
    func getArgumentType(at idx: UInt) -> UnsafePointer<CChar>!
    var frameLength: UInt { get }
    var isOneway: Bool { get }
    var methodReturnType: UnsafePointer<CChar>! { get }
    var methodReturnLength: UInt { get }
}

I’m inheriting from the NSObjectProtocol so that I also get basic object functionality like isEqual:.

So now all that’s needed is to cast the Any returned from createNSMethodSignature() to MethodSignatureProto and I can start calling methods on it. Swift will now even take care of bridging those UnsafePointer<CChar> parameters to String for me.

let methodSig = createNSMethodSignature(with: "v@:") as! MethodSignatureProto

Except oops, that crashes:

Could not cast value of type ’NSMethodSignature’ to ’MethodSignatureProto’.

How can I tell Swift that NSMethodSignature does conform to MethodSignatureProto? This was so much easier in Objective-C!

For someone who has spent a lot of time working with the runtime, it took longer than I’d like to admit to come up with the solution:

let cls: AnyClass = NSClassFromString("NSMethodSignature")!
class_addProtocol(cls, MethodSignatureProto.self)

I just needed to tell the runtime that NSMethodSignature conforms to MethodSignatureProto. Because of course the as operator is using that to test for protocol conformance. Now the cast works and I can call any of the underlying methods directly:

let methodSig = createNSMethodSignature(with: "v@:") as! MethodSignatureProto

print(methodSig.frameLength)    // 224
print(methodSig.isOneway)       // false
print(String(cString: methodSig.methodReturnType))  // "v"
print(methodSig.getArgumentType(at: 0))     // crash!

The last one crashes with:

-[NSMethodSignature getArgumentTypeAt:]: unrecognized selector

I thought it was going to be some new drama but it turned out to be simple. The method name in Objective-C is actually getArgumentTypeAtIndex: but that doesn’t round-trip through the generated Swift name properly. It’s easily fixed by overriding the ObjC name in the protocol:

@objc(getArgumentTypeAtIndex:)
func getArgumentType(at idx: UInt) -> UnsafePointer<CChar>!

This protocol technique can also be used to call static methods, so createNSMethodSignature() can be replaced by:

let cls: AnyClass = NSClassFromString("NSMethodSignature")!
let typedCls = cls as! MethodSignatureProto.Type
let methodSig = typedCls.signature(withObjCTypes: "v@:")

Using protocols like this made it much simpler to expose the unavailable NSInvocation and NSMethodSignature classes to Swift.

Calling directly on AnyObject

Something I’d forgotten until writing this post is that Swift actually allows any Objective-C method to be called directly on AnyObject!

let obj: AnyObject = createNSMethodSignature(with: "v@:")
print(obj.frameLength)    // 224
print(obj.isOneway)       // false
print(String(cString: obj.methodReturnType))    // "v"
print(obj.getArgumentType(at: 0))               // "@"

There’s no protocol involved here, Swift just exposes every ObjC selector it knows about on AnyObject. Even if those selectors are defined on a class that’s unavailable in Swift apparently.

It works surprisingly well, except in a couple of cases like NSInvocation.target where Swift complains about “ambiguous use of ’target’”. That means multiple ObjC classes have a property called “target” but with differing types, so it doesn’t know which one to use. It also doesn’t seem possible to call a class method using this trick.


There are a few different options for exposing an unavailable class to Swift and luckily the best option (using protocols) turned out to not involve a ton of code. It’s hardly something that comes up often, but it’s nice that yet again the runtime lets us monkey around with the wiring inside our apps.

]]>
https://ko9.org/posts/swift-runtime-2-performance/Runtime in Swift: Iterator PerformanceSome lessons learned while building a Swift API for the ObjC runtime functions, part 2.https://ko9.org/posts/swift-runtime-2-performance/Thu, 12 Sep 2024 14:42:00 +0200In this post I’ll describe how I made my Swift wrapper of the Objective-C runtime as fast as calling the C functions directly. This is the second part in a series:

  1. The lifetime of strings passed into C functions
  2. Getting my Swift wrapper to be as fast as calling into C directly (this post)
  3. Exposing some runtime-related classes that are marked as unavailable in Swift

My main goal was to make a nice Swift API for the runtime and I wasn’t too concerned if it added a little performance overhead. The test scenario I used was to iterate over every method of every class, which looks like this when using the runtime functions directly:

var classCount: UInt32 = 0
let classes = UnsafeMutablePointer(mutating: objc_copyClassList(&classCount)!)
for j in 0..<Int(classCount) {
    var methodCount: UInt32 = 0
    if let methods = class_copyMethodList(classes[j], &methodCount) {
        for k in 0..<Int(methodCount) {
            let method = methods[k]
            // ...
        }
        free(methods)
    }
}
free(classes)

Note that accessing the pointer returned from objc_copyClassList directly causes a crash, possibly because the Swift mistakenly bridges the return type to AutoreleasingUnsafeMutablePointer. Wrapping it in one of the other unsafe pointer types fixes it.

The equivalent using my Swift runtime API looks like this:

for cls in ObjCClass.all {
    for method in cls.methods {
        // ...
    }
}

I think you can agree that looks considerably nicer! Too bad it’s also considerably slower. When I ran a quick comparison using XCTestCase.measure I found that my wrapper was three times slower than using the runtime directly.

Performance benchmarking

Time to dig into some performance debugging. I had my suspicions about what the main cause of the slowdown was (heap allocations) but needed some better tools for benchmarking. Luckily I remembered there was a new official Benchmark package announced recently on Swift.org that supports loads of useful metrics around memory usage and allocations, throughput and even CPU instruction counts. It took a few minutes to set up but is definitely worth it for investigating performance issues like this.

Here’s an extract of the benchmark results, where “Direct calls” is the baseline of calling the runtime functions directly, and “Wrapper arrays” is using my Swift API:

Metric (p90)Direct callsWrapper arrays
Instructions (K)16608716
Malloc (large)11
Malloc (small)20489086
Memory Δ (resident peak) (K)389393
Object allocs07037
Releases189380
Retains1819
Throughput (# / s) (#)34871022
Time (total CPU) (μs)297987
Throughput as % of baseline100%29%

Clearly my wrapper is doing a lot more work allocating objects than it needs to, which would be the array created when cls.methods is called. Even though Array in Swift is a struct, it still needs to allocate memory for its backing storage.

In this case there were 2040 classes registered for a total of 32064 methods but depending on which frameworks are linked this can easily be 10x higher. How can I avoid creating an array for every one of those 2000 classes?

One option would be to use a block-based API which executes a closure for each method:

extension ObjCClass {
    func forEachMethod(block: (ObjCMethod) -> Void) {
        var methodCount: UInt32 = 0
        if let methods = class_copyMethodList(cls, &methodCount) {
            for m in 0..<Int(methodCount) {
                block(ObjCMethod(methods[m]))
            }
            free(methods)
        }
    }
}

// Called like:

cls.forEachMethod { method in
    // ...
}

This runs at ~90% the speed of calling the C functions directly which is not bad. Adding @inlinable to the function brought that up to the same performance as the baseline which is even better. Inlinable exports the whole function body in the public interface (instead of just the function signature) so that optimisations can be done across module boundaries. I assume that when the Swift compiler can see inside the function it optimises away the closure entirely.

So problem solved right?

Well...

I really wanted to keep that “for x in y” syntax, it just looks more natural to me (plus I just can’t resist a challenge). There’s only one way to do that without using arrays.

Iterators

The for-in syntax in Swift is available for anything that conforms to the Sequence protocol. A sequence has one requirement, and that is to produce an iterator. The IteratorProtocol also has a single requirement: to implement a next() function which returns the next item in the sequence, or nil at the end.

This means that if I create a custom sequence that iterates over the methods of a class then I can return that instead of an array, thereby avoiding an object allocation but keeping the same syntax at the call site. Here’s what that might look like:

struct MethodList: Sequence {
    let cls: AnyClass

    func makeIterator() -> MethodIterator {
        MethodIterator(cls: cls)
    }
}

class MethodIterator: IteratorProtocol {
    let methods: UnsafeBufferPointer<Method>
    var index = 0

    init(cls: AnyClass) {
        var methodCount: UInt32 = 0
        let methods = class_copyMethodList(cls, &methodCount)
        self.methods = UnsafeBufferPointer(start: methods, count: Int(methodCount))
    }

    deinit {
        free(UnsafeMutableRawPointer(mutating: methods.baseAddress))
    }

    func next() -> ObjCMethod? {
        guard index < methods.count else { return nil }
        defer { index += 1 }
        return ObjCMethod(methods[index])
    }
}

Returning a MethodList sequence instead of an array works as expected, but the performance is terrible! It runs at around 20% the speed of the baseline and somehow needs over eight times the number of CPU instructions. Let’s add it to the results table:

Metric (p90)Direct callsWrapper arraysIterator class
Instructions (K)1660871614000+
Malloc (large)111
Malloc (small)204890864372
Memory Δ (resident peak) (K)389393397
Object allocs070372324
Releases1893802342
Retains181918
Throughput (# / s) (#)34871022747
Time (total CPU) (μs)2979871348
Throughput as % of baseline100%29%21%

You might have already spotted the problem, I’ve just replaced a bunch of Array allocations with MethodIterator objects instead. But there was a good reason for making this a class instead of a struct, because I need to free the list pointer in deinit. Iterators don’t have a concept of “finishing” iteration, so there’s no way to know when it’s safe to free the pointer except when the iterator itself is deallocated. Maybe I could do it when next() returns nil, but what happens if I break out of the for loop before getting to the end?

I thought about using a non-copyable struct which is a special value type that has a lifetime like a class and which does support deinit. Unfortunately noncopyable structs can’t conform to protocols like IteratorProtocol so they can’t help here.

This might be fixed in Swift 6 although I haven’t checked yet if iterators will be marked as ~Copyable.

Stack-allocated classes

I read somewhere on the Swift forums that in certain cases the compiler can mark classes as eligible for stack promotion. This means that if it knows the layout of a class and can prove that it doesn’t escape its scope, the class can be created on the stack instead of the heap. I couldn’t find more details on this, but I assumed this could only work if the class was not internal to a module and hence hidden from the optimiser.

After trying a few things – benchmarking is really handy for this – I found that making everything on my custom sequence and iterator fully public and @inlinable did the trick! Suddenly I was seeing zero object allocations again and performance that was very close to the baseline, only around 1-10% slower:

Metric (p90)Direct callsWrapper arraysIterator classInlined iterator
Instructions (K)1660871614000+2087
Malloc (large)1111
Malloc (small)2048908643722048
Memory Δ (resident peak) (K)389393397397
Object allocs0703723240
Releases189380234218
Retains18191818
Throughput (# / s) (#)348710227473361
Time (total CPU) (μs)2979871348308
Throughput as % of baseline100%29%21%96%

A downside to this approach is that MethodList, MethodIterator and all the functions inside them need to be made public, although they are only implementing simple protocols so it isn’t a big issue. The properties inside them thankfully don’t need to also be made public if they’re instead marked as @usableFromInline.

Other observations

Here are a couple of interesting points I noticed or confirmed along the way:

  • Boxing a single type in a struct adds no overhead at runtime, it’s as if the type is only for the benefit of the compiler. To confirm this I tried using the runtime’s Method type directly instead of wrapping it in my ObjCMethod struct and it made zero difference to the number of CPU instructions.
  • Be careful when using map() on a sequence because it will create an intermediate array which can be expensive. I was using this on an UnsafeBufferPointer<Method> like methods.map(ObjCMethod.init) and even though wrapping it in a struct is “free” it was still creating an unnecessary array. Adding .lazy made it much faster and surprisingly added almost no overhead.
  • Stack-allocated classes are almost identical to structs in performance. Changing MethodIterator to a struct and commenting out deinit barely reduced the number of CPU instructions (although of course it then leaked memory).

One last trick

At the top of the post I got a list of all the runtime classes using objc_copyClassList, which allocates an array of classes for you and returns it. There’s a related function called objc_getClassList which takes an already-allocated buffer and fills it for you. Conveniently, Swift’s Array has a low-level initialiser which will give you the underlying buffer to fill. This means an array of classes can be created from the runtime very efficiently without any additional allocation or looping.

Here’s what that looks like:

struct ObjCClass {
    let cls: AnyClass

    static var allClasses: [ObjCClass] {
        let classCount = objc_getClassList(nil, 0)
        return [ObjCClass].init(unsafeUninitializedCapacity: Int(classCount)) {
            buffer, initializedCount in

            let classCount2 = objc_getClassList(
                AutoreleasingUnsafeMutablePointer(buffer.baseAddress),
                classCount
            )
            initializedCount = Int(min(classCount, classCount2))
        }
    }

What this is doing:

  1. The objc_getClassList function first needs to be called with no buffer so it returns the total number of classes.
  2. The array initialiser exposes a buffer of the specified size to be filled.
  3. On the second call to objc_getClassList I get the class count again because this function is a bit weird: it returns the total number of registered classes instead of the number it filled into the buffer. Normally this wouldn’t be an issue, except that the number of classes is different the second time it’s called!
  4. The initializedCount needs to be set to how much of the buffer was actually filled.
  5. I’m doing something slightly sneaky here and using the fact that AnyClass and ObjCClass have identical memory layouts to cast the buffer directly instead of mapping the type of each item. This happens in the AutoreleasingUnsafeMutablePointer initialiser, which discards the type of the underlying pointer.

This technique not only returns a proper Swift Array, but actually turns out to be slightly faster than the “C style” iteration in the first code snippet.


Overall this was an enlightening adventure in benchmarking but it’s easy to spend a lot of time on these micro-optimisations. Unless you’re working on really performance-critical code, code readability and maintainability is usually far more important. It never hurts to have an idea of what’s happening under the hood though!

]]>
https://ko9.org/posts/swift-runtime-1-lifetime/Runtime in Swift: String LifetimesSome lessons learned while building a Swift API for the ObjC runtime functions, part 1.https://ko9.org/posts/swift-runtime-1-lifetime/Wed, 11 Sep 2024 16:43:00 +0200This series of posts is about some interesting problems I encountered while building a seemingly simple Swift wrapper around the Objective-C runtime. The three parts are:

  1. The lifetime of strings passed into C functions (this post)
  2. Getting my Swift wrapper to be as fast as calling into C directly
  3. Exposing some runtime-related classes that are marked as unavailable in Swift

Why a Swift API for the Objective-C runtime?

While I still like writing about the Objective-C runtime, I’ve almost exclusively used Swift for the past few years so my skills in actually coding Objective-C have gotten a bit rusty. Building this Swift wrapper is my way of splitting the two so I can play with the runtime without having to use the language. Admittedly it’s of limited practical use but at some point it just became a challenge to finish.

Most of the Swift wrappers are pretty trivial and essentially just rename the C functions and convert the types if necessary. For example:

struct ObjCMethod {
    let method: Method

    var argCount: Int {
        Int(method_getNumberOfArguments(method))
    }
}

I initially wanted to add these methods as an extension on the runtime’s Method type itself instead of wrapping it in a struct. When I then started working on the Ivar extension I would get weird “Invalid redeclaration” warnings which was really confusing. Eventually I realised that a lot of these runtime types (including Method and Ivar) are just aliases for OpaquePointer and not actually distinct types, so no wonder the compiler was getting upset.

The first real challenge I ran into was related to bridging strings between Swift and C.

Lifetime of string parameters

Passing a Swift String into a C function is usually straightforward. Take a runtime C function like objc_lookUpClass which returns a class by name:

Class objc_lookUpClass(const char *name);

This gets bridged to Swift like so:

func objc_lookUpClass(_ name: UnsafePointer<CChar>) -> AnyClass?

While the UnsafePointer might look a bit concerning, Swift lets you call this in a very natural way by just using a String:

let clsName = "NSDictionary"

let cls = objc_lookUpClass(clsName)

Under the hood, Swift is creating a temporary UnsafePointer to the String contents and passing it to the C function for us. This works fine as long as the C function does not try to store the pointer and use it after the function has returned.

It gets a little more complicated when the function returns a new C string. In that case we can’t just convert it to a Swift String, we also have to free the returned pointer. Take this function which gets the encoding of a method’s return type:

/// Returns a string describing a method's return type.
/// You must free the string with free().
func method_copyReturnType(_ m: Method) -> UnsafeMutablePointer<CChar>

Because the function name includes “copy” it means we’re getting a newly-allocated string and are responsible for freeing it:

let ptr = method_copyReturnType(method)
defer { free(ptr) }
let returnType = String(cString: ptr)

So far so good, but where I got stuck was needing to bridge strings contained in other types.

Arrays of structs of strings

This runtime function dynamically adds a new property to some class:

func class_addProperty(
    _ cls: AnyClass?,
    _ name: UnsafePointer<CChar>,
    _ attributes: UnsafePointer<objc_property_attribute_t>?,
    _ attributeCount: UInt32
) -> Bool

That attributes parameter is an array of objc_property_attribute_t structs, each of which contains two pointers to strings:

struct objc_property_attribute_t {
    var name: UnsafePointer<CChar>
    var value: UnsafePointer<CChar>
}

You might think you can just call this function like so:

let attribs = [
    objc_property_attribute_t(name: "T", value: "@"),
    objc_property_attribute_t(name: "R", value: ""),
    objc_property_attribute_t(name: "V", value: "_city"),
]

class_addProperty(cls, "city", attribs, UInt32(attribs.count))

But this is just going to get you a bunch of warnings:

⚠️ Cannot pass String to parameter; argument name must be a pointer that outlives the call to init(name:value:)
ℹ️ Implicit argument conversion from String to UnsafePointer<CChar> produces a pointer valid only for the duration of the call to init(name:value:)
ℹ️ Use the withCString method on String in order to explicitly convert argument to pointer valid for a defined scope

These warnings are legitimate: by the time you’ve created a objc_property_attribute_t the strings that it points to may have been deallocated. The proper way to do this in Swift would be to use the suggested withCString function and only access the string contents in a closure:

let name = "T"
let value = "@"

name.withCString { namePtr in
    value.withCString { valuePtr in
        let attrib = objc_property_attribute_t(name: namePtr, value: valuePtr)
    }
}

Note that we still can’t use attrib outside of these closures because it’ll contain invalid pointers. As the documentation for withCString says:

The pointer passed as an argument to body is valid only during the execution of withCString(_:). Do not store or return the pointer for later use.

This could work if we were only dealing with a single property attribute since we can call class_addProperty inside the closures. But how are we meant to deal with an array of attributes of any length? There’s no way to use an unknown number of nested calls to withCString!

Arrays of string pointers

A useful article on the Apple dev forums by the Eskimo called The Peril of the Ampersand explains how to handle this situation (see the Manual Memory Management section at the bottom). I only came across it while I was writing this post, but at least it confirmed that there was no easy way of doing this using standard Swift. The suggested technique is to use strdup to create a copy of all the strings, add them to an array of pointers and free them all afterwards. The solution I’d already come up with was similar but didn’t rely on any manual memory management.

Given an array of name/value string pairs, I wanted to be able to call it like this:

let attribs = [("T", "@"), ("R", ""), ("V", "_city")]

withAttributes(attribs) { objCAttribs in
    class_addProperty(cls, "city", objCAttribs, UInt32(objCAttribs.count))
}

What I did was concatenate all the null-terminated name and value strings into a single string so that we only need one withUnsafe call. By accessing the pointer to that joined string, I can create an array of objc_property_attribute_t and use them within the closure:

func withAttributes(
    _ attribs: [(String, String)],
    block: ([objc_property_attribute_t]) -> Void
) {
    // Concatenate all the name/value pairs as C strings into a single CChar array
    let cStrings = attribs.flatMap {
        $0.0.utf8CString + $0.1.utf8CString
    }

    // Access the underlying memory of the joined strings
    cStrings.withUnsafeBufferPointer { buffer in
        var strPtr = buffer.baseAddress!

        // Create an array of objc_property_attribute_t
        let objCAttribs = attribs.indices.map { _ in
            objc_property_attribute_t(
                name: strPtr.moveAfterNextNull(),
                value: strPtr.moveAfterNextNull()
            )
        }

        // Call the closure with the array of objc_property_attribute_t
        block(objCAttribs)
    }
}

That moveAfterNextNull function is an extension I added to C strings to advance the pointer to the next string in memory. It searches for the next null byte and then moves one byte past that to the start of the next string. You can also use a C function like strchr to search for the null byte although I don’t think it would make much difference.

extension UnsafePointer<CChar> {
    /// Moves the pointer to the start of the next C string,
    /// but returns the original pointer
    mutating func moveAfterNextNull() -> Self {
        let original = self
        while self.pointee != 0 {
            self += 1
        }
        self += 1   // Move past the null to the next string
        return original
    }
}

This is probably the deepest I’ve needed to dig into unsafe string buffers which says a lot about how decent Swift’s C bridging usually is. It was also a comforting reminder about how strictly Swift controls the lifetime of objects and how difficult the language makes it to shoot yourself in the foot.

Maybe there’s an easier way to do this though? Let me know if you can come up with one.

]]>
https://ko9.org/posts/simple-swift-web-server/Simple Web Server in SwiftHow to build a tiny HTTP server in Swift.https://ko9.org/posts/simple-swift-web-server/Fri, 14 Jun 2024 11:28:00 +0200I’ve been wanting to improve the built-in web server of the static site generator I use for these articles, since I need to stop and restart the server any time I want to see my latest changes. It would be really nice to have the server hot reload files so I just have to refresh my browser to see the latest edits. Surely it should be easy to find a nice Swift package for a basic web server?

One of the first candidates that I came across was Swifter which bills itself as a “tiny http server engine written in Swift”. That sounds like a promising start! Hmm, it’s well over 2000 lines of code... we have different ideas of “tiny” 😅

The code is pretty clean and understandable though and it definitely does what I want, so maybe I can strip it down to only what I need:

  1. Run only on localhost
  2. Support only GET requests
  3. Serve image files straight from disk
  4. Serve generated HTML files

I definitely didn’t need POST requests, multi-part form decoding or WebSockets support, so that was easy enough to remove. But then most of what I had left was code like this:

var addr = sockaddr_in6(
    sin6_len: UInt8(MemoryLayout<sockaddr_in6>.stride),
    sin6_family: UInt8(AF_INET6),
    sin6_port: port.bigEndian,
    sin6_flowinfo: 0,
    sin6_addr: in6addr_any,
    sin6_scope_id: 0
)

...and that’s when my eyes started glazing over 😵‍💫. This looks like C code I was writing last century! Surely opening a network socket using a modern language is easier than this in the year 2024. Apple has really nice APIs for talking to a HTTP server, so there must be something I can use for building a server. Time for some more research.

Searching the SDK docs, I came across SocketPort which looks like it should be able to replace some of the gnarlier C APIs. There’s also NSStream which looks like it can read and write to the socket. How do I bind the data streams to the socket though, do I really still need to drop down to file descriptors?

More searching turned up CFStreamCreatePairWithSocketToHost which sounds like just what I need. Yes ok it uses CoreFoundation so we’re going backwards a bit here, but I can live with that. Oh wait, it’s deprecated. With no replacement API suggested 😒

Hang on, I remember rewriting a network status monitor at work recently that used a modern class called NWPathMonitor. Turns out there is a modern framework for networking – helpfully called Network – I’d forgotten all about it. And it has a class called NWListener for handling incoming connections, perfect. Let’s get cracking.

HTTP messages

The format of basic HTTP messages is fairly straightforward, you can find what you need just from the Wikipedia entry for HTTP. A minimal HTTP request looks like this:

GET /some/path?param=foo HTTP/1.1
Host: www.example.com 

The first line contains the method (GET), the resource path and the HTTP version separated by spaces. Following this are the request headers which are colon-separated key/value pairs.

A response to this request can be as simple as:

HTTP/1.1 200 OK
Content-Length: 12

Hello world!

The first line is the protocol version, response status code and reason, again separated by spaces. Next are the response headers, a blank line, then any response body content.

Simple right? Let’s get a server running that serves just this response.

Handling connections

Looking at NWListener, it has a callback-based API for dealing with state changes and new connections:

import Network

final class Server {
    let listener: NWListener

    init(port: NWEndpoint.Port) {
        self.listener = try! NWListener(using: .tcp, on: port)

        listener.stateUpdateHandler = {
            print("listener.state = \($0)")
        }

        listener.newConnectionHandler = handleConnection

        listener.start(queue: .main)
    }

    func handleConnection(_ connection: NWConnection) {
        print("New connection!")
    }
}

The port is just a number that identifies which service on a host you’re trying to talk to. For example, one host might have a web server, a mail server and an FTP all running at once, so they need to be on different ports. Every common protocol has a default port number (port 80 for HTTP) but for debug servers we normally pick some larger port number like 8000. That just means when you connect to your server you add the port number in your browser address bar like http://localhost:8000.

I created a new Swift package that builds a macOS command-line app, since I don’t need a UI for this server. In main.swift, start the server like this:

let server = Server(port: 8000)

RunLoop.current.run()

The second line is so that the app stays running and listening for connections, instead of immediately exiting.

Let’s fill out that handleConnection function to actually open the connection:

    func handleConnection(_ connection: NWConnection) {
        print("New connection!")

        connection.stateUpdateHandler = {
            print("connection.state = \($0)")
        }
        connection.start(queue: .main)
    }

Since we’re just building a simple debug server, receiving events on the main queue is fine.

Start the app and run curl -v localhost:8000 in your terminal to make a request to it, and you should see this:

listener.state = ready
New connection!
connection.state = preparing
connection.state = ready

As soon as we have a connection, we can start reading the HTTP request data from it. Add this function and call it from the end of handleConnection:

    func receive(from connection: NWConnection) {
        connection.receive(
            minimumIncompleteLength: 1,
            maximumLength: connection.maximumDatagramSize
        ) { content, _, isComplete, error in

            if let error {
                print("Error: \(error)")
            } else if let content {
                print("Received request!")
                print(String(data: content, encoding: .utf8)!)
            }

            if !isComplete {
                self.receive(from: connection)
            }
        }
    }

This reads as much data as we can from the connection into content and prints it out. Since web browsers reuse connections for multiple requests (“keep alive”) we finish by waiting for the next request.

Sending a response

Finally, let’s actually send a response, by calling this after “Received request”:

    func respond(on connection: NWConnection) {
        let response = """
            HTTP/1.1 200 OK
            Content-Length: 12

            Hello world!
            """

        connection.send(
            content: response.data(using: .utf8),
            completion: .idempotent
        )
    }

You can actually load localhost:8000 in your web browser now and see the reply displayed.

Boom! A minimal web server in 50 lines of Swift 👊

Ok it’s not super useful as is, let’s add a couple of helper types to wrap the request and response messages.

Parsing HTTP requests

Here’s how we could parse the request:

struct Request {
    let method: String          // "GET"
    let path: String            // "/foo/bar"
    let httpVersion: String     // "HTTP/1.1"
    let headers: [String: String]

    init?(_ data: Data) {
        let str = String(data: data, encoding: .utf8)!
        let lines = str.components(separatedBy: "\r\n")
        guard let firstLine = lines.first,
              let lastLine = lines.last, lastLine.isEmpty else {
            return nil
        }

        let parts = firstLine.components(separatedBy: " ")
        guard parts.count == 3 else {
            return nil
        }

        self.method = parts[0]
        self.path = parts[1].removingPercentEncoding!
        self.httpVersion = parts[2]

        let headerPairs = lines.dropFirst()
            .map { $0.split(separator: ":", maxSplits: 1) }
            .filter { $0.count == 2 }
            .map { ($0[0].lowercased(), $0[1].trimmingCharacters(in: .whitespaces)) }

        self.headers = Dictionary(headerPairs, uniquingKeysWith: { old, _ in old })
    }
}

This ensures that the message is in the correct format for a GET request; it assumes there is no request body. Some things to note:

  • Lines in the request should end with a carriage return and line feed, so this splits on \r\n
  • There must be a blank line at the end of the request
  • Header lines should only be split on the first colon, because the value can also contain colons (e.g. Host: localhost:8000)
  • Header keys are lowercased because they should be treated as case-insensitive
  • This is not production quality code! Those force-unwraps should be handled properly at least 😬

Building HTTP responses

Creating a valid response is not too tricky, it’s basically just outputting all the lines joined with CRLF:

struct Response {
    let httpVersion = "HTTP/1.1"
    let status: Int
    let reason: String
    let headers: [String: String]
    let body: Data

    var messageData: Data {
        let statusLine = "\(httpVersion) \(status) \(reason)"
        var lines = [statusLine]
        lines.append(contentsOf: headers.map({ "\($0.key): \($0.value)" }))
        lines.append("")
        lines.append("")    // adds extra required blank line
        let header = lines.joined(separator: "\r\n").data(using: .utf8)!

        return header + body
    }
}

A response can now be created with:

Response(
    status: 200,
    reason: "OK",
    headers: ["Content-Length": "12"],
    body: "Hello world!".data(using: .utf8)!
)

There are plenty of improvements that could be made here, such as automatically determining the Content-Length or using enums of known status codes and header names.

MIME types

One feature that I needed was serving images, and for that I need to set a header that says what the type of file is, such as Content-Type: image/jpeg. That code is known as the MIME type of the file, and Swifter has a big map of known types.

We can do without that though, because macOS can figure out the MIME type for us. Using the UniformTypeIdentifiers framework and calling UTType.jpeg.preferredMIMEType will return “image/jpeg”. There’s even an API that will determine the type for any file.

I’m going to extend the Response type to make it easy to return images, plus throw in a couple of other improvements:

import UniformTypeIdentifiers

struct Response {
    let httpVersion = "HTTP/1.1"
    let status: Status
    let headers: [Header: String]
    let body: Data

    enum Status: Int, CustomStringConvertible {
        case ok = 200
        case notFound = 404

        var description: String {
            switch self {
                case .ok: return "OK"
                case .notFound: return "Not Found"
            }
        }
    }

    enum Header: String {
        case contentLength = "Content-Length"
        case contentType = "Content-Type"
    }

    init(
        _ status: Status = .ok,
        headers: [Header: String] = [:],
        body: Data = Data(),
        contentType: UTType? = nil
    ) {
        self.status = status
        self.body = body
        self.headers = headers.merging(
            [
                .contentLength: String(body.count),
                .contentType: contentType?.preferredMIMEType,
            ].compactMapValues { $0 },
            uniquingKeysWith: { _, new in new }
        )
    }

    init(_ text: String, contentType: UTType = .plainText) {
        self.init(body: text.data(using: .utf8)!, contentType: contentType)
    }

    init(file url: URL) throws {
        try self.init(
            body: Data(contentsOf: url),
            contentType: url.resourceValues(forKeys: [.contentTypeKey]).contentType
        )
    }

    var messageData: Data {
        let statusLine = "\(httpVersion) \(status.rawValue) \(status)"

        var lines = [statusLine]
        lines.append(contentsOf: headers.map({ "\($0.key.rawValue): \($0.value)" }))
        lines.append("")
        lines.append("")    // adds extra blank line
        let header = lines.joined(separator: "\r\n").data(using: .utf8)!

        return header + body
    }
}

Now different types of HTTP responses can be easily constructed like so:

Response("Hello world")

Response("<html><body>Hi!</body></html>", contentType: .html)

try! Response(file: URL(filePath: "~/Desktop/somepic.jpg"))

Response(.notFound)

There are of course loads more handy features you could build on top of this, like a router to map request paths to different responses. But that’s how you end up with 2000+ lines instead of 50 😆

It’s always nice to build something exactly how you want it from the ground up though, especially when you can use nice modern APIs!

]]>
https://ko9.org/posts/gmail-20-years/20 Years of GmailA look back at the launch of Gmail and the peak of Google.https://ko9.org/posts/gmail-20-years/Sat, 30 Mar 2024 11:08:00 +0100Twenty years ago this week, Gmail was announced to the world.

It’s hard to overstate what a revolution this was at a time when free accounts from Hotmail or Yahoo offered only around 2Mb of email storage. Two megabytes seemed to go further back then, but it was not unusual to get a “mailbox full” error when sending someone an email. Before Gmail, most people used an email client on their computer to move emails off the server because of the limited space and basic web interfaces.

Gmail beta logo

When Google said on April 1st 2004 that they were releasing a web mail product with 1000Mb of storage, everyone assumed it was a joke. Hundreds of times more storage than your competitors, for free? That could not possibly be true.

Once it became clear that Gmail was not a prank and would soon be open for invite-only signups, I just had to get an account. People were selling their invite codes online and I bought one for a few bucks. I remember trying to claim nick@gmail.com (so hopeful!) but the minimum name length was 6 characters.

It wasn’t just the near-unlimited storage, the interface was also a revelation – new messages opened without reloading the whole page. Gmail was one of the first major sites to use AJAX before it was even called AJAX. Google had completely disrupted the mail hosting industry.

The heyday of Google

In the early 2000s Google was easily the coolest tech company. Apple had only just released the iPod and wasn’t that well known, Amazon was for buying books online and Facebook didn’t even exist yet. But Google was like Willy Wonka’s lab; no-one had any idea what incredible mad thing they’d come up with next.

Within a couple of years of launching Gmail, Google released a bunch of amazing and wildly popular products such as Google Maps, Google Docs, Google Earth, YouTube, and even Android.

But.

Google didn’t invent any of those. They bought them.

Same with Waze, Nest, Google Flights, and Fitbit: all acquisitions.

It’s not like Google wasn’t creating products in-house, it’s just that not many of them were all that good. Of course there’s the famous stinkers like Google Glass, Google+, and Google Buzz. They seem to release another messaging app every month. Their cloud service is a distant third and they’re panicked about losing in AI, both markets that they could have dominated. Over the years Google has killed hundreds of products that could have been successful startups.

Was Gmail the last revolutionary product that Google actually invented?

]]>
https://ko9.org/posts/equality-performance/Performance of Automatic Equality ChecksTesting the speed of automated equality in both Objective-C and Swift.https://ko9.org/posts/equality-performance/Sat, 16 Mar 2024 12:29:00 +0100This is an update to my previous article where I showed how you could compare objects in an automated way using the Objective-C runtime. At the end of it I asked:

It makes me wonder though, how would this compare to the compiler-generated == and hash(into:) functions of a Swift class? 🤔

It was a throwaway comment to wrap up the article, but since then it’s been bugging me. So I just had to find out the answer!

The test candidates

For the performance showdown I created four versions of the same data model: a Swift struct, a Swift class and two Objective-C classes. Each is a reasonably large data structure with 24 properties, so we can really stress the different equality implementations.

1. Swift struct

First up is the Swift struct with automatic Hashable conformance, which means the compiler will generate the == and hash(into:) functions for us:

struct EqPerfStruct: Hashable {
    var byte1: UInt8
    var int1: Int
    var arr1: [String]
    var dbl1: Double
    // plus the same types repeated 5 more times
}

2. Swift class

For the Swift class we have to manually implement the equality functions because the compiler does not generate these for classes:

final class EqPerfSwiftClass: Hashable {
    var byte1: UInt8
    var int1: Int
    var arr1: [String]
    var dbl1: Double
    // plus the same types repeated 5 more times

    static func == (lhs: EqPerfSwiftClass, rhs: EqPerfSwiftClass) -> Bool {
        guard lhs.byte1 == rhs.byte1 else { return false }
        guard lhs.int1 == rhs.int1 else { return false }
        guard lhs.arr1 == rhs.arr1 else { return false }
        guard lhs.dbl1 == rhs.dbl1 else { return false }
        // test the remaining 20 properties...
        return true
    }
}

One interesting thing I learned: this is exactly how the Swift compiler generates the automatic Equatable conformance.

3. Objective-C class using manual equality

The first Objective-C class has a standard hand-coded isEqual method:

@interface EqPerfObjCClassManual: NSObject

@property (nonatomic) uint8 byte1;
@property (nonatomic) NSInteger int1;
@property (nonatomic) NSArray *arr1;
@property (nonatomic) double dbl1;
// plus the same types repeated 5 more times

@end


@implementation EqPerfObjCClassManual

- (BOOL)isEqual:(id)object {
    if (![object isKindOfClass:EqPerfObjCClassManual.class]) { return NO; }
    EqPerfObjCClassManual *other = (EqPerfObjCClassManual *)object;

    return self.byte1 == other.byte1
        && self.int1 == other.int1
        && [self.arr1 isEqualToArray:other.arr1]
        && self.dbl1 == other.dbl1
        // test the remaining 20 properties...
}

@end

4. Objective-C class using automatic equality

Finally, we have an Objective-C class with an identical interface but using the automatic ivar-based equality from the previous article. This one is called EqPerfObjCClassAuto.

The test setup

For each of the four candidates I measured the performance using XCTestCase.measure. Since each equality check takes only a few nanoseconds, I called them 100,000 times per test run:

func testStructs() {
    let s1 = EqPerfStruct(), s2 = EqPerfStruct()

    measure {
        for _ in 0..<100_000 {
            _ = s1 == s2
        }
    }
}

I made sure that the tests were run in the Release configuration with diagnostics disabled.

Initial results

The performance tests produced an obvious winner:

CandidateNotesTime
Swift structGenerated equality7 ms
Swift classManual equality30 ms
ObjC classManual equality33 ms
ObjC classAutomatic equality24 ms

The clear takeaway here is that the equality functions generated by the Swift compiler for structs are really fast and you should definitely take advantage of those whenever possible. I was surprised that the Swift and Objective-C classes are very comparable performance-wise, considering that the Swift class is using static dispatch versus dynamic dispatch for the ObjC class. The automated equality method is around 30-40% faster than the hand-coded one which lines up with my experience in production code.

But I wasn’t done yet, there were a few other variations I wanted to test...

Non-final Swift class

I was curious how much difference declaring the Swift class as final made, so I made another version of it which was not marked as final:

CandidateNotesTime
Swift classManual equality, final30 ms
Swift classManual equality, non-final51 ms

This actually looks a bit suspicious, even a non-final Swift class shouldn’t be so much slower than the Objective-C equivalent 🤨

After checking the High-Performance Swift docs I noticed that “whole-module optimisation” is off by default even in Release builds. When WMO was enabled the Swift class tests got ridiculously fast (under 0.01ms) even with 1000x as many iterations! Clearly the call to the equality function was being optimised away, so I tweaked the tests to actually use the result of == instead of discarding it and finally got some reasonable-looking results with WMO enabled:

CandidateNotesTime
Swift classManual equality, final6 ms
Swift classManual equality, non-final11 ms

This looks much more impressive now, and the Swift class and struct are very comparable. The slight advantage to classes may be due to not having to copy around the large struct value.

Swift class with dynamic properties

What if we made the Swift class behave the same as the ObjC class? We can do this by adding the dynamic keyword to each of the Swift properties, which forces them to be called with objc_msgSend just like in Objective-C:

CandidateNotesTime
ObjC classManual equality33 ms
Swift classManual equality, dynamic33 ms

I found this quite interesting as it implies that all the performance benefits of Swift in this scenario are solely due to using static dispatch.

Direct ivar access in Objective-C

In the previous article we saw that while performing a lot of equality checks, the runtime itself can be a bottleneck because of all the message sending. So what if instead of accessing every property via the getter in our isEqual method we used the ivar directly? Reading _byte1 instead of self.byte1 would save a call to objc_msgSend each time. Our isEqual method from above would look more like this:

return _byte1 == other->_byte1
    && _int1 == other->_int1
    && [_arr1 isEqualToArray:other->_arr1]
    && _dbl1 == other->_dbl1
    // test the remaining 20 ivars...
CandidateNotesTime
ObjC classManual equality, using properties33 ms
ObjC classManual equality, using ivars12 ms

That is way faster! It’s now very similar to the performance of the non-final Swift class, which makes sense because there is far less message sending going on.

Direct-dispatched Objective-C properties

Did you know that now we can even mark Objective-C methods and properties as direct dispatch? It may be of limited usefulness since it makes your methods non-public and non-overridable, but I was curious to see what difference it makes to equality performance.

I created yet another test class and annotated it as having direct members:

__attribute__((objc_direct_members))
@interface EqPerfObjCClassDirect : NSObject

Despite the article saying “You can’t annotate the primary class interface”, I found this actually worked fine, so maybe it’s been fixed since Mattt wrote that.

Using direct properties certainly makes an improvement, but not quite as much as using ivars instead:

CandidateNotesTime
ObjC classManual equality, using properties33 ms
ObjC classManual equality, using ivars12 ms
ObjC classManual equality, direct properties21 ms

Final results

Here are the performance results of all of the different variations:

Equality performance results summarising results above

These pretty much line up with my expectations: Swift is fast due to static dispatch and adding any indirection to that has a performance cost. It’s nice to see that Objective-C is very competitive with the equivalent Swift classes!

But wait, weren’t the automated equality methods supposed to be the fastest option for Objective-C? Well there may have been a couple of details I didn’t mention in that article.

Two more things

Even though automated equality was mainly about reducing boilerplate in the app I worked on, I used two additional shortcuts that made the equality checks significantly faster.

Fast bitwise comparison

Since classes are backed by structs, it’s possible to compare the backing structs of two objects as raw blocks of memory using memcmp. This will only tell you if the objects are bitwise identical, so any object properties must contain the exact same pointers. It’s still a useful shortcut if you expect large objects to be equal a lot of the time.

There is one small wrinkle: we need to skip the isa field at the start of each class because that is no longer a pointer and now contains housekeeping information instead. Given that, we can add this inside our isEqual method:

// Skip the isa fields
Ptr selfAfterIsa = (__bridge void *)self + sizeof(Class);
Ptr otherAfterIsa = (__bridge void *)other + sizeof(Class);
size_t sizeWithoutIsa = class_getInstanceSize(self.class) - sizeof(Class);

// Compare as raw memory, returns 0 if identical
if (memcmp(selfAfterIsa, otherAfterIsa, sizeWithoutIsa) == 0) {
    return YES;
}

// Otherwise continue with memberwise equality...

Using this shortcut in the performance tests results in a time of 7ms, but only when comparing equal objects of course. This is as fast as Swift structs!

Side note: it turns out this memcmp trick does not work on Swift structs because unlike ObjC objects they are not initialised to all zeroes. This means the padding between fields is usually different even with two equal structs.

Cached hash comparison

This second shortcut helps when the objects are unequal, but it does come with a big caveat: the objects must be immutable. In the app I worked on, this was used with classes which only had readonly properties and could not be changed after initialisation (think: Redux state or wrapped JSON payloads).

If your objects are immutable then you can cache their hash values because they should never change. Since objects with different hash values must be unequal, we can quickly test those in isEqual before we compare every property:

// Pre-check of cached hash values
if (self.hash != other.hash) {
    return NO;
}

// Otherwise continue with memberwise equality...

Note that this doesn’t work the other way around; two unequal objects may have the same hash value.

This second shortcut resulted in a time of 6ms when comparing unequal immutable classes, which is also as fast as Swift structs.

Conclusion

Going back to the original question “how does my automated equality in ObjC hold up against Swift?” I have to say the answer is: amazingly well! I honestly didn’t expect it to be just as fast as the generated == function of a Swift struct though.

It’ll be a shame to remove this automated equality during the migration away from Objective-C but I certainly don’t have to worry about the performance of the Swift replacement.

]]>
https://ko9.org/posts/inspecting-ivars/Ivars and Automatic Object EqualityInspecting instance variables using the Objective-C runtime for automatic object equality and hashing.https://ko9.org/posts/inspecting-ivars/Thu, 19 Oct 2023 14:56:00 +0200

This is the penultimate post in my series on the Objective-C runtime; you can read the others here.

In earlier articles on the runtime I’ve mainly talked about properties, since they’re how we typically interact with objects. However sometimes it’s useful to pull back the curtain and inspect the instance variables (ivars) that those getters and setters hide.

For this article I’m going to show how we can inspect the ivars of an object and dynamically read and write the values of those ivars. I’ll also go over a real scenario where direct ivar access was useful for fast automatic equality checks and hashing of objects.

Backing structs

As mentioned in Dictionary-Backed Objects the actual storage for an Objective-C object is provided by a C struct.

For example, take a class representing a car:

@interface Car: NSObject
@property NSString *make;
@property NSString *model;
@property uint16 year;
@property uint8 topSpeed;
@property uint16 range;
@property BOOL isElectric;
@end

The backing struct generated by the Objective-C compiler looks something like this:

struct {
    Class isa;  // added to the start of every class
    uint8 _topSpeed;
    BOOL _isElectric;
    uint16 _year;
    uint16 _range;
    NSString *_make;
    NSString *_model;
};

But why has the order of the fields been changed? Let’s look at how this C struct is laid out in memory:

      0     1     2     3     4     5     6     7   
   ┣━━━━━┻━━━━━┻━━━━━┻━━━━━┻━━━━━┻━━━━━┻━━━━━┻━━━━━┫
0  ┃ isa                                           ┃
   ┣━━━━━┳━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━┫
8  ┃ (a) ┃ (b) ┃ _year     ┃ _range    ┃ × × × × × ┃
   ┣━━━━━┻━━━━━┻━━━━━━━━━━━┻━━━━━━━━━━━┻━━━━━━━━━━━┫
16 ┃ _make                                         ┃
   ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
24 ┃ _model                                        ┃
   ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
   
   (a) = _topSpeed
   (b) = _isElectric
    ×  = padding

The three pointers (isa, _make and _model) are 8 bytes each, and must be aligned to the nearest 8 bytes. If the ivars had been laid out in the same order as the properties were defined, there would be more padding needed to align the pointers. The compiler is smart enough to re-order the smaller fields together at the top of the struct, minimising wasted space.

Inspecting ivars

The technique for listing the ivars of a class is very similar to that for inspecting properties. There is a function for looking up a single ivar by name (class_getInstanceVariable) and one for getting all the ivars on a class (class_copyIvarList).

NSLog(@"Ivars of class %@", self);

uint ivarCount = 0;
Ivar *ivars = class_copyIvarList(self, &ivarCount);
for (int i = 0; i < ivarCount; i++) {
    Ivar ivar = ivars[i];

    // do something with ivar
}
free(ivars);

NSLog(@"Class size: %zu", class_getInstanceSize(self));

So what details can we get on each ivar? Mainly just the name, byte offset within the backing struct, and the type encoding. From the type encoding we can also use the NSGetSizeAndAlignment function to determine how many bytes are needed to store each field.

Adding this code into the loop...

    NSUInteger size = 0;
    NSGetSizeAndAlignment(ivar_getTypeEncoding(ivar), &size, NULL);

    NSLog(@"Offset: %td\tSize: %u\t%s (%s)",
          ivar_getOffset(ivar),
          (uint)size,
          ivar_getName(ivar),
          ivar_getTypeEncoding(ivar));

...will print this when we run it:

Ivars of class Car
Offset: 8     Size: 1    _topSpeed (C)
Offset: 9     Size: 1    _isElectric (c)
Offset: 10    Size: 2    _year (S)
Offset: 12    Size: 2    _range (S)
Offset: 16    Size: 8    _make (@"NSString")
Offset: 24    Size: 8    _model (@"NSString")
Class size: 32

We can notice several things here:

  • The isa field never shows up as an ivar but the first ivar is offset by 8 bytes to account for it.
  • There is a gap of two bytes after the _range field as we saw on the memory layout diagram above.
  • The size of the class instance (32 bytes) is indeed equal to the offset plus size of the last field (24 + 8).

Note that ivar_getTypeEncoding always returns NULL for Swift classes, even when they are declared as @objc.

Other ivar functions

The remaining functions for ivars are object_getIvar and object_setIvar which read and write the value of a given ivar. These functions take id parameters for the value, so only work for properties that point to objects. For scalars we need to calculate the address of the ivar and cast it to the correct type instead.

There are two more functions called class_getIvarLayout and class_getWeakIvarLayout that indicate which ivars are object pointers and are mainly for internal use by the runtime. You can decode the value that these return using source code from the runtime itself.

Using ivars for automated equality checks

If we wanted to compare two Car objects, we’d normally have to write a method like this:

- (BOOL)isEqualToCar:(Car *)other {
    return [self.make isEqualToString:other.make]
        && [self.model isEqualToString:other.model]
        && self.year == other.year
        && self.topSpeed == other.topSpeed
        && self.range == other.range
        && self.isElectric == other.isElectric;
}

This is not only repetitive and dull to write, it’s also error-prone since we can easily forget to compare any new properties we might add to the class. You also need a matching hash function, which has the same problems.

Note that equality is a surprisingly complex topic once you start dealing with subclasses. Should an instance of a subclass ever be equal to an instance of the superclass? If so does it maintain symmetry, i.e. if [a isEqual:b] does [b isEqual:a]? It’s not something you can apply a general solution to.

Equality using properties

An app I work on had dozens of large classes which needed to support equality checking, causing constant issues with the large boilerplate isEqual and hash methods. My first solution was to add a category on NSObject to automatically calculate these using all the properties on an object, so that these methods could be just a single line for each class:

- (BOOL)isEqualToCar:(Car *)other {
    // iterates over all properties and compares their values
    return [self isEqualUsingAllProps:other];
}

It helped us to remove a lot of fragile code. However once we started using it to frequently compare view models we saw it was noticeably slower than the hand-rolled isEqual methods. The problem was using Key-Value Coding to read the value of each property, which is not as fast as comparing two property values directly.

Equality using ivars

So I wondered what if we bypass KVC and read the underlying ivars directly? Let’s give it a go:

- (BOOL)isEqualToUsingAllIvars:(id)other {
    if (self == other) {
        // identical pointers
        return YES;
    }

    if ([other isMemberOfClass:self.class] == NO) {
        // not the same class, also excludes subclasses
        return NO;
    }

    // Compare all ivars
    uint ivarCount = 0;
    Ivar *ivars = class_copyIvarList(self.class, &ivarCount);
    for (int i = 0; i < ivarCount; i++) {
        Ivar ivar = ivars[i];
        const char *encoding = ivar_getTypeEncoding(ivar);
        TypeEncoding *type = [[TypeEncoding alloc] initWithEncoding:@(encoding)];

I’ll use the TypeEncoding helper from an earlier article on Type Encodings. If the ivar is an object, we have to compare the two values using their own isEqual method:

        if (type.isObjectType) {
            id obj1 = object_getIvar(self, ivar);
            id obj2 = object_getIvar(other, ivar);
            if ([obj1 isEqual:obj2] == NO) {
                return NO;
            }
        }

Otherwise we need to compare the ivars as scalar values. A straightforward way to deal with scalar values of any size is to determine the size using NSGetSizeAndAlignment then comparing that many bytes directly. We need to do a little pointer manipulation to get the exact address of each ivar and pass those to memcmp:

        else if (type.isIntType || type.isFloatType) {
            ptrdiff_t offset = ivar_getOffset(ivar);

            // get the memory address of each ivar
            void *ptr1 = (__bridge void *)self + offset;
            void *ptr2 = (__bridge void *)other + offset;

            NSUInteger size;
            NSGetSizeAndAlignment(encoding, &size, NULL);

            // note this returns 0 if the bytes are equal
            if (memcmp(ptr1, ptr2, size) != 0) {
                return NO;
            }
        }
    }
    free(ivars);

    return YES;
}

An automated hash method looks very similar: combining the hash of object ivars, and using the private CFHashBytes function for hashing scalars.

Performance results

Using ivars turned out to be faster than comparing properties as expected, but calls to the runtime functions were still showing up in profiling. To avoid having to look up the ivar encoding and offset each time, I created a list of all the ivar details of a class that contained:

  1. The size of the ivar
  2. The offset from the previous ivar (padding)
  3. The general kind of the ivar: object reference, weak reference, scalar, or “skip” (excluded from equality checks)

This list was then compacted by replacing skipped values with extra padding on the next ivar, and combining contiguous scalars into a single one where possible. Combining scalars meant that we could compare a bigger block of memory with memcmp instead of calling it for each ivar. This encoding was calculated the first time it was needed, then cached on the class object.

So how did it perform?

It exceeded my expectations by being even faster than hand-written equality methods! The automated isEqual methods were twice as fast as the old methods, and hash was 4-5x faster.

This is still used for hundreds of classes in our app, but they’re steadily being replaced by Swift so one day I’ll sadly have to remove it. It makes me wonder though, how would this compare to the compiler-generated == and hash(into:) functions of a Swift class? 🤔

]]>
https://ko9.org/posts/kvo-talk/Tech Talk on KVOA tech talk from the archives, extolling the virtues of Key-Value Observing.https://ko9.org/posts/kvo-talk/Sat, 2 Apr 2022 12:34:00 +0200I recently came across the slides for a talk that I presented in 2017 on the subject of Key-Value Observing. While it may not be as relevant today, I still thought it was quite a good presentation (if I may say so myself 😁) and wanted to share it here.

I’ll add my comments below each section in a block quote like so.


A Totally Impartial Look At Key-Value Observing

KVO sucks

More Specifically

KVO Is A Bad Idea, Implemented In A Terrible Way

 

Ok maybe a little harsh but it never hurts to get people’s attention at the start of a talk 🙂

While I am aware that KVO underpinned some important features like Cocoa Bindings and may work fine for small teams using it in a very disciplined way, when used in a large app with many developers contributing, it’s a nightmare.

In my opinion KVO is a rare miss for Apple’s APIs and should probably never have been ported to iOS.


What’s So Bad About It?

It’s Super Fragile

  • Accidentally forgot to remove an observation before the observed object was deallocated? CRASH
  • Removed an observation before adding it? CRASH
  • Removed an observation twice? CRASH
  • Deallocated the observer before removing the observation? CRASH

It Has No Protection

  • Can I find out who is observing an object? NOPE
  • Can I find out which objects I am observing? NOPE
  • Can I at least just remove all my observers? HAHA, NOPE
  • Can I determine if a property even supports KVO? NAH
  • Can I easily break my superclass? YEP
  • Can I break behaviour of my subclasses? SURE!

It’s Painful To Use

  • Can’t keep the observer creation code and the observation handler together
  • Can’t find out which object changed along a keypath
  • Can’t observe weak pointers being nilled
  • Doesn’t support fancy modern features like blocks, queues, weak references or even custom selectors
  • To use it safely, needs lots of boilerplate: context objects, calls to super, keypath checks, flags to track observation

I’ve experienced all of these crashes more than once over the years. Since the code to register, observe and unregister can be spread all over a big view controller, it’s extremely easy to break by accident.

The API for KVO has remained essentially unimproved since the very first iOS SDK. Which is odd considering that even the confusingly-similar NSNotificationCenter API got some minor updates in iOS 4 with support for these “fancy modern features” like blocks and queues 😄


KVO Is Not A Great Idea Anyway

  • Introduces invisible dependencies
  • Almost impossible to find out which parts of the code might be observing your object
  • Hard to notice if you break KVO compliance
  • Relies on objects that you don’t control the lifetime of
  • Keypath observing breaks loose coupling

The notion that KVO encourages bad software design is nothing new, but that hasn’t stopped people from attempting to fix its shortcomings for a long time.


OK I Get The Picture

So What Do I Use Then?

Literally Anything Else

  • Notifications
  • Callback blocks
  • Target / action
  • Delegates
  • Swift KVO 😒

I don’t really consider KVO in Swift to be much of an improvement conceptually, but at least the API uses strong key paths, completion blocks and unregistration on deallocation.


But I Really Want To Use KVO

If You Must Use KVO, Use This

self.observer =
    [self addObserverForKeyPath:SELFKEY(prop.subprop)
                        options:KVOSendInitialValue
                          block:^(id newVal, id oldVal) {
                              NSLog(@"Now %@", newVal);
                          }];

or:

self.observer =
    [self addObserverForKeyPath:SELFKEY(prop.subprop)
                        options:KVOSendEqualValues
                       selector:@selector(valueChanged:)];

Now we get to the point of the talk, it wasn’t just a rant 😆

I not only wanted to discourage usage of KVO, but provide a safer alternative to the existing uses of it in our codebase.


NSObject+KVObserver

Why It Sucks Less

  • Robust, safe, and simple to use
  • Observation is tied to lifetime of returned token
    • Automatically removed when the token is nilled
    • Usually you don’t have to remove observers at all
  • Enforces good KVO practices
    • Can only observe properties of objects you own
    • All observation handlers are called on the main thread

While my KVO helper worked quite well (improving even on Mike Ash’s effort), I won’t be going through the source code for it as usual. It didn’t really use any interesting features of the ObjC runtime, and honestly it takes some pretty gnarly code to make KVO play nice, which I’m not keen to show off.


The presentation ended with a screenshot of a KVO crash in Fabric, the dreaded “cannot remove an observer for a key path because it is not registered as an observer” exception.

70,000 crashes in a week. 😞

]]>
https://ko9.org/posts/ev-touring-europe/EV Touring in EuropeLessons learned from a few road trips in Europe with an electric car.https://ko9.org/posts/ev-touring-europe/Sun, 20 Mar 2022 17:42:00 +0100Over the past few months I’ve been lucky enough to take some longer trips in different electric cars, and picked up a few things that I wish I’d known beforehand. So here are some of the lessons that I learned the hard way, so you don’t have to make the same mistakes 😄

I live in Amsterdam where owning a car is not essential, so instead I use sharing services when I need a car. Many of these services now only have electric cars, so I’ve had the opportunity to drive a few different models for short trips around the city. They’re a joy to drive and super convenient—you just unlock one of their cars in the neighborhood with an app, pay a few cents per minute and can park anywhere and recharge for nothing.

Around the city, EVs are pretty similar to petrol cars though. Where you really notice the difference is on longer drives. I’ve done two road trips so far, across five different countries and about 2000km total: one in a rented Kia Niro and the other in a brand new Hyundai Ioniq 5.

Ioniq 5 (source: hyundai.com)

Highlights

  • Driving an EV is just plain fun! They have incredible torque, great handling, and they sound like you’re in a spaceship. The guilt-free feeling of not burning petrol is just icing on the cake. Knowing that your ride is powered by sunshine or wind is actually pretty damn cool 😎
  • We’re really spoiled for charging infrastructure in the Netherlands. There are thousands of public charging points just in Amsterdam, and every service station on the highway has DC fast chargers.
  • Charging overnight is so handy. You plug in the car when you get home and just like your phone, it’s topped up and ready in the morning. Neither of the B&Bs we stayed at billed us anything to recharge which was a pleasant surprise.
  • The cars I used had really good driver assist features, like adaptive cruise control and automatic steering on highways. I know these aren’t EV-specific, but they make it very easy to drive longer distances without fatigue, even in heavy traffic. Going from 130km/h to a crawling stop and back to highway speed without touching any pedals is magic 🤩

Lowlights

  • Range is still an issue for long drives. EVs get roughly half the distance from a full charge than you would from a typical tank of petrol. It just means you’ll need a short charging stop every few hours of driving.
  • DC fast chargers are really fast, but they’re also quite expensive: around double the price of a normal public charger. So you definitely pay for the convenience, but they’re only needed for topping up during a long trip, not for everyday driving.
  • “How far can you drive?” and “how fast does it charge?” are the most common questions, and the answer is… it’s complicated. AC vs DC chargers, different charging speeds, different cable plugs; it’s not as simple as filling up a tank. Batteries discharge more quickly when they’re nearly empty (like your phone probably does) so the remaining range can drop faster than you expect. You can charge them to 80% much more quickly than to 100% full. It all takes a while to figure out, but hopefully I can demystify some of it here!
  • The networks of public charging points are run by different companies with different payment systems. Making a one-off payment for an “off network” charger can be quite a hassle. There is a heck of a lot that can be improved with standardising and simplifying the charge and payment experience across every country.

Lessons learned

Here’s what I had no idea about before I took an EV out of the city.

DC and AC chargers are very different

Electricity is electricity right? Sure, but there are big differences on how fast you can cram it into your car. In short, normal household power is AC (alternating current) but batteries only deal with DC (direct current). So the common charging points that are hooked up to the city grid are AC, while fast chargers can provide DC straight to your battery. It might sound like they’re on a spectrum from “slower to faster” but DC chargers are in a whole different league to AC.

AC chargers are:

  1. mostly found in cities
  2. relatively cheap, comparable to normal electricity prices
  3. designed for slow charging to 100%, for example overnight or while parked for a few hours
  4. typically rated at 7, 11 or 22kW charging speeds

For comparison, charging from a regular household power socket tops out at around 2.5kW. You can charge from them, but it’s really slow!

Whereas DC fast chargers are:

  1. more common on highways and the outskirts of cities
  2. more expensive per kWh (unit of charge), up to twice the price of AC
  3. designed for quick charging bursts to add range, but not to full and not for everyday usage
  4. rated from about 40kW to a hair-raising 350kW 😳

Just like your phone needs a charger to convert AC to DC, fast DC charging stations also have a converter. Except they’re the size of a fridge, really loud, and apparently cost a few hundred grand each. But that means that they’re able to shove power into your EV as fast as the battery can handle.

DC fast chargers (source: fastnedcharging.com)

Charger speed doesn’t equal speed of charging

Just because a charger is rated at 100kW, it doesn’t mean you’re going to fill an empty 50kWh battery in half an hour. In theory that’s how simple the maths is, but it’s a long way from the reality of battery chemistry.

Batteries charge at different rates depending on their temperature and how full they are (and probably a hundred other factors). What that means is that you might be able to charge at top speed until around 80% full, but then charging will slow dramatically so that the last 20% might take just as long again. That’s fine if you’re charging overnight, but is why it’s typical to not fill up the whole battery when fast charging.

You’re also limited by the charging speed of the car itself. The Ioniq 5 I drove could charge at up to 220kW DC so can fill from 10% to 80% in just under 20 minutes which is quite impressive. It’s worth finding out the charging limits of the car so that you don’t pay more to use a 350kW charger when the car can only take say 80kW.

Something that wasn’t obvious to me is that when you use an AC charger, the car needs to use its small internal DC converter. Several times I used public 22kW chargers and wondered why they weren’t adding the range I expected, but it turns out the Ioniq can only convert AC to DC at a maximum of 11kW.

Remaining range is optimistic

Every EV shows how much range is remaining, front and centre on the dashboard. Unfortunately it seems more like an aspirational figure, or assumes that you plan to drive downhill the whole way 😁

On my first EV trip, I was 230km from home and the battery had 300km range remaining, so I thought I’d make it easily. Everything was fine until about 100km to go, when the range started dropping noticeably faster than the distance remaining. Over the next 50km, I progressed quickly from “I’ll be fine” to “I should be able to make it” to “ok this is really pushing my luck” to having to pull over for a quick 5 minute charge. Doing 130km/h+ drains the battery a lot faster, but I was keeping a very steady speed with cruise control so assumed the range estimator could adjust.

Having the current power draw and remaining range display is useful, but in some ways it’s also less relaxing when you’re constantly aware of it. You really notice how much it gets affected by fast acceleration, driving up long hills, headwinds or autobahn speeds. Of course petrol cars are the same but you don’t notice the extra fuel you’re burning second by second.

Charging infrastructure varies widely between countries

As I mentioned, the Netherlands has a very high uptake of EVs (behind only Norway) so probably has the best charging infra on mainland Europe. This should give you an idea of the ridiculous number of public chargers in central Amsterdam:

Amsterdam chargers (source: amsterdam.nl)

If you expect similar coverage in other cities though, you’re in for a rude shock. The number of chargers thins out the further south and east you go across Europe, which makes sense when you consider GDP and the current cost of electric cars.

I’ve driven an EV into Belgium, Luxembourg, France and Germany and not had any real problems finding a charger, but it still pays to plan ahead and see what’s available at your destination. There are plenty of apps to find chargers, including Google and Apple Maps. I ended up searching like this most of the time:

Luxembourg chargers (source: Google Maps)

Keep in mind that chargers can be occupied, out of order, or hard to find, so it’s best to stay flexible. I preferred to head for big clusters of chargers in car parks to improve the odds of one being available and working.

Some Aldi and Lidl supermarkets in Germany and Belgium have free chargers in the carpark, even DC fast chargers! I thought there would be a catch, but during store hours you really can just pull up and plug in for nothing. This is more of a nice bonus though, I wouldn’t rely on them.

Don’t forget about destination charging either, more and more hotels and B&Bs have electric car chargers available now.

Payment can be a pain

Paying for charging was probably the biggest hassle of EV touring. Every charge point generally has two different ways of paying:

  1. You subscribe to one of the charging providers and you get a swipe card that you use to start charging. This is great when it works, but the charger has to be on that providers network, and it’s unlikely that every charger that you use across different countries is on the same network. Sometimes the card doesn’t work even on a supported charger, which is frustrating.
  2. You need to make an ad-hoc payment, usually by scanning a QR code on the side of the charger, then signing up through their mobile site or app with your credit card. This can vary from a mildly inconvenient to an incredibly frustrating experience. The rates are also invariably higher than being on a subscription.

The perfect setup would be a single system across every country Europe-wide, ideally just using contactless phone or card payments instead of needing a particular swipe card. There’s also a big need for a centralised data source for the locations and availability of all chargers, so that we can use any app to look up every charger in a city, not just those on specific networks. It should be consistent, reliable, and seamless everywhere—just like it is with petrol stations.

I’m sure we’ll get there soon enough, more and more charging infrastructure is being built every year. Until then, EV touring in Europe is already a good experience, it just takes a little more planning and research.

It’s exciting to think how much better it will be 10 years from now! 🚗⚡️

]]>
https://ko9.org/posts/dictionary-backed-objects/Dictionary-Backed ObjectsAn Objective-C class with dynamic storage. Describes how to create method implementations at runtime, using functions such as resolveInstanceMethod and class_addMethod.https://ko9.org/posts/dictionary-backed-objects/Sun, 16 May 2021 14:54:00 +0200Today we’ll look at dynamic method resolution, one of the coolest features of the Objective-C runtime, which lets our app create new methods on the fly! 🚀

We’ll use the dynamic method resolution features of the runtime to to build a dictionary-backed class. What does “dictionary backed” mean? Well let’s first have a look at how normal classes work.

Take a Person class declared as:

@interface Person: NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic) int age;
@property (nonatomic, copy, nullable) NSArray *siblings;
@end

This class needs some backing storage to actually store the string, int and array in memory. In Obj-C, classes are backed by C structs, which in this case would look like:

struct {
    Class isa;
    NSString *_name;
    int _age;
    NSArray *_siblings;
};

Objective-C knows to create, or synthesize, an ivar for each property because that’s the default behaviour if we don’t manually implement any getters or setters.

The isa field is present at the start of every class struct, so that Obj-C knows which kind of class it represents. The other fields which provide storage for properties (“ivars”) are by default prefixed with an underscore.

Historical note: we used to have to write @synthesize name = _name; to explicitly declare storage for each property, but since 2012 Xcode has auto-synthesized these for us 🦕

Allocating storage space

When you instantiate an object from a class with new or alloc+init, memory is allocated for the backing struct. This struct is always a fixed size, even though it might contain pointers to variable-size objects, such as the name string or the array of siblings.

This space needs to be allocated even for nullable properties which are not set, which is usually not an issue since they’re just pointers that take 64 bits each.

But imagine you had a big class with dozens of properties, many of which are nullable:

@interface Movie: NSObject
@property (nonatomic, copy) NSString *title;
@property (nonatomic) int year;
@property (nonatomic) NSTimeInterval length;
@property (nonatomic, copy) NSString *director;

// nullable metadata:
@property (nonatomic, copy, nullable) NSArray<Movie *> *related;
@property (nonatomic, copy, nullable) NSString *plotSummary;
@property (nonatomic, copy, nullable) NSArray<NSString *> *keywords;
@property (nonatomic, copy, nullable) NSArray<NSString *> *genres;
@property (nonatomic, copy, nullable) NSArray<NSString *> *awards;
@property (nonatomic, copy, nullable) NSArray<NSString *> *altTitles;
@property (nonatomic, copy, nullable) NSArray<NSString *> *reviews;
// etc...
@end

This class struct could get relatively large, perhaps a kilobyte or more for a single instance, which doesn’t even include the space needed for the strings and arrays themselves. Again, this is not a big deal unless you are dealing with thousands of Movie objects at once, but it’s a lot of wasted memory if most of those fields are usually nil.

Dictionary storage

Instead of a fixed-size backing struct, what if we used an NSDictionary for storage instead? Dictionaries are dynamically-sized, so will automatically expand to fit in as many objects as needed.

A movie where most of the properties are nil could take up much less memory, because those missing properties just won’t be stored at all. The data for a valid Movie object could be only:

@{
    @"title": @"My Great Movie",
    @"year": @2020,
    @"length", @7200,
    @"director": @"Jane Doe",
}

While we reduce the storage space needed, by storing everything in a dictionary we lose the type-safety and convenience of properties. But there is a way to get the best of both.

How could we convert our Movie class with properties to use dictionary storage instead of a backing struct? By providing custom getters and setters for each property, Xcode will no longer auto-synthesize these properties and therefore no longer reserve space in the struct for them.

An implementation of our class could look something like this:

@interface Movie ()
// internal storage for properties
@property (nonatomic) NSMutableDictionary *storage;
@end

@implementation Movie

- (instancetype)init {
    self = [super init];
    if (self) {
        _storage = [NSMutableDictionary dictionary];
    }
    return self;
}

- (NSString *)title {
    return self.storage[@"title"];
}

- (void)setTitle:(NSString *)title {
    self.storage[@"title"] = title;
}

- (int)year {
    return [self.storage[@"year"] intValue];
}

- (void)setYear:(int)year {
    self.storage[@"year"] = @(year);
}

// ...

@end

For a class with a lot of properties, creating a getter and setter for each one would get tedious very quickly, not to mention error-prone.

If only there were a way to automatically generate all those methods... 🤔

Onto the fun stuff!

Dynamic method resolution

Remember that in Objective-C we’re usually not calling functions, but sending messages. Calling [self doSomething] sends a message (a selector called doSomething) to an object (called self).

The difference with message sending is that the linking between messages and methods isn’t hardcoded into the app, but can be modified while the app is running. If the runtime can’t find a method to respond to a message, it won’t fail immediately but will give us a chance to respond to the message dynamically.

The important runtime calls we’ll need to respond to unknown messages are:

  • +resolveInstanceMethod. This is a method on NSObject called by the runtime when it can’t find a method for a given selector. If we implement this and determine that we can handle the particular selector dynamically, we should add the method and return YES.
  • class_addMethod. This provides an implementation for a selector on a specific class. The method only needs to be added once, and then it can be called just like any normal method which was compiled into the app!
  • imp_implementationWithBlock. The implementation of a method (IMP) is usually just a C function which takes at least two parameters, self and the selector being called (_cmd). For us it’s more convenient to provide a block for the implementation instead, and this function will turn a block into an IMP for us.

    You can read more about IMP and method implementations in the earlier article on Implementing Key-Value Coding.

    Making properties dynamic

    To make use of dynamic method resolution, the first thing we need to do is mark all of our properties as dynamic. Just beneath @implementation Movie, we put:

    @dynamic title, year, length, director; // add all the other props too
    

    The @dynamic keyword tells Objective-C “don’t create an ivar for this property, and don’t create any getter or setter, I’ll provide them at runtime”. All it will do for you is create the property and the getter and setter selectors but not the actual implementations.

    If you tried to access these dynamic properties at this point, you’d get a crash with the familiar error:

    -[Movie setTitle:]: unrecognized selector sent to instance

    So we have to make sure that those selectors get handled! 😄

    Resolving methods

    We’ll need to implement the +resolveInstanceMethod method on our Movie class. As mentioned, this method takes the selector which Obj-C can’t find a method for.

    Since we’re dealing with properties, the selector would only be either a getter or setter. For example, title and setTitle:. So the first thing we’ll need to do is figure out which property the selector refers to. For this, we’ll be using the ClassProperty wrapper from the earlier article on Inspecting Properties.

    Let’s add another method to our Movie class to find the matching property:

    + (nullable ClassProperty *)propertyForSelector:(SEL)sel {
        for (ClassProperty *prop in self.classProperties) {
            if (sel == prop.getter || sel == prop.setter) {
                return prop;
            }
        }
        return nil;
    }
    

    Now we can go ahead and implement resolveInstanceMethod itself:

    + (BOOL)resolveInstanceMethod:(SEL)sel {
        ClassProperty *prop = [self propertyForSelector:sel];
        if (prop == nil) {
            NSLog(@"Couldn't resolve method for selector: %@",
                  NSStringFromSelector(sel));
            return NO;
        }
    

    This first part looks for the matching property and exits early if it can’t be found. A return value of NO indicates to the runtime that the method could not be resolved.

    Let’s first handle the case when we’re resolving the getter of the property. We’ll also only add support for NSString values for now:

        if (sel == prop.getter) {
        
            id block = ^(Movie *self) {
                NSString *val = self.storage[prop.name];
                return val;
            };
    

    The implementation block simply reads the value from the backing storage and returns it. We’ll assume it’s the correct type since no-one else can access our storage dictionary.

    Before we call class_addMethod, we’ll need to figure out the type encoding of the method. For a getter, this is the encoding of the return type plus an object type (self) and selector (_cmd).

    Then we just turn the block into an IMP and pass it to class_addMethod:

            NSString *typeEncoding
                = [prop.type.encoding stringByAppendingString:@"@:"];
    
            IMP imp = imp_implementationWithBlock(block);
    
            return class_addMethod(self, sel, imp, typeEncoding.UTF8String);
        }
    

    The case for the setter method looks very similar. The main differences are that the implementation block takes a new value parameter, and the type encoding is now void (the return type), then class, selector and the value type:

        if (sel == prop.setter) {
            id block = ^(Movie *self, NSString *val) {
                self.storage[prop.name] = val;
            };
    
            NSString *typeEncoding
                = [@"v@:" stringByAppendingString:prop.type.encoding];
    
            IMP imp = imp_implementationWithBlock(block);
    
            return class_addMethod(self, sel, imp, typeEncoding.UTF8String);
        }
    

    Supporting properties of any type

    From the earlier article on Implementing Key-Value Coding, we saw that properties need to be handled separately for each type. As in that situation, the easiest way to do this is by using a macro to generate the code for most cases.

    All of the scalar types will have to be boxed into an NSValue so they can be placed in the storage dictionary and unboxed when we read them out.

    Let’s add some macros that will create getter and setter blocks to box and unbox scalar values for us:

    #define GETTER_BLOCK(UNBOX_METHOD)          \
        (id)^(Movie *self) {                    \
            id val = self.storage[prop.name];   \
            return [val UNBOX_METHOD];          \
        }
    
    #define SETTER_BLOCK(BOXABLE_TYPE)          \
        (id)^(Movie *self, BOXABLE_TYPE val) {  \
            self.storage[prop.name] = @(val);   \
        }
    
    #define ACCESSOR_BLOCK(GETTER, TYPE, UNBOX) \
        GETTER? GETTER_BLOCK(UNBOX) : SETTER_BLOCK(TYPE)
    

    We would call these for int properties like GETTER_BLOCK(intValue) and SETTER_BLOCK(int), or just use the combined convenience macro: ACCESSOR_BLOCK(isGetter, int, intValue).

    Since this will be a big switch statement to handle all the cases, we can pull that into a separate method like so:

    + (nullable id)blockForAccessor:(ClassProperty *)prop isGetter:(BOOL)isGetter {
        switch (prop.type.type) {
            case TypeChar:
                return ACCESSOR_BLOCK(isGetter, char, charValue);
            case TypeInt:
                return ACCESSOR_BLOCK(isGetter, int, intValue);
            case TypeShort:
                return ACCESSOR_BLOCK(isGetter, short, shortValue);
            case TypeLong:
                return ACCESSOR_BLOCK(isGetter, long, longValue);
            case TypeLongLong:
                return ACCESSOR_BLOCK(isGetter, long long, longLongValue);
            case TypeUnsignedChar:
                return ACCESSOR_BLOCK(isGetter, unsigned char, unsignedCharValue);
            case TypeUnsignedInt:
                return ACCESSOR_BLOCK(isGetter, unsigned int, unsignedIntValue);
            case TypeUnsignedShort:
                return ACCESSOR_BLOCK(isGetter, unsigned short, unsignedShortValue);
            case TypeUnsignedLong:
                return ACCESSOR_BLOCK(isGetter, unsigned long, unsignedLongValue);
            case TypeUnsignedLongLong:
                return ACCESSOR_BLOCK(isGetter, unsigned long long, unsignedLongLongValue);
            case TypeFloat:
                return ACCESSOR_BLOCK(isGetter, float, floatValue);
            case TypeDouble:
                return ACCESSOR_BLOCK(isGetter, double, doubleValue);
            case TypeBool:
                return ACCESSOR_BLOCK(isGetter, BOOL, boolValue);
            case TypeObject:
            case TypeClass:
                return isGetter? GETTER_BLOCK(self) : (id)^(Movie *self, id val) {
                    self.storage[prop.name] = val;
                };
            case TypeCString:
                return ACCESSOR_BLOCK(isGetter, const char *, UTF8String);
            case TypeSelector:
                return isGetter?
                (id)^(Movie *self) {
                    id val = self.storage[prop.name];
                    return val? NSSelectorFromString(val) : nil;
                }
                : (id)^(Movie *self, SEL val) {
                    self.storage[prop.name] = val? NSStringFromSelector(val) : nil;
                };
            case TypeVoid:
            case TypeArray:
            case TypeStruct:
            case TypeUnion:
            case TypeBitField:
            case TypePointer:
            case TypeUnknown:
            default:
                return nil;
        }
    }
    

    Each type is handled in a manner quite similar to that in the KVC article:

    • For scalars we just need to pass the correct type and unboxing method.
    • Objects and Class types are just put straight into the dictionary as-is. We’re use self as the “unboxing method” for objects here, since that just returns the object itself.
    • Both C strings and selectors can be boxed into an NSString
    • We’ll skip all the uncommon types that aren’t supported by properties anyway.

    Struct properties

    The main difference here is the support of structs.

    When implementing KVC, we could handle structs in a special way because the method implementations already existed, we just had to figure out how to call them. But here we’re creating the implementations, and it’s not trivial to create a function that can accept a struct parameter of any size.

    It would be possible to support specific struct types (like CGSize or CGRect) but not in a generic way for all structs. So for now we’ll leave them out, since it would depend on your actual needs to how these would be handled.

    Putting it all together

    To finish up, let’s rewrite resolveInstanceMethod to use our new blockForAccessor method:

    + (BOOL)resolveInstanceMethod:(SEL)sel {
        ClassProperty *prop = [self propertyForSelector:sel];
        if (prop == nil) {
            NSLog(@"Couldn't resolve method for selector %@",
                  NSStringFromSelector(sel));
            return NO;
        }
    
        BOOL isGetter = (sel == prop.getter);
    
        id block = [self blockForAccessor:prop isGetter:isGetter];
        if (block == nil) {
            NSLog(@"Couldn't implement method for selector %@",
                  NSStringFromSelector(sel));
            return NO;
        }
    
        NSString *typeEncoding = isGetter
            ? [prop.type.encoding stringByAppendingString:@"@:"]
            : [@"v@:" stringByAppendingString:prop.type.encoding];
    
        IMP imp = imp_implementationWithBlock(block);
    
        return class_addMethod(self, sel, imp, typeEncoding.UTF8String);
    }
    

    Wrapping up

    In this article we’ve covered some core functionality of the Objective-C runtime: resolving method implementations dynamically. This is a really useful technique for creating many accessors or methods in a generic way without a lot of repetitive code.

    To make our dictionary-backed object more useful, we could extract it into a superclass which any object could inherit from. I’ve done this in the sample code for this article, creating a DictionaryBackedObject class.

    ]]>
    https://ko9.org/posts/safe-json-parsing/Parsing JSON SafelyHow to ensure that a malformed network payload can never crash your app.https://ko9.org/posts/safe-json-parsing/Sun, 14 Feb 2021 14:30:00 +0100In this article we’re going to look at how to make sure that your app can handle an unexpected server response correctly. We’ll start with a simple, type-safe way to extract data from a payload, and later we’ll use the Objective-C runtime to instantiate our data models from JSON automatically.

    But first...

    A word on defensive programming

    I don’t just want to focus on how to prevent these specific crashes, but also to talk about the mindset of defensive programming.

    The stability and security of your app depends on how defensively you code against different kinds of potential bugs or vulnerabilities.

    Like a lot of things in life, defensiveness is a trade-off. You don’t want your app crashing all the time because you made a lot of optimistic assumptions, but you also don’t want to be overly paranoid and spend a lot of time protecting against situations that almost never happen.

    Some different defensive programming scenarios:

    • Should you add assertions to your code to ensure that required conditions are met? Yes. Assertions are cheap and effective at finding “this should never happen” problems.
    • Should you design your APIs to be simple, to have sensible defaults, and to disallow invalid usage? Sure. The earlier you can catch developer mistakes, the better. Assume no-one will read the documentation!
    • Should you validate user input before processing it? Definitely. Keep in mind that the vast majority of bad input will be typos or other innocent mistakes, so you don’t always have to be ultra-strict. Checking that a phone number has an area code is fine, but trying to validate email addresses with some hugely complicated regex is only going to block some real emails eventually.
    • How about defending against a malicious user with a jailbroken device? Usually not worth it. With physical access to unlocked hardware, all bets are off security-wise. There’s very little you can do to protect against this, so it’s rarely worth going to extreme lengths to do so. Just sticking with Apple’s security best practices is enough.
    • What about sanity checks on the OS, like making sure alloc/init doesn’t return nil? Nope. If memory allocation fails when creating a new object, then the device is out of memory or something else is very wrong, and your app is about to crash anyway. There’s next to nothing you can do to recover from this, so why spend time protecting against it?
      • Should you ensure that the server is sending a response in the right format? Absolutely! Even if you control the server that your app talks to, you still shouldn’t blindly trust that it will always send what you expect. Server code and databases can change, and the developers making those changes might not be aware what format your app is expecting to receive.

      Let’s first look at an example of handling server responses in an unsafe manner, and then some ways we can improve on it.

      Unsafe payload processing

      Let’s say you’re implementing a login method in your app. The user enters their credentials, which are then sent to your server for validation. If successful, the server responds with a payload like this:

      {
          "id": 123,
          "name": "Joe User",
          "is_premium": true,
          "lang_skills": "objc,swift,python"
      }
      

      Seems pretty straightforward to parse — we might use something like this:

      NSUInteger userId = [payload[@"id"] unsignedIntegerValue];
      NSString *name = payload[@"name"];
      BOOL isPremium = [payload[@"is_premium"] boolValue];
      NSArray *langs = [payload[@"lang_skills"] componentsSeparatedByString:@","];
      

      However there are several ways this code could realistically fail at some point:

      1. The id field in your user databases changes from a number to a string, maybe to use UUIDs instead. NSString doesn’t have a method called unsignedIntegerValue... crash! 💥
      2. The server is rewritten in a new language which treats false as null. The is_premium field now sometimes contains NSNull, which of course crashes when you try to call boolValue on it.
      3. A backend developer changes the lang_skills field from a comma-separated string into an array of strings. Now the call to componentsSeparatedByString on an NSArray will crash.

      Even worse, maybe the scripting language used on your backend doesn’t use strict typing, and sends all kinds of unexpected values, such as 0 or null instead of empty strings.

      What’s the big deal?

      The failure scenarios above might seem contrived and unlikely — why would they change the data format without telling us? — but these are not just hypothetical. They may not happen often, but when they do they can potentially have a huge impact.

      In May 2020, Facebook made a small change to the backend data format for their mobile SDK. Subsequently, their SDK caused many major apps to start crashing immediately on launch. The developers of these crippled apps, which have a combined hundreds of millions of users, could do nothing until Facebook fixed the issue.

      The code in the Facebook SDK that parsed the payload looks like this:

      if (restrictiveParams[eventName][@"is_deprecated_event"]) {
          ...
      }
      

      The flawed assumption was that restrictiveParams[eventName] would always be a dictionary, so that using a subscript on it would be fine. The problem happened when that particular field was changed to a boolean on the server...

      -[__NSCFBoolean objectForKeyedSubscript:]: unrecognized selector

      Oops.

      This is definitely not defensive programming, and is frankly inexcusable for code this critical.

      Actually there are two bugs on this single line! It checks whether the is_deprecated_event value is nil or not, instead of testing whether it is true or false as a boolean value.

      The app that I work on also used to be plagued with these malformed payload problems, which caused millions of easily-preventable crashes over a few years.

      We can do better than this when parsing our payloads.

      Update

      Since I started writing this article, the same issue happened again in July 2020, where an unexpected payload caused millions more crashes in the Facebook SDK! 😳

      This time, it was an object that was assumed to be an NSDictionary but was actually NSNull, which of course crashed when count was called on this object — almost exactly like an example I gave above. Here’s the Github issue with details of the second incident.

      Simple type checking

      So how do we prevent this happening in our code? Well we can just check the type of each field before using it:

      NSUInteger userId = 0;
      id userIdVal = payload[@"id"];
      if ([userIdVal isKindOfClass:[NSNumber class]]) {
          userId = [userIdVal unsignedIntegerValue];
      } else {
          // report an error
      }
      

      But that would get tedious very quickly. Can we centralise this type checking logic? Why not encapsulate it into NSDictionary itself?

      Typed dictionary values

      Normally [NSDictionary objectForKey:] returns an id or nil, regardless of what type of object is stored with that key. We can create some extension methods to NSDictionary which will only return an object if it is a specific type, or nil otherwise.

      We’ll start with two methods to read strings and numbers:

      @interface NSDictionary (TypedValues)
      - (nullable NSString *)stringForKey:(NSString *)key;
      - (nullable NSNumber *)numberForKey:(NSString *)key;
      @end
      
      @implementation NSDictionary (TypedValues)
      
      - (NSString *)stringForKey:(NSString *)key {
          id val = self[key];
          return [val isKindOfClass:NSString.class]? val : nil;
      }
      
      - (NSNumber *)numberForKey:(NSString *)key {
          id val = self[key];
          return [val isKindOfClass:NSNumber.class]? val : nil;
      }
      
      @end
      

      Note that dictionary keys can be any object that conforms to NSCopying, but we’ll only accept strings because that’s all that JSON supports.

      Now we can parse the payload like this:

      NSUInteger userId = [payload numberForKey:@"id"].unsignedIntegerValue;
      NSString *name = [payload stringForKey:@"name"];
      BOOL isPremium = [payload numberForKey:@"is_premium"].boolValue;
      NSArray *langs = [payload stringForKey:@"lang_skills"] componentsSeparatedByString:@","];
      

      Just using these simple helper methods has made our JSON parsing much safer. Processing this response payload can now practically never crash our app, no matter what corrupted data the server throws at us (assuming that you checked that payload was actually a dictionary first).

      You still have to ensure that you didn’t get any unexpected nil values, but at least you’ve got the ability to check that and act on it, instead of assuming it’ll be fine.

      Strict versus lenient parsing

      The way we’ve implemented the helper methods is quite strict when it comes to data types. Meaning that the numberForKey method will only return a value if it is represented in the JSON as a number type.

      But what if the server starts returning numeric user IDs encoded in strings? For example “123” instead of 123. Is that reason enough to fail the parsing, or could we be a little more lenient? For numbers, we could attempt to extract a numeric value from the payload, regardless of the actual format, like so:

      - (NSInteger)integerForKey:(NSString *)key {
          return [self numberForKey:key].integerValue ?:
                  [self stringForKey:key].integerValue;
      }
      

      This method will first test if the JSON value is a number, otherwise check for a string and parse that as an integer. If neither is true, zero will be returned, which might be a reasonable default for missing or invalid data in that field.

      Sometimes however this “looser” parsing can have unexpected side effects. Say the user ID field contained the value “123ABC”. You would probably want your app to raise an error because this format is unknown, but integerForKey would happily return 123 and discard the rest of the string.

      So there isn’t one perfect solution to decide between strict or lenient parsing, it depends on your particular situation. It might be that strict parsing is better for you because it could highlight problems earlier, instead of silently handling them.

      Note that Swift’s JSONDecoder is very strict, and will fail unless it finds the exact data type it expected. If our payload above contained “is_premium”: 1, this would throw an exception in Swift because it only accepts true or false for boolean fields.

      Automated safe parsing

      Typed dictionary access is a good, simple solution for a small app, or when you can ensure that payloads will only ever be used via these methods. For the app I work on, using typed access resulted in a huge drop in the number of crashes. But people aren’t perfect and habits are hard to break, and over time more unsafe parsing crept back into the code.

      I figured that the risk could be eliminated entirely if the network layer prevented any access to the raw JSON payloads.

      What if, instead of manually having to parse the response JSON ourselves, we declared the payload format as a data model? So for the login example above, we would have:

      @interface LoginResult: JSONModel
      @property (readonly) NSUInteger userId;
      @property (readonly) NSString *name;
      @property (readonly) BOOL isPremium;
      @property (readonly) NSString *languageSkills;
      @end
      

      The JSONModel superclass should be able to automatically instantiate any subclass from a JSON payload for us. Our network API would change from something like this:

      - (void)request:(NSURL *)url
           completion:(void(^)(id result))completion;
      

      To instead return a JSONModel subclass which we specify:

      - (void)request:(NSURL *)url
               asType:(Class)resultType   // must be a subclass of JSONModel
           completion:(void(^)(__kindof JSONModel *result))completion;
      

      By doing this we’ll have completely wiped out payload crashes, since it will no longer be possible to access the raw JSON in an unsafe way.

      So how can we automatically create an object from a JSON structure? Well as you might have guessed, we can do that using the Objective-C runtime to introspect the properties of the class. 👍

      Implementing JSONModel

      We’ll be “filling in” a new JSONModel subclass instance with the JSON data using these steps:

      1. Introspect the subclass to find out which properties it has
      2. For each property, check if the JSON payload has a field with the same name
      3. Check that the property type matches the JSON value
      4. If the name and type matches, set the property value

      The introspection step would normally be the trickiest part, but luckily we already solved that in Inspecting Objective-C Properties 😄

      We’ll be using the ClassProperty class from that article, and also TypeEncoding from Type Encodings Explained, so make sure you understand how they work first.

      Our JSONModel class will need a method to create an instance from a JSON dictionary. It should return nil if the JSON object does not contain all the values of the correct types.

      @implementation JSONModel
      
      + (instancetype)parsedFromJSON:(id)jsonObj {
          if ([jsonObj isKindOfClass:NSDictionary.class] == NO) {
              return nil;
          }
          NSDictionary *jsonDict = (NSDictionary *)jsonObj;
      
          // Create a new instance of the subclass to fill in
          JSONModel *obj = [self new];
      
          // Introspect our properties
          NSArray<ClassProperty *> *props = [self classProperties];
      

      That’s step 1 done. Now we have to iterate over each property and see if there’s a corresponding JSON value with the same type. Objects parsed by NSJSONSerialization can only return a small number of types, so we don’t need to handle tons of cases like we did in Implementing KVC.

      The possible types in JSON are NSDictionary, NSArray, NSString, NSNumber and NSNull. For now, we’ll just check if the value type and our property type are the exact same kind of class, and if so, use KVC to set the value:

          for (ClassProperty *prop in props) {
              id jsonVal = jsonDict[prop.name];
              if (jsonVal == nil) {
                  NSLog(@"No value found for property %@", prop.name);
                  return nil;
              }
      
              if (prop.type.classType && [jsonVal isKindOfClass:prop.type.classType]) {
                  [obj setValue:jsonVal forKey:prop.name];
              } else {
                  NSLog(@"Couldn't load value for %@", prop.name);
              }
          }
          
          return obj;
      }
      

      That’s basically all there is to automatic JSON parsing! But we’re not quite there yet. If you tried running this with our example login result payload above with [LoginResult parsedFromJSON:], it wouldn’t work.

      This is because userId and isPremium are not class types, but scalars (NSUInteger and BOOL), so would fail when we test for prop.type.classType. We’ll need to handle scalar types specially.

      Supporting scalar types

      We can check if a property has a simple numeric type by testing if isIntType (which includes BOOL) or isFloatType on prop.type. However, this value would be parsed from the JSON payload as an NSNumber, so how can we convert that to say, NSUInteger userId? Do we need to handle each of the dozen scalar types after all?

      Well KVC handles unboxing of scalar values automatically, so again we’re in luck! We can simply call setValue:forKey: with the NSNumber and it will convert it to the correct scalar type for us.

      Let’s add another case into that type check before the else:

              } else if ((prop.type.isIntType || prop.type.isFloatType)
                          && [jsonVal isKindOfClass:NSNumber.class]) {
                  [obj setValue:jsonVal forKey:prop.name];
              } else {
                  // ...
      

      So now we’re able to handle JSON values of any class or scalar type.

      The LoginResult object is still not being parsed correctly though. The error message is “No value found for property userId”.

      Oh wait... the field name in the JSON payload is “id”, not “userId” 😅

      We’ll need to support properties that have different names to the JSON keys.

      Mapping JSON fields to properties

      Custom conversion between payload keys and model fields is quite common, since JSON keys typically use snake_case and ObjC properties are camelCase. Swift’s Codable provides this functionality explicitly via the CodingKeys enum, or automatically as an option of JSONDecoder. We can do it with a simple string-to-string mapping.

      This mapping will be specific to each JSONModel subclass, so we could implement it using a class method which can be overridden:

      + (NSString *)jsonKeyForProperty:(NSString *)propName {
          // don't do any mapping by default
          return propName;
      }
      

      Now we can change this line from above:

      // id jsonVal = jsonDict[prop.name];
      
      NSString *jsonKey = [self jsonKeyForProperty:prop.name];
      id jsonVal = jsonDict[jsonKey];
      

      In our LoginResult class, we can specify the mapping like so:

      @implementation LoginResult
      
      + (NSString *)jsonKeyForProperty:(NSString *)propName {
          NSDictionary *mapping = @{
              @"userId": @"id",
              @"isPremium": @"is_premium",
              @"languageSkills": @"lang_skills",
          };
          return mapping[propName] ?: [super jsonKeyForProperty:propName];
      }
      
      @end
      

      Now calling [LoginResult parsedFromJSON:] will return a fully populated LoginResult object!

      NSDictionary *payload = @{
          @"id": @123,
          @"name": @"Joe User",
          @"is_premium": @YES,
          @"lang_skills": @"objc,swift,python",
      };
      
      LoginResult *result = [LoginResult parsedFromJSON:payload];
      
      for (ClassProperty *prop in LoginResult.classProperties) {
          NSLog(@"%@ = %@", prop.name, [result valueForKey:prop.name]);
      }
      
      // Prints:
      //
      // userId = 123
      // name = Joe User
      // isPremium = 1
      // languageSkills = objc,swift,python
      

      Setting readonly properties

      Sharp-eyed readers might be asking here “hang on, the properties in LoginResult are readonly, how can setting their values like this work??” 👀

      Good question! As it turns out, KVC has another handy trick, which is to set the ivar directly if no setter method exists:

      If no simple accessor is found, and if the class method accessInstanceVariablesDirectly returns YES, look for an instance variable with a name like _<key>, _is<Key>, <key>, or is<Key>, in that order. If found, set the variable directly with the input value (or unwrapped value) and finish.

      Key-Value Coding Programming Guide

      This means that when we call setValue:forKey: on a read-only property, KVC will notice there is no matching setter, and put the value straight into the ivar backing the property instead. So it turns out that “read only” doesn’t mean much and you can write to any readonly property from outside a class 😱

      Of course, you can break all kinds of things when you use a dynamic language like Objective-C if you really want to. In this case, you can just override accessInstanceVariablesDirectly to return NO.

      Wrapping up

      In this article we’ve implemented a basic JSON parser using runtime reflection, but there are other handy features you could add:

      • Support for lenient decoding, such as extracting numbers from strings, or skipping a property if a matching value is not found.
      • Other data types such as NSURL or NSDate can be automatically parsed from strings and numbers in the JSON.
      • Safe parsing of typed arrays, for example testing that a NSArray<NSString *> property actually contains only strings. This one is a little tricker, because details of generics are not available to the runtime, so you’d have to specify what type an array is meant to contain in the model.
      • Support for nested objects. If the type of a property happens to be a subclass of JSONModel, we should automatically populate that from a nested JSON dictionary.
      • Error handling for different situations, like when a JSON value is found but it’s the wrong type.
      • More robust property setting, like using try/catch around setValue:forKey:.

      If any Facebook engineers are reading, feel free to use this in your SDKs… in fact we’d really appreciate if you did 😂

      ]]>
      https://ko9.org/posts/state-machines/State Machines in SwiftImplementing a trivial state machine in Swift.https://ko9.org/posts/state-machines/Sun, 3 Jan 2021 14:20:00 +0100I recently needed to implement a state machine in a project and was pleasantly surprised to find how easy this is in Swift. Declaring state transitions is practically built into the syntax of the language!

      State machine

      Plenty of articles have been written about state machines in Swift, but I was a little disappointed that they all seemed to over-complicate matters with protocols, transition classes, sets of valid states, etc — even the ones touted as the “simplest” or “easiest” implementations.

      While it’s great to have these open source frameworks, in my opinion they miss the point that state machines in Swift are so simple, with so little boilerplate code, that importing a dependency in most cases is just unnecessary. In this article, I’ll describe a truly minimal implementation of a state machine in Swift.

      I won’t be putting any sample code on Github as usual, because the whole point is that state machines are easier to implement from scratch each time 😄

      A really simple state machine

      At their heart, state machines have just two things:

      1. A set of states
      2. A set of transitions between those states

      The first one is almost always implemented as an enum, because that’s the best way to represent a set of mutually exclusive options.

      Let’s model the states of a simple web browser (or anything that loads remote content):

      enum BrowserState {
          case empty      // initial state, nothing loaded yet
          case loading    // request sent, waiting for content
          case loaded     // successful, displaying content
          case error      // request failed
          case cancelled  // user cancelled the request
      }
      

      Now how should we define the valid transitions between states? In other languages like Objective-C, we might define a mapping between each state and those it can transition to, probably using NSDictionary and NSSet. Then to test if a transition was valid, we’d have to look up the current state in the map, and check if its set contained the target state.

      In Swift however, this becomes much simpler with enum case pattern matching. We can create a tuple of the current and next state, then match it against all the valid transitions:

      extension BrowserState {
          func canTransition(to state: BrowserState) -> Bool {
              switch (self, state) {
                  case (.empty, .loading),        // start load
                       (.loading, .loaded),       // success
                       (.loading, .error),        // failure
                       (.loading, .cancelled),    // user cancel
                       (.error, .loading),        // retry
                       (.cancelled, .loading):    // retry
                      return true
                  default:
                      return false
              }
          }
      }
      

      That’s it! 🤩

      There is almost no boilerplate code or common logic, which to my mind, is why capturing this in a generic state machine implementation doesn’t add much value.

      The handling of transition events and invalid states is also specific to each use case. Here’s one example of how we might use our BrowserState in a browser class:

      class Browser {
          var state: BrowserState = .empty {
              willSet {
                  precondition(state.canTransition(to: newValue),
                      "Invalid transition: \(state) -> \(newValue)")
              }
          }
      }
      

      In just a few lines of code, we’ve set the initial state of the browser and made it impossible to transition to an invalid state.

      Of course, you might want to fail a little more gracefully than by crashing the app. We can use transition functions to define custom behaviour while changing states:

      extension Browser {
          func load() {
              if state.canTransition(to: .loading) {
                  state = .loading
                  // send request, then set state
                  // to either .loaded or .error
              }
          }
      
          func cancel() {
              if state.canTransition(to: .cancelled) {
                  state = .cancelled
              }
          }
      }
      

      These can be tailored to your Browser class, depending on whether you want to handle invalid transitions with assertions, exceptions, returning a BOOL, printing a message, etc.

      Attaching values to state

      A very handy feature of Swift is being to attach values to enum cases, which means that our state machine can also capture more information if necessary.

      In reality, our Browser class would need to keep track of all kinds of extra state: the URL being loaded, the content to display, or any network error that occurred. It would make a lot of sense to attach these details to the BrowserState itself, since each value only applies in certain states:

      enum BrowserState {
          case empty
          case loading(URL)
          case loaded(content: String)
          case error(Error)
          case cancelled
      }
      

      The best part is that our canTransition function doesn’t need to change, because pattern matching can ignore attached values.

      If you wanted to have code executed before entering or exiting certain states, you could even attach closures to those case values.

      Simpler transition declarations

      Another useful feature of Swift’s pattern matching is that you can match against wildcard cases. To demonstrate this, let’s use a more fun example of a state machine — the behaviour of an enemy in a game:

      enum EnemyState {
          case idle       // 😐
          case patrolling // 🚶‍♂️
          case suspicious // 👀
          case hunting    // 😠
          case attacking  // 🔫
          case dead       // 😵
      }
      

      With more states, there are a lot more possible transitions (30 in this case), but we shouldn’t have to list them all. If we take the idle case, there are a lot of potential transitions:

      • Starting a patrol ➞ patrolling
      • Hearing something ➞ suspicious
      • Finding a body ➞ hunting
      • Seeing the player ➞ attacking
      • Stealth killed by the player ➞ dead

      Instead of listing all of these, we can simply match against case (.idle, _), where _ matches any case.

      It turns out there are a few states that can transition to any other state, and any state can transition straight to dead:

      extension EnemyState {
          func canTransition(to state: EnemyState) -> Bool {
              switch (self, state) {
                  case (.idle, _),        // can jump to any state
                       (.patrolling, _),  // can jump to any state
                       (.suspicious, _),  // can jump to any state
                       (.hunting, .suspicious),   // didn't find anything
                       (.hunting, .attacking),    // found the player
                       (.attacking, .hunting),    // lost the player
                       (_, .dead):    // can be killed during any action
                      return true
                  default:
                      return false
              }
          }
      }
      

      Usually an existing state machine implementation would require you to list out each possible transition explicitly, which could be an unnecessary inconvenience.

      My advice would be to roll your own state machine from scratch — you get the full power of Swift’s syntax, can customise it to your exact use case, use fewer dependencies, and better understand what you code is doing. What’s not to like? 😉

      ]]>
      https://ko9.org/posts/implementing-kvc/Implementing Key-Value CodingUsing the Objective-C runtime to read and write property values dynamically.https://ko9.org/posts/implementing-kvc/Sat, 1 Aug 2020 18:34:00 +0200In this article we’ll see how Key-Value Coding (KVC) works internally, and implement a basic version of it ourselves using the Objective-C runtime. The ability to read and write properties dynamically is a useful tool to have in our reflection toolbox, and we’ll be making use of it in later articles.

      KVC is one of the core features of Foundation, and enables us to access the properties of an object indirectly. With KVC, we can get and set property values using the property names instead of their accessor methods.

      // Normal accessor usage:
      int i = person.age;
      person.age = 18;
      
      // With KVC:
      NSNumber *n = [person valueForKey:@"age"];
      [person setValue:@18 forKey:@"age"];
      

      There’s a few interesting points to note about KVC. The property name is a string, so we can construct these at run time and pass in any string as the key. Since we can pass any key, the compiler can’t check this at build time so KVC calls can fail, unlike synthesized accessor methods. Using an invalid key that can’t be mapped to a property will result in a run time exception.

      Also of interest is how KVC deals with property values without knowing what type they are.

      Boxing

      KVC has to work with properties of any type, not just objects. The only reasonable way to do this is to treat all values as id, which is the most generic type in Objective-C. Even non-objects like int need to be handled as id, so these scalar values are automatically wrapped or “boxed” into NSValue or its subclass, NSNumber. You can read about how boxing works in Apple’s KVC guide.

      You can see in the example above that valueForKey returned an NSNumber for age instead of an int. Likewise, we had to box the 18 into an NSNumber by writing it as @18.

      This is a necessary overhead when dealing with types dynamically. It also unfortunately means that we lose the type safety that the compiler would normally provide:

      NSString *a = person.age;
      // Compiler error: conversion of 'int' to 'NSString *'
      
      NSString *b = [person valueForKey:@"age"];
      // No problem! Until you try to use it...
      

      However we can easily check the type of the returned value if necessary, or use one of the NSNumber convenience methods like intValue to extract the type we want.

      How KVC works

      If we look at just the getter side first, calling valueForKey on an object does something like:

      1. Look for a property on that object with the same name as the key
      2. Find the actual implementation of the getter method for that property, which will be a C function
      3. Call the getter function using a return type the same as the property’s type
      4. Box up scalar values into NSValue or NSNumber
      5. Return the value

      The process for setValue:forKey: is very similar, with a few steps reversed.

      In step 1, KVC’s valueForKey actually searches for multiple getter methods that are variants of the key, and also has some special support for collection properties. Apple’s KVC also supports valueForKeyPath, which recursively calls valueForKey on a nested object.

      We won’t be adding any of these extra features here, because all of the interesting runtime stuff is in the basic valueForKey implementation.

      KVC also has the ability to get and set ivar values directly, in case it can’t find a matching property name or accessor method. While accessing ivars dynamically is done in a similar way, it’s a little more complicated than using property accessors, so we’ll tackle that in a separate article.

      Let’s jump in and start building our own KVC!

      The API

      Since the KVC methods are implemented on NSObject, we can’t just use the same method names on a new class. So while we’ll also add ours to a category on NSObject, we’ll name our methods a little differently. Our method names will also reflect that the “key” in this case has to be a property name.

      /// Returns the value of a property with the given name
      - (id)valueForProperty:(NSString *)propertyName;
      
      /// Sets a property with the given name to the specified value
      - (void)setValue:(id)value forProperty:(NSString *)propertyName;
      

      Implementing the KVC getter

      Step one of the implementation was to find a property matching the name that was passed in. For this, we’ll be using some code and the ClassProperty wrapper from the earlier article on Inspecting Properties:

      - (id)valueForProperty:(NSString *)propertyName {
      
          objc_property_t prop = class_getProperty(self.class,
                                                   propertyName.UTF8String);
      
          if (prop == NULL) {
              [NSException raise:NSInvalidArgumentException
                          format:@"Can't find property %@.%@",
                                 NSStringFromClass(self.class), propertyName];
          }
      
          ClassProperty *property = [[ClassProperty alloc] initWithProperty:prop];
      
          SEL getter = property.getter;
      

      Here we’re using class_getProperty to get the details of the property with the exact name specified, and throwing an exception if it doesn’t exist. Exceptions are not commonly used in Objective-C, and we could just return nil instead, but we’ll do it this way to match the behaviour of KVC.

      We’ll create a ClassProperty wrapper object to make it simpler to look up some details of the property, like the selector of the getter. Usually the selector just has the same name as the property, in which case we could just use NSSelectorFromString(propertyName), but ClassProperty also handles custom getter names for us.

      Getting the implementation function

      This next step is the real meat of KVC.

      Every property has a getter method and optional setter, and whether they are provided by us or generated by the compiler, they are all implemented as underlying C functions.

      We need to get a pointer to the actual method implementation so we can call into it. These implementation pointers are represented by the IMP type in Objective-C, and we can get one using the class_getMethodImplementation runtime function.

      This function can also fail, so we’ll throw an exception if the implementation doesn’t exist:

          IMP imp = class_getMethodImplementation(self.class, getter);
      
          if (imp == NULL) {
              [NSException raise:NSInternalInconsistencyException
                          format:@"Can't find implementation of %@.%@",
                          NSStringFromClass(self.class), propertyName];
          }
      

      Ok that wasn’t so hard. Now how do we use imp?

      Well since this is a C function pointer, we can just call it like imp()! 😯

      But as you might have guessed, it’s not quite so simple. The compiler has no idea what kind of parameters or return type this function has, so we have to explicitly cast it first.

      Calling the getter implementation

      Just like casting object pointers or scalars to other types, you can also cast function pointers. The syntax is a bit harder to read though.

      But what do need to cast it to? A getter method doesn’t take any arguments, and returns a value of the same type as the property. However, every Objective-C method does have two implicit arguments: self and _cmd (which is the method’s selector). These arguments are explicit for the underlying C function.

      Let’s take the person.age example from above. The getter method signature is:

      - (int)age;
      

      That means the signature of the underlying implementation function would be:

      int AgeGetter(id self, SEL _cmd);
      

      Note that the implementation would actually be an anonymous function without a name.

      So for an int property, we can cast imp to the correct type with:

      (int (*)(id, SEL))imp
      

      It might not be the easiest to read if you’re not familiar with function pointers, but the type is the same as the function signature above, except with (*) where the function name would be.

      If we assigned that to a properly-typed function pointer, then we could call it like so:

      int (*intGetterFunc)(id, SEL) = (int (*)(id, SEL))imp;
      
      int value = intGetterFunc(self, getter);
      

      Which would be exactly the same as calling the property getter normally, such as person.age.

      Supporting any property type

      That would be fine if we only needed to handle int properties, but KVC needs to handle (nearly) every property type. Unfortunately there’s no shortcut here – we have to handle every type individually.

      We can get the type using the TypeEncoding helper class from the previous article on Type Encodings, and then handle them all in a big switch statement:

      switch (property.type.type) {
          case TypeChar: {
              char (*charGetter)(id, SEL) = (char (*)(id, SEL))imp;
              char c = charGetter(self, getter);
              return [NSNumber numberWithChar:c];
          }
          case TypeInt: {
              int (*intGetter)(id, SEL) = (int (*)(id, SEL))imp;
              int i = intGetter(self, getter);
              return [NSNumber numberWithInt:i];
          }
          case TypeShort: {
              short (*shortGetter)(id, SEL) = (short (*)(id, SEL))imp;
              short s = shortGetter(self, getter);
              return [NSNumber numberWithShort:s];
          }
      

      You’ll notice very quickly that this looks quite repetitive. We’ll have to repeat this function casting, calling, and boxing for all thirteen scalar types.

      We can at least remove some of the boilerplate by defining a macro:

      #define RETURN_BOXED(TYPE) {                              \
          TYPE (*getterFunc)(id, SEL) = (TYPE (*)(id, SEL))imp; \
          TYPE val = getterFunc(self, getter);                  \
          return @(val);                                        \
      }
      

      The trailing backslashes join these lines together, since a macro must be defined on a single line.

      Note that we can just use the @() boxing syntax to create a NSNumber here, since the compiler knows what type we’re boxing by this point.

      Now we can replace all the scalar switch cases like so:

      switch (property.type.type) {
          case TypeChar:              RETURN_BOXED(char)
          case TypeInt:               RETURN_BOXED(int)
          case TypeShort:             RETURN_BOXED(short)
          case TypeLong:              RETURN_BOXED(long)
          case TypeLongLong:          RETURN_BOXED(long long)
          case TypeUnsignedChar:      RETURN_BOXED(unsigned char)
          case TypeUnsignedInt:       RETURN_BOXED(unsigned int)
          case TypeUnsignedShort:     RETURN_BOXED(unsigned short)
          case TypeUnsignedLong:      RETURN_BOXED(unsigned long)
          case TypeUnsignedLongLong:  RETURN_BOXED(unsigned long long)
          case TypeFloat:             RETURN_BOXED(float)
          case TypeDouble:            RETURN_BOXED(double)
          case TypeBool:              RETURN_BOXED(BOOL)
      

      The rest of the types we’ll handle case by case.

      After handling every scalar individually, object types are easy… they can all be treated the same! There’s also no boxing required, just return the result of the getter directly:

          case TypeObject:
          case TypeClass:
              return ((id (*)(id, SEL))imp)(self, getter);
      

      Properties with a type of Class can be handled just like any other object type.

      Less common property types

      This actually takes care of the vast majority of types that we’d typically use properties for.

      Some of our EncodedType cases we don’t have to handle at all. You can’t even define a property which is a C array or a bitfield, so we can ignore those completely.

      Other types are valid to use as properties, but KVC doesn’t support them: void, unions, function pointers and other non-object pointers. You’ll get a “not key value coding compliant” exception if you try to access them, so we’ll do the same. No big loss, since these these types are rarely (if ever) used for properties.

          case TypeVoid:
          case TypeArray:
          case TypeUnion:
          case TypeBitField:
          case TypePointer:
          case TypeUnknown:
              [NSException raise:NSInvalidArgumentException
                          format:@"Not KVC-compliant: %@.%@",
                          NSStringFromClass(self.class), propertyName];
      

      There are two more types which KVC doesn’t support, C strings and selectors. But we can support them! C strings can just be boxed into an NSString, and selectors can also be convert to strings:

          case TypeCString:
              RETURN_BOXED(char *)
      
          case TypeSelector: {
              SEL (*selGetter)(id, SEL) = (SEL (*)(id, SEL))imp;
              SEL sel = selGetter(self, getter);
              return NSStringFromSelector(sel);
          }
      

      Note we can use RETURN_BOXED here because the @() boxing syntax also works for C strings.

      So that’s all the types handled. Well, all except one…

      Getting struct values

      Structs are occasionally used as property types – think CGSize or NSRange – but they are considerably more difficult to handle. This is because we don’t know what kind of struct the getter should return, or how big it is. However, KVC supports struct properties, so we should be able to get it working too.

      With all the other types, we knew exactly what the getter function should return, so we could cast it correctly. If we only wanted to support say, CGRect we could do something like this:

      CGRect (*rectGetter)(id, SEL) = (CGRect (*)(id, SEL))imp;
      CGRect r = rectGetter(self, getter);
      return [NSValue valueWithCGRect:r];
      

      But that only handles one struct type. There’s plenty of others in Foundation and UIKit with varying sizes, not to mention user-defined structs that could be of any size. How can we handle those?

      The first thing we need to know is that with KVC, a struct is boxed into NSValue. While NSValue has a few handy methods like valueWithPoint and valueWithRect for handling specific structs, those are not useful to us because they aren’t generic enough. We need some way to construct an NSValue from any struct.

      Taking a look at the API, we see valueWithBytes:objCType:, which takes a pointer to a memory buffer, plus the type encoding of the value. We already have the type encoding, but how do we get a function to return a value into a memory buffer?

      There are two new classes which can help us here:

      1. NSMethodSignature. Describes the types of a method’s arguments and return value, including the size of the return value.
      2. NSInvocation. This is an object that encapsulates a whole method call. You specify what to use as self, which method selector to use, and which argument values to pass in. Then you can invoke the method, and most importantly, get the return value in a memory buffer.

      Looks like we have all the pieces we need to call a struct getter now. Note that we won’t be casting or calling imp in this case.

          case TypeStruct: {
              // allocate a buffer to hold the return value
              NSMethodSignature *sig = [self methodSignatureForSelector:getter];
              void *buffer = malloc(sig.methodReturnLength);
      
              // set up and call the invocation
              NSInvocation *invoc = [NSInvocation invocationWithMethodSignature:sig];
              invoc.selector = getter;
              [invoc invokeWithTarget:self];
              [invoc getReturnValue:buffer];
      
              // box the returned struct
              const char *encoding = property.type.encoding.UTF8String;
              NSValue *val = [NSValue valueWithBytes:buffer objCType:encoding];
              free(buffer);
              return val;
          }
      

      That’s quite a lot to take in! But no-one said runtime programming would be easy 😅

      So you might be asking yourself, if this handles any type, why couldn’t we just use it for all the boxed cases? After all, NSValue can hold any scalar type too.

      Good question, but there’s a couple of reasons why it’s worth handling the scalar cases individually:

      1. We can’t wrap scalars in an NSNumber in a generic way. The valueWithBytes:objCType: on the NSValue superclass won’t give us back an NSNumber. So our only choice is to use one of the specific initialisers like numberWithInt or numberWithDouble.
      2. Direct function calls are much faster than using NSInvocation. At least 10x faster from my rough benchmarking.

      The final step is to handle any unknown types after the switch, which we can do by throwing an exception, using an assert, or just returning nil.

      Now we just need to implement the other half of KVC: setValue:forProperty:.

      Setting property values

      This part is quite similar to what we’ve already done above, so we’ll go through it more quickly. The first part is getting the IMP for a property setter:

      - (void)setValue:(id)value forProperty:(NSString *)propertyName {
      
          objc_property_t prop = class_getProperty(self.class,
                                                   propertyName.UTF8String);
      
          if (prop == NULL) {
              [NSException raise:NSInvalidArgumentException
                          format:@"Can't find property %@.%@",
                          NSStringFromClass(self.class), propertyName];
          }
      
          ClassProperty *property = [[ClassProperty alloc] initWithProperty:prop];
      
          SEL setter = property.setter;
      
          if (property.isReadOnly || setter == NULL) {
              [NSException raise:NSInvalidArgumentException
                          format:@"Can't set read-only property %@.%@",
                          NSStringFromClass(self.class), propertyName];
          }
      
          IMP imp = class_getMethodImplementation(self.class, setter);
          if (imp == NULL) {
              [NSException raise:NSInternalInconsistencyException
                          format:@"Can't find setter of %@.%@",
                          NSStringFromClass(self.class), propertyName];
          }
      

      This is almost identical to the start of valueForProperty, with an additional check for read-only properties. It could be extracted into a method called impForProperty:isGetter: if we wanted.

      The next part also looks very familiar — we’ll need to handle each property type individually by using a switch. This time though, we have to cast the IMP to a setter function, which for our age property, has the form:

      void AgeSetter(id self, SEL _cmd, int newValue);
      

      The other difference is that this time, we need to unbox the scalar from an NSValue, using the correct method for the type such as intValue. We’ll define another macro to reduce code duplication.

      #define SET_UNBOXED(TYPE, METHOD) {                                   \
          void (*setterFunc)(id, SEL, TYPE) = (void (*)(id, SEL, TYPE))imp; \
          setterFunc(self, setter, [value METHOD]);                         \
          break;                                                            \
      }
      
      switch (property.type.type) {
          case TypeChar:             SET_UNBOXED(char, charValue)
          case TypeInt:              SET_UNBOXED(int, intValue)
          case TypeShort:            SET_UNBOXED(short, shortValue)
          case TypeLong:             SET_UNBOXED(long, longValue)
          case TypeLongLong:         SET_UNBOXED(long long, longLongValue)
          case TypeUnsignedChar:     SET_UNBOXED(unsigned char, unsignedCharValue)
          case TypeUnsignedInt:      SET_UNBOXED(unsigned int, unsignedIntValue)
          case TypeUnsignedShort:    SET_UNBOXED(unsigned short, unsignedShortValue)
          case TypeUnsignedLong:     SET_UNBOXED(unsigned long, unsignedLongValue)
          case TypeUnsignedLongLong: SET_UNBOXED(unsigned long long, unsignedLongLongValue)
          case TypeFloat:            SET_UNBOXED(float, floatValue)
          case TypeDouble:           SET_UNBOXED(double, doubleValue)
          case TypeBool:             SET_UNBOXED(BOOL, boolValue)
          case TypeObject:
          case TypeClass:            SET_UNBOXED(id, self)
          case TypeCString:          SET_UNBOXED(const char *, UTF8String)
          case TypeSelector: {
              void (*setterFunc)(id, SEL, SEL) = (void (*)(id, SEL, SEL))imp;
              setterFunc(self, setter, NSSelectorFromString(value));
              break;
          }
          case TypeVoid:
          case TypeArray:
          case TypeUnion:
          case TypeBitField:
          case TypePointer:
          case TypeUnknown:
              [NSException raise:NSInvalidArgumentException
                          format:@"Not KVC-compliant: %@.%@",
                          NSStringFromClass(self.class), propertyName];
      

      Notice that objects, classes, and C strings can also be handled using SET_UNBOXED. Because we’re just calling any method to unbox the value, we can call UTF8String to “unbox” an NSString to a C string. We can also use a small trick to do this with objects… calling self on any object returns the same object.

      There’s no real need to do any type checking on value here (like making sure it’s an NSNumber), since trying to call these unboxing methods on the wrong type of object will result in a runtime exception anyway.

      Finally, the setter for struct properties.

      Setting struct values

      This almost looks the same as for getters, except that NSMethodSignature doesn’t have an equivalent of methodReturnLength for the parameter sizes. Luckily there’s another runtime function we can use, NSGetSizeAndAlignment, which will calculate the size of any type based on its type encoding.

      Otherwise, it’s just the reverse of above: we unbox the NSValue into a buffer, and set that buffer as the third argument on the NSInvocation (with self and _cmd being the first two).

          case TypeStruct: {
              // get the size of the struct parameter
              const char *encoding = property.type.encoding.UTF8String;
              NSUInteger size;
              NSGetSizeAndAlignment(encoding, &size, NULL);
      
              // allocate a buffer and copy the value into it
              void *buffer = malloc(size);
              [value getValue:buffer size:size];
      
              // set up and call the invocation
              NSMethodSignature *sig = [self methodSignatureForSelector:setter];
              NSInvocation *invoc = [NSInvocation invocationWithMethodSignature:sig];
              invoc.selector = setter;
              [invoc setArgument:buffer atIndex:2];
              [invoc invokeWithTarget:self];
      
              free(buffer);
              break;
          }
      }
      

      Wrapping up

      That’s it! We’ve built the main functionality of Key-Value Coding.

      While you may not need to actually use a custom implementation like this, it’s useful to understand how to read and write properties dynamically.

      Like many features of Objective-C, you find there’s not much magic to it when you lift the curtain, but just different edge cases that need to be handled properly.

      ]]>
      https://ko9.org/posts/encode-types/Type Encodings ExplainedThe @encode directive and how Objective-C encodes type details.https://ko9.org/posts/encode-types/Sun, 5 Jul 2020 17:23:00 +0200In this article we’re going to look into Objective-C’s type encodings, and then we’ll then build a small convenience class to parse these encodings so we can use them more easily.

      Type encodings are compact string codes that describe the details of a type, and are compiled into the metadata of your app. We touched on them in Inspecting Objective-C Properties, where they appeared in the type attribute of each property.

      Every type has an encoding, and although they are always represented as C strings, most of them are just a single character. For example, int types are represented by i, and double is d.

      The other scalar types are just as straightforward and don’t need much explanation, but you can find the full list in Apple’s Runtime Programming Guide.

      Type encodings are also available for Objective-C specific types, such as:

      • id pointers encode as @
      • Class encodes as #
      • SEL (selectors) encode as :

      Some encodings are more than just a single character and include extra information.

      More complex encodings

      Object types

      While plain id types are represented as @, an object of a specific class like NSString has the class name appended in quotes: @"NSString".

      Even protocols are included in the encoding if the type is declared as such. Something like id<NSCopying> encodes to @"<NSCopying>".

      Block types

      Blocks are treated like objects, but their type encodings do not include any information about the block parameters or return type. Every block type is represented as @?, which combines the encodings for “object” and “function pointer”.

      Getting the type encodings for the parameters of a block is quite a bit more complicated, but it is possible. The internal details of blocks are documented in the Block Implementation compiler spec, and you can find some example code which will extract them for you.

      Compound types

      Structs and unions are encoded with the types of all of their member fields. For example, CGSize, which is defined as:

      struct CGSize {
          CGFloat width;
          CGFloat height;
      };
      

      Has a type encoding of {CGSize=dd}.

      Note that in 64-bit apps, CGFloat is defined as double (not float) which is why the two fields have a type of d.

      This works even for nested structs like CGRect, which is a combination of a CGPoint origin and a CGSize. The type encoding for CGRect is:

      {CGRect={CGPoint=dd}{CGSize=dd}}

      Depending on where the type encoding comes from, it can sometimes even include the member names as well:

      {CGRect="origin"{CGPoint="x"d"y"d}"size"{CGSize="width"d"height"d}}

      Other C types

      The remaining types in the list like void, C strings, C arrays, bitfields, function pointers and non-object pointers are rarely used in Objective-C, so we won’t go into more detail on them here.

      Instead, lets take a look at what encodings are used for.

      Where type encodings are used

      In the Objective-C runtime, type encodings are returned by several functions for describing the types of:

      • Properties (property_getAttributes)
      • Ivars (ivar_getTypeEncoding)
      • Method parameters and return types (method_getTypeEncoding)

      They are also used by some Foundation classes like NSMethodSignature, which wraps the method-related runtime functions. The NSValue class uses an encoding to keep track of the type of value it is wrapping, which is why you need to initialise it with an objCType parameter.

      To get the encoding of any type, we use the @encode compiler directive. For example:

      NSLog(@"%s", @encode(CGSize));
      
      // Prints: {CGSize=dd}
      

      Note that we need to use a type here, not a variable. To get the encoding of a variable, wrap it in the typeof() operator first.

      A small gotcha is that the @encode directive can return slightly different encodings than the runtime does. This is why the encoding of a struct might include the member names: @encode does not add them, but ivar_getTypeEncoding will. Not a big problem, but it can make it more complicated to compare type encodings.

      A wrapper for type encodings

      Let’s create a convenience class for dealing with type encodings, so we don’t have to parse C strings every time.

      Firstly, we can turn the type encodings table into an enum:

      typedef NS_ENUM(char, EncodedType) {
          TypeChar              = 'c',
          TypeInt               = 'i',
          TypeShort             = 's',
          TypeLong              = 'l',  // note: long encodes to 'q' on 64 bit
          TypeLongLong          = 'q',
          TypeUnsignedChar      = 'C',
          TypeUnsignedInt       = 'I',
          TypeUnsignedShort     = 'S',
          TypeUnsignedLong      = 'L',
          TypeUnsignedLongLong  = 'Q',
          TypeFloat             = 'f',
          TypeDouble            = 'd',
          TypeBool              = 'B',  // note: BOOL encodes to 'c' on 64 bit
          TypeVoid              = 'v',
          TypeCString           = '*',
          TypeObject            = '@',
          TypeClass             = '#',
          TypeSelector          = ':',
          TypeArray             = '[',
          TypeStruct            = '{',
          TypeUnion             = '(',
          TypeBitField          = 'b',
          TypePointer           = '^',
          TypeUnknown           = '?',
      };
      

      We’ll just use the first character of the encoding, since it’s enough to tell us which type it is.

      You might think “shouldn’t we use @encode to get these instead of hardcoding them?”. Well yes, technically, but these haven’t changed in decades and are very unlikely to change in the future. Since hardcoding them makes our life easier, I’m prepared to compromise in this particular case 🙂

      Now let’s define an interface with some properties of encodings that would be handy to have:

      @interface TypeEncoding : NSObject
      
      @property (nonatomic, readonly) EncodedType type;
      
      /// The raw type encoding
      @property (nonatomic, readonly, copy) NSString *encoding;
      
      /// If the type is either an object, a Class type, or a block
      @property (nonatomic, readonly) BOOL isObjectType;
      
      /// If the type is float or double
      @property (nonatomic, readonly) BOOL isFloatType;
      
      /// If the type is a signed or unsigned integer of any size
      @property (nonatomic, readonly) BOOL isIntType;
      
      /// The class of the object type. Nil for anything except TypeObject.
      @property (nonatomic, readonly, nullable) Class classType;
      
      - (instancetype)initWithEncoding:(NSString *)encoding;
      
      @end
      

      We’ll implement this by extracting the EncodedType straight off the start of the encoding string:

      @implementation TypeEncoding
      
      - (instancetype)initWithEncoding:(NSString *)encoding {
          self = [super init];
          if (self) {
              _encoding = [encoding copy];
              _type = [encoding characterAtIndex:0];
      
              static const unichar TypeConst = 'r';
              if (_type == TypeConst) {
                  // const C strings are encoded as "r*", skip the 'r'
                  _type = [encoding characterAtIndex:1];
              }
          }
          return self;
      }
      

      We could set all the properties right in init, but we’ll extract those into separate getters. The isObjectType and isFloatType methods just test if the EncodedType matches certain values:

      - (BOOL)isObjectType {
          return _type == TypeObject || _type == TypeClass;
      }
      
      - (BOOL)isFloatType {
          return _type == TypeFloat || _type == TypeDouble;
      }
      

      We could explicitly check for each of the eleven integer types (including BOOL), but another way would be to put all the integer types in an array and test if it contains our value. Since EncodedType is a character, the array of types just becomes a C string:

      - (BOOL)isIntType {
          static const char *integralTypes = "cislqCISLQB";
          return strchr(integralTypes, _type) != NULL;
      }
      

      The classType method needs a little string manipulation to extract the class name (if present) out of the type encoding. So an encoding like @"NSURL" will return NSURL, and id will default to NSObject.

      - (Class)classType {
          if (_type == TypeObject
              && [_encoding hasPrefix:@"@\""] && [_encoding hasSuffix:@"\""]) {
              NSRange range = NSMakeRange(2, _encoding.length - 3);
              NSString *classStr = [_encoding substringWithRange:range];
              return NSClassFromString(classStr) ?: NSObject.class;
          }
          return nil;
      }
      
      @end
      

      Wrapping up

      We could add some more functionality to our TypeEncoding class, such as extracting protocol names or the names and member types of structs, but we can leave that until we need it.

      One place this wrapper would already be useful is on ClassProperty from the previous post. The encodeType property can be changed from NSString to TypeEncoding and initialised as:

      _encodeType = [[TypeEncoding alloc] initWithEncoding:attribDetail];
      

      TypeEncoding will also come in very handy for future posts, when we need to get and store the values of properties and ivars dynamically.

      ]]>
      https://ko9.org/posts/inspecting-objective-c-properties/Inspecting Objective-C PropertiesUsing the Objective-C runtime to inspect properties of a class, and creating a convenience wrapper to work with them.https://ko9.org/posts/inspecting-objective-c-properties/Sun, 14 Jun 2020 10:04:00 +0200In this article we’re going to use the Objective-C runtime for some basic introspection: getting the properties of a class, including the attributes of each property (like readonly or weak). Then we’ll create a wrapper class so we can work with property attributes more easily, without needing to use the C functions. Finally we will look at how this all works with Swift.

      Getting property names

      Take an example Person class:

      @interface Person: NSObject
      @property (nonatomic, copy) NSString *name;
      @property (nonatomic) NSDate *dateOfBirth;
      @property (nonatomic, readonly) int age;    // computed from dateOfBirth
      @end
      

      In a running app, each property of a class is represented by the opaque type objc_property_t. We can’t use it directly, but it gets returned from and passed into the property-related runtime functions.

      The runtime lets us get the details of a single property or all the properties of a class at once. To use any of these functions, we’ll need to first @import ObjectiveC.runtime.

      Getting the name of a single property

      We’ll get the name of the dateOfBirth property first, using the class_getProperty and property_getName functions.

      Note that runtime functions deal with C strings, not NSString. This means that they’re declared as const char *, don’t have a leading @, and are printed with %s instead of %@.

      objc_property_t prop = class_getProperty(Person.class, "dateOfBirth");
      const char *propName = property_getName(prop);
      NSLog(@"%s", propName);
      
      // Prints: dateOfBirth
      

      Not very exciting so far, since we already had the property name 😁

      But what if we could get all the properties of the Person class without knowing the names? We can with class_copyPropertyList.

      Getting the names of all the properties

      When we call class_copyPropertyList, we need to pass in a pointer to an unsigned int, which it will set to the number of properties found. It will return a pointer to the first item in an array of objc_property_t. Since the method name contains “copy”, we’re taking ownership of the array and need to free it when we’re done.

      uint propCount;
      objc_property_t *props = class_copyPropertyList(Person.class, &propCount);
      for (uint n = 0; n < propCount; n++) {
          const char *propName = property_getName(props[n]);
          NSLog(@"%d: %s", n, propName);
      }
      free(props);
      
      // Prints:
      //  0: name
      //  1: dateOfBirth
      //  2: age
      

      Now this is getting a bit more interesting, but we really want to get all the details of each property, not just the names.

      Getting property attributes

      We’ll expand on the last example by adding another function call, to property_getAttributes:

      uint propCount;
      objc_property_t *props = class_copyPropertyList(Person.class, &propCount);
      for (uint n = 0; n < propCount; n++) {
          const char *propName = property_getName(props[n]);
          const char *propAttribs = property_getAttributes(props[n]);
          NSLog(@"%d: %s - %s", n, propName, propAttribs);
      }
      free(props);
      
      // Prints:
      //  0: name - T@"NSString",C,N,V_name
      //  1: dateOfBirth - T@"NSDate",&,N,V_dateOfBirth
      //  2: age - Ti,R,N
      

      You might be thinking, what the heck are those attribute strings? 🤔

      They’re a compact encoding which contain all of the details of a property, and are included in the compiled binary. Apple’s docs on Declared Properties have a full description of the format, but the short version is:

      • They always start with a T plus the encoded type of the property:
        • @ for an object, followed by the quoted name of the class if available
        • i for int
        • d for double
        • the full list can be found in Type Encodings
      • The rest is a comma-separated list of flags:
      • The final flag is V with the name of the backing ivar, if it has one

      Unavailable property attributes

      Not all of the details of a property can be accessed at run time. The missing ones are:

      1. Nullability. There is no way to ask the runtime whether a property is nullable or nonnull
      2. Generics. If you have a property of type NSArray<NSString *> *, the attributes string will only say that you have an NSArray, but not which type of objects it contains

      The reason that they are unavailable is that in Objective-C, nullability and generics are lightweight, meaning they are only used at build time and are not encoded or used in the compiled binary.

      Creating a property metadata wrapper

      It would be useful for doing further reflection to build an Objective-C class to give us easy access to property metadata, instead of having to deal with runtime API directly or parse those attribute strings each time. The interface could look something like this:

      /// These are mutually exclusive, so use an enum
      typedef NS_ENUM(char, SetterType) {
          Assign, Strong, Weak, Copy
      };
      
      @interface ClassProperty : NSObject
      
      @property (nonatomic, readonly) NSString *name;
      
      /// This will be the raw type encoding for now
      @property (nonatomic, readonly) NSString *encodeType;
      
      @property (nonatomic, readonly) BOOL isReadOnly;
      @property (nonatomic, readonly) BOOL isNonAtomic;
      @property (nonatomic, readonly) BOOL isDynamic;
      @property (nonatomic, readonly) SetterType setterType;
      
      /// Custom getter or the default getter
      @property (nonatomic, readonly) SEL getter;
      
      /// Custom or default setter, NULL if the property is readonly
      @property (nonatomic, readonly, nullable) SEL setter;
      
      /// Will be nil if the property is computed or dynamic
      @property (nonatomic, readonly, nullable) NSString *ivarName;
      
      - (instancetype)initWithProperty:(objc_property_t)property;
      
      @end
      

      Let’s get started on the implementation. For simplicity, we’ll just put the parsing of the property attributes string right in the init method:

      @implementation ClassProperty
      
      @synthesize getter = _getter;
      @synthesize setter = _setter;
      
      - (instancetype)initWithProperty:(objc_property_t)property {
          self = [super init];
          if (self) {
              _name = @(property_getName(property));
              _setterType = Assign;  // this is the default
      

      Notice with the _name assignment that you can “box” a C string into an NSString the same way you box a number into an NSNumber, by wrapping it in @().

      To parse the attributes, let’s just split the string by comma, and iterate over each part. We can just look at the first character in each attribute to see what it is. We’re not going to worry too much about performance here, but switching on a single char is not a bad way to go.

              NSString *attribStr = @(property_getAttributes(property));
              NSArray *attribs = [attribStr componentsSeparatedByString:@","];
      
              for (NSString *attrib in attribs) {
                  unichar attribChar = [attrib characterAtIndex:0];
                  NSString *attribDetail = [attrib substringFromIndex:1];
      
                  switch (attribChar) {
                      case 'T':
                          _encodeType = attribDetail;
                          break;
                      case 'R':
                          _isReadOnly = YES;
                          break;
                      case 'N':
                          _isNonAtomic = YES;
                          break;
                      case 'D':
                          _isDynamic = YES;
                          break;
                      case '&':
                          _setterType = Strong;
                          break;
                      case 'W':
                          _setterType = Weak;
                          break;
                      case 'C':
                          _setterType = Copy;
                          break;
                      case 'G':
                          _getter = NSSelectorFromString(attribDetail);
                          break;
                      case 'S':
                          _setter = NSSelectorFromString(attribDetail);
                          break;
                      case 'V':
                          _ivarName = attribDetail;
                          break;
                      default:
                          NSAssert(NO, @"Unknown attribute: %@", attrib);
                  }
              }
      

      There’s one final touch needed: if the property doesn’t have a custom getter or setter, we want to return the default selectors.

      For a getter, the default is simply the name of the property. Setters are slightly trickier, we need to turn a property name like myProperty into a selector setMyProperty:, and then only if it isn’t readonly:

              if (_getter == NULL) {
                  _getter = NSSelectorFromString(_name);
              }
      
              if (_setter == NULL && _isReadOnly == NO) {
                  NSString *name = [NSString stringWithFormat:@"set%@%@:",
                                    [_name substringToIndex:1].uppercaseString,
                                    [_name substringFromIndex:1]];
                  _setter = NSSelectorFromString(name);
              }
          }
          return self;
      }
      
      @end
      

      Getting the properties of any class

      This is already quite useful for examining a single property, but it would be really handy to get a list of these for any class. We can achieve this by moving our class_copyPropertyList example above into a category on NSObject:

      @interface NSObject (Introspection)
      @property (class, readonly) NSArray<ClassProperty *> *classProperties;
      @end
      
      @implementation NSObject (Introspection)
      
      + (NSArray<ClassProperty *> *)classProperties {
          NSMutableArray *clsProps = [NSMutableArray array];
          uint propCount;
          objc_property_t *props = class_copyPropertyList(self.class, &propCount);
          for (uint n = 0; n < propCount; n++) {
              [clsProps addObject:[[ClassProperty alloc] initWithProperty:props[n]]];
          }
          free(props);
          return clsProps;
      }
      
      @end
      

      Now we can get metadata for all the properties of any class!

      Assuming we’d added a custom description to ClassProperty, it’s as easy as:

      NSLog(@"%@", Person.classProperties);
      
      // Prints:
      //  (
      //    name: nonatomic, copy, type=@\"NSString\", getter=name, setter=setName:, ivar=_name
      //    dateOfBirth: nonatomic, strong, type=@\"NSDate\", getter=dateOfBirth, setter=setDateOfBirth:, ivar=_dateOfBirth
      //    age: readonly, nonatomic, assign, type=i, getter=age
      //  )
      

      Dumping the properties of any class

      We can go one step further by introspecting not just the properties themselves, but the property values of an object by using Key-Value Coding:

      Person *person = [Person new];
      person.name = @"Homer";
      person.dateOfBirth = [NSDate dateWithTimeIntervalSince1970:0];
      
      for (ClassProperty *prop in Person.classProperties) {
          NSLog(@"%@ = %@", prop.name, [person valueForKey:prop.name]);
      }
      
      // Prints:
      //  name = Homer
      //  dateOfBirth = Thu Jan  1 01:00:00 1970
      //  age = 50
      

      This is starting to look really useful now... we could use this to do automatic serialisation and deserialisation of data model classes! But that’s a topic for another post 😉

      Swift compatibility

      While this series is not really about Swift, it’s worth looking into how it interacts with the Objective-C runtime.

      The wrapper class we’ve created here will work on Swift classes, but only for properties marked with @objc, since they are the only ones visible to the runtime.

      For the equivalent of our Person class in Swift, it would print these property details:

      @objc class Person: NSObject {
          @objc var name: String
          @objc var dateOfBirth: Date
          @objc var age: Int { ... }
          var swiftOnly: Bool  // not visible to ObjC
      }
      
      print(Person.classProperties)
      
      // Prints:
      //  (
      //    name: nonatomic, copy, type=@\"NSString\", getter=name, setter=setName:
      //    dateOfBirth: nonatomic, copy, type=@\"NSDate\", getter=dateOfBirth, setter=setDateOfBirth:
      //    age: readonly, nonatomic, assign, type=q, getter=age
      //  )
      

      You should be able to spot a few differences to the Objective-C class: properties are always nonatomic, there are no backing ivars, and object properties are automatically copy instead of strong if the class conforms to NSCopying.

      Introspecting with Swift

      While the Objective-C runtime functions don’t work on pure Swift classes, Swift has its own introspection tool called Mirror:

      struct Person {
          let name = "Homer"
          let dateOfBirth = Date(timeIntervalSince1970: 0)
          var age: Int { ... }
      }
      
      let p = Person()
      let m = Mirror(reflecting: p)
      m.children.forEach { p in
          print("\(p.label!) = \(p.value) (\(type(of: p.value)))")
      }
      
      // Prints:
      //  name = Homer (String)
      //  dateOfBirth = 1970-01-01 00:00:00 +0000 (Date)
      

      This works quite differently to our Objective-C introspection though:

      • We need to use an instance of the class instead of just being able to examine the class itself
      • The computed property age doesn’t show up
      • While there is no type information included, the actual values are, so we can use type(of:) on those
      • There is less detail about the properties, but some of those concepts just don’t exist in Swift, like ivars and custom getters and setters

      On the plus side, Mirror can be used for any Swift type, not just classes. You’ll notice that we actually introspected a Swift struct above.

      Unfortunately, it looks like Mirror is not designed to work at all on Objective-C classes, so we can’t use a single technique for both languages.

      Wrapping up

      That’s about it for introspecting Objective-C properties.
      I’ve personally found the property functions to be among the most useful tools for runtime programming, and I’ve built several interesting projects using them. I plan to delve into some of these in future posts.

      Apple’s documentation on:

      ]]>
      https://ko9.org/posts/runtime-intro/The Objective-C Runtime in PracticeAn introduction to the practical uses of the Objective-C runtime.https://ko9.org/posts/runtime-intro/Sun, 3 May 2020 11:21:00 +0200I really like Objective-C.

      It’s been my primary programming language for more than a decade now, and I’m still finding out new, interesting things to solve with it. Sadly, it seems the language is entering its sunset years, and while there’s a lot to like about Swift, I feel like some of the fun will be lost on the move away from dynamic dispatching and typing.

      So I plan to write about some of the useful things I’ve discovered over the years, before they become ancient history 😄

      What’s so great about Objective-C?

      In a nutshell: the runtime.

      It’s what sets ObjC apart from other C-based object oriented languages like C++. Instead of connecting up function calls during compilation, ObjC leaves most of that until your app is actually running. This makes it much more dynamic and lets us examine and modify the behaviour of our apps on the fly; something very difficult to do in most other compiled languages.

      The runtime is what lets us access this power — it exposes the low-level wiring of the language.

      Of course, there are trade-offs. Objective-C bakes a lot more metadata about our code into the app, which increases the binary size. Message sending also adds a level of indirection that has a performance cost. However these downsides are negligible for most apps, and well worth the flexibility they add.

      So what is the runtime?

      The runtime is like a mini operating system for the Objective-C language, which your app relies on to do anything. Even a simple method call like [self description] gets converted by the compiler into this:

      objc_msgSend(self, @selector(description))
      

      That objc_msgSend() function is part of the runtime… actually, the most important part. Objective-C is a message-based language, and this is what sends the messages. Your code will normally call this function tens of millions of times per second!

      There are many great resources about the ObjC runtime which I’ve listed below. The runtime is even open source, which comes in handy if you really need to check how something works.

      In these series of posts, I’m going to focus on some practical uses of the runtime instead of how it works, but I encourage you to dig into those links.

      What does it let us do?

      The runtime enables many Objective-C language features, like being able to message nil, Key-Value Coding and Observing, the responder chain, NSUndoManager, message forwarding and object proxying.

      In general, the main powers that the runtime unlocks for us are introspection and reflection.

      Introspection

      Introspection is the ability for a program to examine itself while it’s running, such as finding out what classes are defined, and which methods and properties they contain.

      You would have already used introspection in Objective-C and maybe not even realised it. Methods like respondsToSelector:, isKindOfClass: and even the NSStringFromClass function are examples of examining your app at run time.

      The runtime functions go way beyond these. You can not only list all the classes, protocols, properties, methods and ivars in your app, but get details on their types and implementations.

      Reflection

      While introspection gives us a read-only view of our app’s internals, reflection extends this by allowing write access. This means we can change how the app works while it’s running!

      Perhaps you’ve even used some of these reflection functions directly before. Associated objects (objc_getAssociatedObject) and swizzling (method_exchangeImplementations) are two of the more common usages.

      A very useful feature of reflection is being able to implement properties and methods at run time. For example, you might have seen the @dynamic keyword used in Core Data classes. This tells the compiler not to generate the normal getter and setter methods for properties, because NSManagedObject will provide its own custom behavior when the app runs. We’ll learn how to provide those dynamic implementations in a future post.

      You can even create new classes and protocols at run time. KVO uses this to transparently create subclasses of observed objects, which then masquerade as the original class.

      Many of these things could be done without the runtime, but they’d require a lot of repetitive and fragile boilerplate code to implement, and we programmers never like writing repetitive code when we can make the computer do it 😁

      Up next

      I’m going to write a series of posts on practical uses of the runtime, starting with inspecting the properties of any class. Then we’ll look at how to create a class with dynamic storage (like NSManagedObject), and how to introspect the ivars of a class, which will let us dynamically compare and hash objects.

      We’ll also see how to automatically encode and decode objects from JSON, simulate immutable classes, and finally how we might build an app that can be patched to modify its behaviour after release! 🤯

      You’ll find new posts under the runtime tag as they’re published.

      Articles

      References

      ]]>