Hi Eric,
Le 10 juin 09 à 09:26, Eric Wasylishen a écrit :
> Here's my first attempt at the refactoring.
>
> I changed any implementations of the -(NSArray *)properties methods to
> -(NSArray*)propertyNames, and removed the [super properties]
> arrayByAddingObjectsInArray: properties]; bits, so they just return
> the properties for that class.
Perfect.
> The -(NSArray *)allPropertyNames method is implemented in
> EtoileFoundation/ETPropertyValueCoding.m, in an NSObject category, but
> not part of the ETPropertyValueCoding protocol.
I'm not convinced by that. ETPropertyValueCoding protocol is only just
a way to formalize the protocol, since all classes will automatically
inherit it from NSObject. Currently it isn't declared on NSObject, but
it should be since NSObject implements all the requested methods.
The protocol also makes clearer that a custom root class other than
NSObject must conform to it to be Étoilé-compliant.
> I thought this is the
> best approach, since allPropertyNames only needs to be overridden if
> an object wants to hide/not inherit properties from its superclasses.
Right, but this shouldn't be allowed normally, in order to let
developers fully introspect every properties that make up an object
and not only the visible/displayed ones. This will be useful when you
switch to the 'development mode' in a running Étoilé-native application.
-[ETLayout setDisplayedProperties:] in EtoileUI is available to let
you set which properties are visible in the UI.
In the long run, we might want to add an extra method -
allVisiblePropertyNames, then a layout can transparently use these as
displayed properties. However I'm not sure we should add it to
NSObject. When this flexibility will be needed, it could be a better
choice to specify that with an ETModelDescription.
Once ETModelDescription will be finished, we could reimplement -
propertyNames and -allPropertyNames to behind the scene just store the
property names in a model description object per class, then this
model description could also be used as a cache to avoid recursing up
to the root class each time we call -allPropertyNames. The cache could
be implemented with a simple dictionary keyed by class in the current
implementation though.
So in the end, I'm in favor of the following in NSObject+Model:
NSObject (Model) <ETPropertyValueCoding>
// ETPropertyValueCoding protocol
-propertyNames;
-allPropertyNames;
-valueForProperty;
-setValue:forProperty:
// all the other methods
-isCollection;
-isMutable;
etc.
@end
Does that make sense or do you still feel it isn't the best approach?
Everything looks mostly fine in your patch. Here are some additional
comments…
You should probably use the A() macro everywhere it's possible. Not
really critical but this would make the code a bit more concise.
> +// FIXME: Why are these static?
> static id (*valueForKeyIMP)(id, SEL, NSString *) = NULL;
> static void (*setValueForKeyIMP)(id, SEL, id, NSString *) = NULL;
Hm, it looks wrong. I wanted to make these variables static to avoid
collisions in case identically named function pointers also declared
in other files. This should have been int (static *valueForKey)(id,
SEL, NSString *) = NULL;. However I'm not sure at all this is valid C
or even makes sense. Are C function pointers normal variables or not
really?
> +...@implementation NSObject (ETPropertyValueCoding)
>
> +- (NSArray *) allPropertyNames
> +{
> + NSMutableArray *properties = [[self propertyNames] mutableCopy];
> + for (Class c = [self superclass]; c != Nil; c = [c superclass])
> + {
> + id (*propertyNamesIMP)(id, SEL) =
> + (id (*)(id, SEL))[c instanceMethodForSelector:
> @selector(propertyNames)];
> +
> + [properties addObjectsFromArray:
> + propertyNamesIMP(self, @selector(propertyNames))];
> + }
> + return properties;
> +}
> +
> +...@end
I doubt caching the IMP is faster than calling the method directly, it
could even be slower.
Although you call the same selector -propertyNames, the method
implementation is not shared. Each class has its own method
implementation. The IMP cannot be reuse on several objects in the
loop. Which is usually the case where an IMP call can make a difference.
Great work. Feel free to commit once you have made the necessary
adjustments.
Cheers,
Quentin.
_______________________________________________
Etoile-dev mailing list
[email protected]
https://mail.gna.org/listinfo/etoile-dev