Hi David!

On 5. 8. 2012., at 23:08, David Chisnall <[email protected]> wrote:

> Hi Ivan,
> 
> You seem to be very, very confused and none of the documentation that you 
> cite says what you seem to think it says.
> 
> @dynamic does not mean run-time synthesized, it means the exact opposite, as 
> I said.  @dynamic is just a hint to the compiler that you are providing the 
> implementation of the property (possibly in a superclass, possibly in a 
> category) and that it should not complain if it can't see one, nor should it 
> try to synthesise one if you are in a synthesis-by-default mode (recent 
> versions of clang default to synthesising properties if they don't see 
> implementations or @dynamic).
> 
> And, as I suspected, you are confusing properties with KVC (Key-Value 
> Coding), or, perhaps more relevantly, with KVO (Key-Value Observing).  
> CoreAnimation makes heavy use of both KVC and KVO for animatable properties.  
> Note that the CoreAnimation documentation occasionally uses the term 
> 'properties' in the traditional way, rather than referring to declared 
> properties.
> 
> For a property to be used for animation, it must be KVC-compliant, so that CA 
> can set it, and KVO compliant, so that CA can receive notifications when it 
> changes.  Again, this does not require any method synthesis at run time.  
> 
> If you read the two links you posted carefully, then you will see that they 
> say nothing like what you seem to believe that they say.  They explicitly 
> deal with the case where methods do not exist (@dynamic is used to ensure 
> that it does not), and so the first path in Core Animation fails.  Trying to 
> animate a property with no corresponding setters and getters, CoreAnimation 
> will enter into one of the fall-back mechanisms, either one provided by 
> KVC/KVO or its own, which is very similar but a bit less general (and 
> therefore, in theory at least, faster).  
> 
> As I said before, you do not need to add methods at run time.  You simply 
> need to implement the case when you try to set a property that does not have 
> a corresponding method.  The Omni link explicitly tells you that this stuff 
> is all done using KVC...
> 
> The examples use declared properties for simplicity, because they are using 
> it as a way of providing type information to KVC.  I don't think our KVC 
> implementation actually makes use of property metadata in this way, so that's 
> something that you could look at fixing, although I think property 
> introspection only works with clang and the GNUstep runtime...
> 
> David
> 


Thanks for all the hints, however, I'm now even more confused. 

Some phrases such as this excerpt from Apple documentation (emphasis added) 
indicate that accessor methods are indeed synthesized at runtime:
http://developer.apple.com/library/mac/#releasenotes/GraphicsImaging/CoreAnimation_RN/_index.html
> Will now only synthesize property accessor methods for properties marked 
> @dynamic in their class implementation. For backwards compatibility we retain 
> the previous behavior for executables linked on OS versions prior to 10.6.


I'd follow your advice and implement these properties via KVC/KVO, storing 
these additional properties in a dictionary, however that doesn't seem to be 
enough. Here's some quickly whipped-together test code.

#import "ISAppDelegate.h"

@interface LayerSubclass : CALayer
@property (nonatomic, retain) NSString * prop;
@end

@implementation LayerSubclass
@dynamic prop;
@end

@interface ObjectSubclass : NSObject
@property (nonatomic, retain) NSString * prop;
@end
@implementation ObjectSubclass
@dynamic prop;
- (id)valueForKey:(NSString*)key
{
    if([key isEqualToString:@"prop"])
        return @"ok";
    return [super valueForKey:key];
}
- (void)setValue:(NSString*)value forKey:(NSString*)key
{
    if([key isEqualToString:@"prop"])
    {
        return;
    }
    
    return [super setValue:value forKey:key];
}
@end

void printMethods(id obj)
{
    unsigned int count=0;
    Method * methodList = class_copyMethodList([obj class], &count);
    NSLog(@"%d methods:", count);
    for(int i = 0; i < count; i++)
    {
        NSLog(@" - %@", NSStringFromSelector(method_getName(methodList[i])));
    }
    NSLog(@"------");
    free(methodList);
}

@implementation ISAppDelegate

- (void)dealloc
{
    [super dealloc];
}

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    LayerSubclass * b = [LayerSubclass new];
    printMethods(b);
    b.prop = @"5";
    [b setProp:@"5"];
    printMethods(b);
    
    ObjectSubclass * s = [ObjectSubclass new];
    s.prop = @"5";
}


@end

Here's the output:

2012-08-06 10:46:06.112 SubclassingCALayerTest[4490:303] 0 methods:
2012-08-06 10:46:06.114 SubclassingCALayerTest[4490:303] ------
2012-08-06 10:46:06.114 SubclassingCALayerTest[4490:303] 1 methods:
2012-08-06 10:46:06.115 SubclassingCALayerTest[4490:303]  - setProp:
2012-08-06 10:46:06.116 SubclassingCALayerTest[4490:303] ------
2012-08-06 10:46:06.117 SubclassingCALayerTest[4490:303] -[ObjectSubclass 
setProp:]: unrecognized selector sent to instance 0x101a27d80
2012-08-06 10:46:06.118 SubclassingCALayerTest[4490:303] -[ObjectSubclass 
setProp:]: unrecognized selector sent to instance 0x101a27d80

Obviously, CALayer has created accessor methods for the layer - even a direct 
message-send of setProp: works. In fact, the method appears to be created when 
the first attempt to access it is made.
On the other hand, no hackery seems to be happening in NSObject subclass when 
setting the value of "prop". "setValue:forKey:" is not called - instead, the 
expected behavior of attempting to use the accessor "setProp:" occurs, and 
fails.

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.

If you can point me at mistakes I made in the test code, or my understanding of 
the underlying mechanisms, I'd appreciate that. :-)

--
Ivan Vučica

_______________________________________________
Discuss-gnustep mailing list
[email protected]
https://lists.gnu.org/mailman/listinfo/discuss-gnustep

Reply via email to