What does @NSManaged do?
November 6, 2015
There's not much about @NSManaged
in the Apple docs and the little it is irrevocably ties it to NSManagedObject
. I imagine there must have been a misunderstanding since the evidence suggests @NSManaged
presents exactly the same behavior as @dynamic
in Objective-C (which is different from Swift
's dynamic
) and it's certainly not tied to NSManagedObject
or Core Data.
Let us start by creating a Hero
class. The first thing I've noticed was that, while dynamic
can be successfully applied to properties of any class, @NSManaged
makes no sense outside the NSObject
protocol.
The compiler may not complain, but there's no way for you to provide an implementation and storage at runtime for a class that doesn't implement the NSObject
protocol.
Inevitably your app will crash.
class Hero:NSObject
{
@NSManaged var agility:Int
dynamic var strength:Int
var endurance:Int
var inventory:[String]
override init()
{
self.endurance = 0
self.strength = 0
self.inventory = ["sword"]
super.init()
self.agility = 0
}
}
The compiler enforces the initialization of endurance
, strength
and inventory
before super.init()
but agility
after it. It sort of makes sense since the super
class could provide a valid implementation and storage for agility
in its initialization
method (not called until you send the first message to the class, which could be init
), as NSManagedObject
would do for its subclasses.
We override the initialize
method and see what are those properties made of. It would have also been a good place to provide a runtime implementation for any @NSManaged
properties.
override class func initialize()
{
var propertiesCount:UInt32 = 0
var ivarsCount:UInt32 = 0
let properties = class_copyPropertyList(self.self, &propertiesCount)
let ivars = class_copyIvarList(superclass(), &ivarsCount)
for i in 0..<propertiesCount
{
let property = properties[Int(i)]
let attributes = String.fromCString(property_getAttributes(property))?.characters.split(",").map{String($0)}
let name = String.fromCString(property_getName(property))
print("property \(name), \(attributes)")
}
for i in 0..<ivarsCount
{
let ivar = ivars[Int(i)]
let name = String.fromCString(ivar_getName(ivar))
print("ivar \(name)")
}
ivars.destroy()
properties.destroy()
}
This outputs:
property Optional("agility"), Optional(["Tq", "N", "D"])
property Optional("strength"), Optional(["Tq", "N", "Vstrength"])
property Optional("endurance"), Optional(["Tq", "N", "Vendurance"])
property Optional("inventory"), Optional(["T@\"NSArray\"", "N", "C", "Vinventory"])
ivar Optional("isa")
ivar Optional("strength")
ivar Optional("endurance")
ivar Optional("inventory")
As you can see, agility
is a long long
(Tq
), nonatomic
(N
) and dynamic
property. Exactly the same signature a similar @dynamic
property would have in Objective-C.
Another interesting fact is that the Swift dynamic
is not really needed here, all the properties are dynamically dispatched. However, never rely on that, the compiler could optimize the properties, there are only two ways to guarantee dynamic dispatching of a property: dynamic
(with storage and implementation) and @NSManaged
(without storage and implementation).