[objective-c] “카테고리가 기본 클래스에서도 구현 될 메서드를 구현하고 있습니다.”경고 표시 안 함

경고를 억제하는 방법을 궁금합니다.

카테고리는 기본 클래스에 의해 구현 될 메서드를 구현하고 있습니다.

특정 코드 범주에 대해 다음이 있습니다.

+ (UIFont *)systemFontOfSize:(CGFloat)fontSize {
    return [self aCustomFontOfSize:fontSize];
}



답변

범주를 사용하면 기존 클래스에 새 메서드를 추가 할 수 있습니다. 클래스에 이미 존재하는 메서드를 다시 구현하려면 일반적으로 범주 대신 하위 클래스를 만듭니다.

Apple 문서 : 기존 클래스 사용자 정의

범주에 선언 된 메서드의 이름이 원래 클래스의 메서드와 같거나 동일한 클래스 (또는 수퍼 클래스)의 다른 범주에있는 메서드와 동일하면 어떤 메서드 구현에서 사용되는지에 대한 동작이 정의되지 않습니다. 실행 시간.

동일한 클래스에서 정확히 동일한 서명을 가진 두 메서드는 각 호출자가 원하는 구현을 지정할 수 없기 때문에 예측할 수없는 동작을 유발합니다.

따라서 범주를 사용하고 클래스에 대해 새롭고 고유 한 메서드 이름을 제공하거나 클래스에서 기존 메서드의 동작을 변경하려는 경우 하위 클래스를 제공해야합니다.


답변

비열하게 말한 모든 것이 정확하지만 실제로 경고를 억제하는 방법에 대한 질문에 대답하지는 않습니다.

어떤 이유로 든이 코드가 있어야하고 (제 경우에는 프로젝트에 HockeyKit이 있고 UIImage 범주의 메서드를 재정의합니다 [편집 : 더 이상 그렇지 않음]) 프로젝트를 컴파일해야하는 경우 , 당신이 사용할 수있는 #pragma 과 같이 명령문을 하여 경고를 차단할 .

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"

// do your override

#pragma clang diagnostic pop

여기에서 정보를 찾았습니다. http://www.cocoabuilder.com/archive/xcode/313767-disable-warning-for-override-in-category.html


답변

더 나은 대안 (이 경고가 재난으로부터 사용자를 구하는 이유에 대한 bneely의 답변 참조)은 방법 전환을 사용하는 것입니다. 메서드 재구성을 사용하면 누가 “승리”하는지 불확실하지 않고 이전 메서드를 호출 할 수있는 기능을 유지하면서 범주의 기존 메서드를 대체 할 수 있습니다. 비결은 재정의에 다른 메서드 이름을 지정한 다음 런타임 함수를 사용하여 교체하는 것입니다.

#import <objc/runtime.h> 
#import <objc/message.h>

