Sunday, December 1, 2013

Categoy, Class Extension, Method Swizzling

Letezo Class-t 2 felekeppen lehet kiboviteni oroklodes nelkul:
1. Categories (amivel egy letezo Class-hoz lehet method-okat adni):
@interface ClassName (CategoryName)
..
@end
pl:
hederbe (XYPerson+XYPersonExtraMethods.h):
#import “XYPerson.h”
@interface XYPerson (XYPersonExtraMethods) 
- (NSString *)lastNameFirstNameString;
@end
implementacio (XYPerson+XYPersonExtraMethods.m):
#import “XYPerson+XYPersonExtraMethods.m;
@implementation XYPerson (XYPersonExtraMethods)
- (NSString *)lastNameFirstNameString {
    return [NSString stringWithFormat:@”%@, %@”, self.lastName, self.fistName];
}
@end
Es utana igy lehet meghivni:
#import “XYPerson+XYPersonExtraMethods.h”;
@implementation someObject
- (void)someMethod {
    XYPerson *person = [[XYPerson alloc] initWithDefaultValues];
    NSLog(@”%@”, [person lastNameFirstNameString]);
}
@end
Vigyazni kell, mert ha 2 kulonbozo helyen is definialva van Category ugyanarra a Class-ra es elofordul ugyanolyan method mindket Category-ban, akkor undefinded, hogy futasi idoben melyik hivodik meg. Felul is lehet irni az eredti Class method-jat, de ha az eredetileg is egy Category-ban volt definialva akkor megint csak undefined, hogy melyik hivodik meg. Erre megoldas a Method Swizzling.

2. Class Extension (amivel egy Class internal impelmentaciojat lehet kiboviteni):
Hasonlit a Category-ra, de csak olyan Class-on lehet alkalmazni aminek megvan a soruce kodja (egy compile soran forditodik le mindketto es az eredeti Class implementaciojaba kell beleirni).
Anonymous Category-nak is hivjak es igy nez ki:
@interface ClassName () {
    id _someCustomInstanceVariable; //igy lehet instance valtozot hozzaadni
}
    @property NSObject *extraProperty; //A Category-val szemben itt property-ket is lehet definialni
@end
Sokszor private property-k es method-ok letrehozasara hasznaljak: readonly-nak adunk meg valamit az interface-n es readwrite-nak Class Extension-ban. 
XYPerson.h:
@interface XYPerson: NSObject
@property (readonly) NSString *anyProp;
- (void)assignAnyProp;
@end
XYPerson.m:
@interface XYPerson ()
@property (readwrite) NSString *anyProp; //readwrite elhagyhato, mert az a default
@end

@implementation XYPerson
@end
A compiler letrehoz egy settert igy most anyProperty csak belulrol allithato.
-----------------------
Egy letezo Class letezo Method-janak lecserelese. Ugyan Category-val is lehet override-olni meglevo Method-ot, de ha eredetileg is egy Category-ban volt a method akkor undefined, hogy melyik valtozat fut le, raadasul Mehod Swizzling-gel az eredeti method-ot is meg lehet hivni. A megoldas, hogy az uj method-ot mashogy nevezzuk el, majd kicsereljuk oket:
@implementation NSView (MyOverride)
- (void) override_drawRect: (NSRect)r {
    [self override_drawrect: r]; //itt valojaban mar a kicserelt eredeti drawRect-et hivjuk
    //whatever we want to add to the original method
}
@end
Igy csereljuk meg az uj method-ot az eredetivel:
void MehtodSwizzle(Class c, SEL origSEL, SEL overrideSEL) {
    Method origMethod = class_getInstanceMethod(c, origSEL);
    Method overrideMethod = class_getInstanceMethod( c, overrideSEL);
    //ha a method az eredeti Class superclass-aban letezik azt kulon kell kezelni mivel a class_getInstanceMethod ott is keres. Mivel a class_addMethod nem erinti a superclass-t ezert azzal lehet kiszurni hogy hol volt az origSEL eredetileg
    if (class_addMethod(c, origSEL, 
                        method_getImplementation(overrideMethod), 
                        method_getTypEncoding(overrideMethod))) {
    //ha az addMethod sikerult akkor az origSEL az origClass superclass-aban volt, de most mar ott van az origClass-ban az overrideMethod implementeaciojaval, mar csak az overrideSEL-t kell hozzaadni az origClass superclassaban levo origSEL implementaciojaval
        class_replaceMethod(c, overrideSEL, 
                            method_getImplementation(orgiMethod), 
                            method_getTypeEncoding(origMethod));
    }
    else {
    //ha az addMethod failed akkor az origClass-ban volt definialva az origMethod nem pedig a superclass-aban es ilyenkor siman meg lehet cserelni helyben a 2 method-ot
        method_exchangeImplementation(origMethod, overrideMethod);
    }
}
Vegul meg biztosnak kell lenni, hogy a MeghodSwizzle meghivodik a progam indulasanal es ezt elintezhetjuk a Category-ban:
+ (void)load {
   MethodSwizzle(self, @selector(drawRect:), 
                       @selector(override_drawRect));
}

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.