2013-04-20 2 views
1

NSString 클래스의 메서드 Swizzle isEqualToString:을 시도 할 때 이상한 동작이 발생합니다. 여기에 문제의 코드입니다 :메서드 Swizzling isEqualToString

#import <Foundation/Foundation.h> 
#import <objc/objc-runtime.h> 

@interface NSString (SwizzleString) 
- (BOOL) custom_isEqualToString:(NSString *)aString; 
- (NSRange)custom_rangeOfString:(NSString *)aString; 
@end 

@implementation NSString (SwizzleString) 

- (BOOL) custom_isEqualToString:(NSString *)aString; 
{ 
    NSLog(@"Inside custom_isEqualToString method definition"); 
    return [self custom_isEqualToString:aString]; 
} 

- (NSRange)custom_rangeOfString:(NSString *)aString; 
{ 
    NSLog(@"Inside custom_rangeOfString method definition"); 
    return [self custom_rangeOfString:aString]; 
} 

@end 

int main(int argc, const char * argv[]) 
{ 

    Method m1, m2; 

    m1 = class_getInstanceMethod([NSString class], @selector(isEqualToString:)); 
    m2 = class_getInstanceMethod([NSString class], @selector(custom_isEqualToString:)); 
    method_exchangeImplementations(m1, m2); 

    m1 = class_getInstanceMethod([NSString class], @selector(rangeOfString:)); 
    m2 = class_getInstanceMethod([NSString class], @selector(custom_rangeOfString:)); 
    method_exchangeImplementations(m1, m2); 

    NSString *foo = @"Foo"; 

    // Does not log anything, is still using isEqualToString: implementation 
    [foo isEqualToString:@"Foo"]; 
    // Also does not log anything, since it is using the method implementation from isEqualToString: 
    [foo custom_isEqualToString:@"Foo"]; 

    // Does log something because rangeOfString now uses custom_rangeOfString IMP 
    [foo rangeOfString:@"Foo"]; 
    // Does not log anything because it uses the method implementation from rangeOfString: 
    [foo custom_rangeOfString:@"Foo"]; 
} 

isEqualToString:rangeOfString: 모두 (NSStringExtensionMethods)라는 NSString의 범주에 정의되어 있습니다, 그래서 내가 제대로 방법을 스위 즐링있어 것을 보여주기 위해 rangeOfString 스위 즐을 포함하고 구체적으로 NSString 개체를 성공적으로 너무 class cluster 문제에 대한 질문을 삭제할 수 있습니다.

위 코드의 어셈블리를 생성 할 때 objc_msgSend 호출 대신 l_objc_msgSend_fixup_isEqualToString_과 같은 내용이 표시됩니다. 이렇게하는 것이 isEqualToString: 찾을 수 있습니다 보인다는 objective-c vtable에 대해 자세히 알아 내기를 알려준 : 나는 그것을 할 수있을 것입니다 방법에 하루 종일 목표 - C 소스와 인터넷을 통해 파고 봤는데

static const char * const defaultVtable[] = { 
    "allocWithZone:", 
    "alloc", 
    "class", 
    "self", 
    "isKindOfClass:", 
    "respondsToSelector:", 
    "isFlipped", 
    "length", 
    "objectForKey:", 
    "count", 
    "objectAtIndex:", 
    "isEqualToString:", 
    "isEqual:", 
    "retain", 
    "release", 
    "autorelease", 
}; 

어떻게 든 여전히 isEqualToString:을 swizzle 수 있습니다.

+1

돈이 ' 플라이 웨이 (FlyWeight) 패턴을 구현하거나 클래스 클러스터 (Class Cluster)를 구현하는 것들을 이제까지 ** 뽐내고 있습니다. 그것은 단지 시간 낭비 일뿐입니다. – CodaFi

+2

낭비하는 시간이 항상 낭비되는 것은 아닙니다. 이 시점에서 나는 주로 호기심을 느낀다. –

답변

0

알고 계시 겠지만,이 클래스 클러스터입니다. 당신은 정말 당신이 가지고있는 문자열 객체를 요청하십시오 실제 클래스가 아닌 공용 클래스가 필요합니다

NSString *foo = @"Foo"; 

m1 = class_getInstanceMethod([foo class], @selector(isEqualToString:)); 
m2 = class_getInstanceMethod([foo class], @selector(custom_isEqualToString:)); 
method_exchangeImplementations(m1, m2); 

클래스 이름 자체를 사용하는 깨지기 쉬운 대안도있다 :

NSClassFromString(@"__NSCFConstantString") 
NSClassFromString(@"__NSCFString") 
+0

와우, 나는 그것을 시험해 볼 수 있었다! 나는 클래스 클러스터에 대해 알고 있고 성공에 대한 방법을 뽐내고있다. 그러나 어떤 이유로이 것을 간과해야만한다. 저를 올바른 방향으로 돌려 주셔서 감사합니다! –

관련 문제