void MethodSwizzle(Class c, SEL orig, SEL new) {
    Method origMethod = class_getInstanceMethod(c, orig);
    Method newMethod = class_getInstanceMethod(c, new);
    if(class_addMethod(c, orig, method_getImplementation(newMethod), method_getTypeEncoding(newMethod)))
        class_replaceMethod(c, new, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
    else
    method_exchangeImplementations(origMethod, newMethod);
}

그런 다음 사용자 정의 구현을 정의하십시오.

+ (UIFont *)mySystemFontOfSize:(CGFloat)fontSize {
...
}

기본 구현을 자신의 것으로 재정의하십시오.

MethodSwizzle([UIFont class], @selector(systemFontOfSize:), @selector(mySystemFontOfSize:));


답변

코드에서 이것을 시도하십시오.

+(void)load{
    EXCHANGE_METHOD(Method1, Method1Impl);
}

UPDATE2 :이 매크로 추가

#import <Foundation/Foundation.h>
#define EXCHANGE_METHOD(a,b) [[self class]exchangeMethod:@selector(a) withNewMethod:@selector(b)]

@interface NSObject (MethodExchange)
+(void)exchangeMethod:(SEL)origSel withNewMethod:(SEL)newSel;
@end

#import <objc/runtime.h>

@implementation NSObject (MethodExchange)

+(void)exchangeMethod:(SEL)origSel withNewMethod:(SEL)newSel{
    Class class = [self class];

    Method origMethod = class_getInstanceMethod(class, origSel);
    if (!origMethod){
        origMethod = class_getClassMethod(class, origSel);
    }
    if (!origMethod)
        @throw [NSException exceptionWithName:@"Original method not found" reason:nil userInfo:nil];
    Method newMethod = class_getInstanceMethod(class, newSel);
    if (!newMethod){
        newMethod = class_getClassMethod(class, newSel);
    }
    if (!newMethod)
        @throw [NSException exceptionWithName:@"New method not found" reason:nil userInfo:nil];
    if (origMethod==newMethod)
        @throw [NSException exceptionWithName:@"Methods are the same" reason:nil userInfo:nil];
    method_exchangeImplementations(origMethod, newMethod);
}

@end


답변

메서드 재구성을 사용하여이 컴파일러 경고를 억제 할 수 있습니다. 다음은 UITextBorderStyleNone과 함께 사용자 지정 배경을 사용할 때 UITextField에서 여백을 그리기위한 메서드 재구성을 구현 한 방법입니다.

#import <UIKit/UIKit.h>

@interface UITextField (UITextFieldCatagory)

+(void)load;
- (CGRect)textRectForBoundsCustom:(CGRect)bounds;
- (CGRect)editingRectForBoundsCustom:(CGRect)bounds;
@end

#import "UITextField+UITextFieldCatagory.h"
#import <objc/objc-runtime.h>

@implementation UITextField (UITextFieldCatagory)

+(void)load
{
    Method textRectForBounds = class_getInstanceMethod(self, @selector(textRectForBounds:));
    Method textRectForBoundsCustom = class_getInstanceMethod(self, @selector(textRectForBoundsCustom:));

    Method editingRectForBounds = class_getInstanceMethod(self, @selector(editingRectForBounds:));
    Method editingRectForBoundsCustom = class_getInstanceMethod(self, @selector(editingRectForBoundsCustom:));


    method_exchangeImplementations(textRectForBounds, textRectForBoundsCustom);
    method_exchangeImplementations(editingRectForBounds, editingRectForBoundsCustom);

}


- (CGRect)textRectForBoundsCustom:(CGRect)bounds
{
    CGRect inset = CGRectMake(bounds.origin.x + 10, bounds.origin.y, bounds.size.width - 10, bounds.size.height);
    return inset;
}

- (CGRect)editingRectForBoundsCustom:(CGRect)bounds
{
    CGRect inset = CGRectMake(bounds.origin.x + 10, bounds.origin.y, bounds.size.width - 10, bounds.size.height);
    return inset;
}

@end


답변

재정의 속성은 클래스 확장 (익명 범주)에는 유효하지만 일반 범주에는 유효하지 않습니다.

클래스 확장 (익명 카테고리)을 사용하는 Apple Docs에 따르면 공용 클래스에 대한 개인 인터페이스를 생성하여 개인 인터페이스가 공개적으로 노출 된 속성을 재정의 할 수 있습니다. 즉, 속성을 읽기 전용에서 읽기 쓰기로 변경할 수 있습니다.

이에 대한 사용 사례는 공용 속성에 대한 액세스를 제한하는 라이브러리를 작성하는 반면 동일한 속성에는 라이브러리 내에서 전체 읽기 쓰기 액세스가 필요합니다.

Apple 문서 링크 : https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html

클래스 확장을 사용하여 개인 정보 숨기기 “를 검색 하십시오 .

따라서이 기술은 클래스 확장에는 유효하지만 카테고리에는 유효하지 않습니다.


답변

카테고리는 좋은 것이지만 남용 될 수 있습니다. 카테고리를 작성할 때 원칙적으로 기존 방법을 다시 구현하지 않아야합니다. 이렇게하면 다른 클래스가 종속 된 코드를 다시 작성하므로 이상한 부작용이 발생할 수 있습니다. 알려진 클래스를 깨뜨려 디버거를 뒤집을 수 있습니다. 단순히 나쁜 프로그래밍입니다.

당신이 그것을해야한다면, 당신은 정말로 그것을 서브 클래스해야한다.

그런 다음 스위 즐링의 제안은 나에게 큰 NO-NO-NO입니다.

런타임에 Swizzing하는 것은 완전한 NO-NO-NO입니다.

바나나가 주황색처럼 보이기를 원하지만 런타임에만? 오렌지를 원한다면 오렌지를 쓰세요.

바나나를 오렌지처럼 보이게하지 마십시오. 더 나쁜 것은, 바나나를 오렌지를 지원하기 위해 조용히 전세계 바나나를 방해하는 비밀 요원으로 바꾸지 마십시오.

이런!