[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: @dynamic property synthesis
From: |
David Chisnall |
Subject: |
Re: @dynamic property synthesis |
Date: |
Mon, 6 Aug 2012 16:05:37 +0100 |
On 6 Aug 2012, at 09:51, Ivan Vučica wrote:
> Unless I'm misunderstanding again, I need to replicate the behavior of
> CALayer by adding accessor methods at runtime. I can do that in two ways:
> adding methods to the class at runtime, or intercepting an attempt to call an
> unknown method (via -forwardInvocation:). If I'm adding a method, it needs to
> be there; whether it's created in +initialize or in -forwardInvocation:
> doesn't really matter. No matter how I'm using forwarding mechanisms, I need
> to detect that it's an attempt to call an accessor.
It is most likely that Apple has implemented this via the
+resolveInstanceMethod: (which should also work for us). This basically lets
you lazily add methods, and is faster than the -forwardInvocation: method. The
addition of methods like this is an optimisation though - don't worry about it
too much because it's not essential, just a nice speedup for certain uses (and,
to be honest, when you're throwing data across a PCIe bus, a couple of
-forwardInvocation: calls are going to be in the noise, performance-wise).
Now, the good news is that this code is actually easier to implement on GNUstep
than OS X. On OS X, they must use property introspection to find what the
selector should be. We don't need to do that, because the type of the method
can be determined from the selector.
The easiest way to implement this sort of thing on older systems is to have a
placeholder class, something like _CALayer, with accessor methods for a few
types (basically, integer and float types, plus objects). You then can just
copy this method to the correct place.
With the GNUstep runtime (or the OS X 10.7 / iOS 5 runtime), however, it's even
easier. You can use imp_implementationWithBlock() to generate the method for a
block[1]. This means that you can bind the ivar to the method, so you don't
have to do a dynamic call every time. Something like this would work for the
trivial case of just adding accessors for objects:
+ (BOOL)resolveInstanceMethod:(SEL)name
{
// Look up the ivar, this is a pointer to something in the runtime, so
never freed
Ivar ivar = class_getInstanceVariable(self, sel_getName(name));
// This binds the ivar, so when it's used it is very cheap to access
id(^idBlock)(id Self) { return object_getIvar(Self, ivar); }
// Now we get an IMP from the block
IMP imp = imp_implementationWithBlock(idBlock);
// And add the IMP to class
class_addMethod(self, name, imp, sel_getType_np(name));
// And it worked (well, we assume so - error checking in the real
version!), so let the runtime know that it should redo the lookup.
return YES;
}
Note that for set methods you will need to extract the ivar name by removing
the leading set and changing the case of the first letter - probably easier to
go via an NSString for that.
Note also that this code path doesn't need optimising - it will only ever be
called once for each property, so it can be relatively slow as long as the
block it returns is simple.
Also note that I typed this code into my mail client and haven't tried
compiling it - it is for illustrative use only!
David
[1] http://etoileos.com/news/archive/2011/11/10/1426/
-- Sent from my Apple II