2010-12-08 7 views
4

Mail.app 용 플러그인을 만들고 있습니다. 플러그인이 Mail 툴바에 버튼을 추가하고 싶습니다. 이를 위해 MessageViewer의 initialize 메소드 (MessageViewer는 Mail.app의 FirstResponder 클래스)에이 버튼을 추가하는 함수를 호출하는 것이 가장 좋은 방법이라고 판단했습니다. 그것은 작동하지 않습니다,클래스 메서드를 swizzle 수 없습니다.

+ (void) initialize 
{ 
[super initialize]; 

// class_setSuperclass([self class], NSClassFromString(@"MVMailBundle")); 
// [ArchiveMailBundle registerBundle]; 


// Add a couple methods to the MessageViewer class. 
Class MessageViewer = NSClassFromString(@"MessageViewer"); 

// swizzleSuccess should be NO if any of the following three calls fail 
BOOL swizzleSuccess = YES; 
swizzleSuccess &= [[self class] copyMethod:@selector(_specialValidateMenuItem:) 
           fromClass:[self class] 
            toClass:MessageViewer]; 
swizzleSuccess &= [[self class] copyMethod:@selector(unsubscribeSelectedMessages:) 
           fromClass:[self class] 
            toClass:MessageViewer]; 

을 그러나, 나는 동일한 작업을 수행하려고 할 때 :

: 여기
// //Copy the method to the original MessageViewer 
swizzleSuccess &= [[self class] copyMethod:@selector(_specialInitMessageViewer:) 
           fromClass:[self class] 
            toClass:MessageViewer]; 

는 스위 즐링 방법이다 내가 수정하고있어 코드는 잘 작동하는 것 같다
+ (BOOL)swizzleMethod:(SEL)origSel withMethod:(SEL)altSel inClass:(Class)cls 
{ 
// For class (cls), swizzle the original selector with the new selector. 
    //debug lines to try to figure out why swizzling is failing. 
// if (!cls || !origSel) { 
    //   NSLog(@"Something was null. Uh oh."); 
    //} 
Method origMethod = class_getInstanceMethod(cls, origSel); 

if (!origMethod) { 
    NSLog(@"Swizzler -- original method %@ not found for class %@", NSStringFromSelector(origSel), 
      [cls className]); 
    return NO; 
} 
    //if (!altSel) NSLog(@"altSel null. :("); 
Method altMethod = class_getInstanceMethod(cls, altSel); 
if (!altMethod) { 
    NSLog(@"Swizzler -- alternate method %@ not found for class %@", NSStringFromSelector(altSel), 
      [cls className]); 
    return NO; 
} 

method_exchangeImplementations(origMethod, altMethod); 

return YES; 

} 


+ (BOOL) copyMethod:(SEL)sel fromClass:(Class)fromCls toClass:(Class)toCls 
{ 
    // copy a method from one class to another. 
    Method method = class_getInstanceMethod(fromCls, sel); 
    if (!method) 
    { 
     NSLog(@"copyMethod-- method %@ could not be found in class %@", NSStringFromSelector(sel), 
       [fromCls className]); 
     return NO; 
    } 
    class_addMethod(toCls, sel, 
        class_getMethodImplementation(fromCls, sel), 
        method_getTypeEncoding(method)); 
    return YES; 
} 

log_getInstanceMethod를 호출 할 때 로그에 오류가 발생하여 오류가 발생하는 것으로 보입니다. 이것은 MessageViewer의 initialize 메소드뿐 아니라 내 자신의 클래스 내부의 메소드에서도 발생합니다.

내가 여기에 고려하지 않은 몇 가지 문제가 있습니까?

+0

코드가 제대로 포맷되지 않아 엉망으로 보입니다. 맹 글링을하지 않으려면 각 코드 행의 최소 4 개 공간이 필요합니다. –

답변

5

수동으로 스윕을 구현하는 대신 JRSwizzle을 사용해야합니다. 한 가지주의 할 점은 JRSwizzle의 +jr_swizzleClassMethod:withClassMethod:error: 구현은 스텁 일 뿐이지 만 클래스의 메타 클래스에 +jr_swizzleMethod:withMethod:error을 그냥 호출하면됩니다 (예 : klass 대신 klass->isa을 전달하면됩니다. 여기에서 klass은 윙윙 거리는 클래스 임).

+0

고마워, 케빈, JRSwizzle 패키지가 멋지다! 이제는 클래스 메서드를 swizzle 클래스에 복사 할 책임이 있습니까, 아니면 jr_swizzleClassMethod 호출에서이를 나타낼 수있는 방법이 있습니까? (명확한 경우 사과, ObjC에 대해서는 매우 익숙하다.) – Aaron

+0

'jr_swizzleMethod : withMethod : error :'는 모든 것을 처리한다. 메서드를 대상 클래스에 복사하고 재정의하려는 메서드로 IMP를 바꿉니다. –

+0

자, 클래스 Foo에 newMethod 메소드가 있다고 가정하고 클래스 Bar의 메소드 origMethod를 사용하여 swizzle하고 싶습니다. 전환 할 클래스의 메소드를 어떻게 표시합니까? – Aaron

9

클래스 메서드를 바꾸려면 class_getClassMethod() 대신 class_getInstanceMethod() 함수를 사용하는 이유는 무엇입니까?

관련 문제