2016-01-07 2 views
0

Swizzling не выполняет динамический обмен методом. Это код, который я использовал. Я слышал, что это решение, в котором инъекция зависимостей не может быть выполнена в XCTest в xcode 7. Можете ли вы дать мне объяснение по Swizzling over DI (Dependency) с примером?Swizzling не работает для методов класса

#import "TNUserDetail+Swizzle.h" 
#import <objc/runtime.h> 

@implementation TNUserDetail (Swizzle) 

+ (void) swizzleInstanceSelector:(SEL)originalSelector 
       withNewSelector:(SEL)newSelector 
{ 
    Method originalMethod = class_getClassMethod(self, originalSelector); 
    Method newMethod = class_getClassMethod(self, newSelector); 

    BOOL methodAdded = class_addMethod([self class], 
             originalSelector, 
             method_getImplementation(newMethod), 
             method_getTypeEncoding(newMethod)); 

    if (methodAdded) { 
     class_replaceMethod([self class], 
          newSelector, 
          method_getImplementation(originalMethod), 
          method_getTypeEncoding(originalMethod)); 
    } else { 
     method_exchangeImplementations(originalMethod, newMethod); 
    } 
} 

+(BOOL)isSignUpSwizzle { 

    return sighUp; 
} 


Test 
_____ 

@implementation TNSettingsViewControllerTests 

- (void)setUp { 
    [super setUp]; 

    UIStoryboard *sb = [UIStoryboard storyboardWithName:@"Main" bundle:nil]; 

    self.settingVC = [sb instantiateViewControllerWithIdentifier:@"TNSettingsViewController"]; 


    [self.settingVC performSelectorOnMainThread:@selector(loadView) withObject:nil waitUntilDone:YES]; 
    [self.settingVC performSelectorOnMainThread:@selector(viewWillAppear:) withObject:nil waitUntilDone:YES]; 
} 

-(void)testTwitterConnectSwitchValueChanged 
{ 
    static dispatch_once_t onceToken; 
    dispatch_once(&onceToken, ^{ 

     [TNUserDetail swizzleInstanceSelector:@selector(isSignUpWithTwitter) withNewSelector:@selector(isSignUpSwizzle)]; 
     [TNUserDetail isSignUpWithTwitter]; 
    }); 

    sighUp = YES; 
    self.settingVC.twitterConnectSwitch.on = YES; 
    [self.settingVC.twitterConnectSwitch sendActionsForControlEvents:UIControlEventValueChanged];; 
} 

Вот когда я называть [TNUserDetail isSignUpWithTwitter] + (BOOL) isSignUpSwizzle не вызывается, и только фактический метод вызывается. Что не так. Обратите внимание, что оба метода - это методы класса.

ответ

2

Методы экземпляр существует в классе таблицы рассылки, но методы класса существуют в таблице рассылки meta_class, поэтому вам нужно использовать 'meta class' вместо self (class).

#import "TNUserDetail.h" 
#import <objc/runtime.h> 

@implementation TNUserDetail 

+ (void)swizzleInstanceSelector:(SEL)originalSelector withNewSelector:(SEL)newSelector { 
    const char *className = [NSStringFromClass(self) UTF8String]; 
    Class clazz = objc_getMetaClass(className); 
    Method originalMethod = class_getClassMethod(clazz, originalSelector); 
    Method newMethod = class_getClassMethod(clazz, newSelector); 

    BOOL methodAdded = class_addMethod(clazz, 
             originalSelector, 
             method_getImplementation(newMethod), 
             method_getTypeEncoding(newMethod)); 

    if (methodAdded) { 
     class_replaceMethod(clazz, 
          newSelector, 
          method_getImplementation(originalMethod), 
          method_getTypeEncoding(originalMethod)); 
    } else { 
     method_exchangeImplementations(originalMethod, newMethod); 
    } 
} 

+ (void)load { 
    [super load]; 
    [self swizzleInstanceSelector:@selector(printHello) withNewSelector:@selector(printHelloWorld)]; 
} 

+ (void)printHello { 
    NSLog(@"Hello"); 
} 

+ (void)printHelloWorld { 
    NSLog(@"Hello World"); 
} 

@end 

и вызвать [TNUserDetail printHello]; печати 'Hello World'

Но ваш swizzling влияет на весь проект. В этом случае я рекомендую использовать частичные издевки (OCMock)

Смежные вопросы