The string in question came from a call to the
property_getAttributes
function in the Objective-C runtime.
Right, this is a classic example of the second case I mentioned, where folks are using a
String
to hold some data structure that isn’t really text. In that case I often avoid this problem entirely by working with arrays of bytes. For example [1]:
let prop: objc_property_t = …
let attr = property_getAttributes(prop)!
if attr[1] == CChar(ascii: "s") {
… do something …
}
In this case you know that
attr
is an ASCII C string, and thus you can index it as an array.
Of course this is unsafe because you’re working with pointers, just like you are in Objective-C. If you’re doing this a few times, you can create simple wrapper functions for the cases you need; those will prevent the unsafeness from leaking out into the rest of your app.
OTOH, if you’re doing a tonne of work with the Objective-C runtime, you can build an abstraction that presents this information in a way that’s more digestable to the rest of your app.
One of the nice things about Swift is that these abstractions can be very lightweight. In Objective-C your primary abstraction mechanism is objects, and those are relatively heavyweight. In Swift you have powerful structs, that allow you to build abstractions that are easy for your clients to use and yet cheap at runtime.
For example, the following type is no more expensive than a raw
objc_property_t
type:
struct QObjCProperty {
init(property: objc_property_t) {
self.property = property
}
let property: objc_property_t
}
And yet it allows you to add abstractions like this one:
extension QObjCProperty {
var isInt16: Bool {
guard let attr = property_getAttributes(self.property) else { return false }
return attr[1] == CChar(ascii: "a")
}
}
WARNING It’s been a long time since I’ve worked with Objective-C property attribute strings, so the logic I’m using in
isInt16
might be completely bogus. The point of this code is to show how to build the abstraction, not as an example of the correct way to use the Objective-C runtime.
Share and Enjoy
—
Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware
let myEmail = "eskimo" + "1" + "@apple.com"
[1] This code doesn’t actually compile out of the box because
CChar
doesn’t have an
init(ascii:)
initialiser (it’s present for
UInt8
but not
Int8
). To get around this I added my own initialiser in an extension.
extension CChar {
init(ascii: Unicode.Scalar) {
precondition(ascii.isASCII)
self = Int8(ascii.value)
}
}