runtime 관련 대상 및 방법 교환

7423 단어

연관 객체


runtime의 대상 관련은 알고 있으면 알 수 있다. 그는 하나의 키를 통해 두 대상이나 대상과 어떤 클래스 사이를 연결한다.이렇게 하면 대상의 직접적인 전달, 통신, 대응 관련 방법을 얻을 수 있다.
runtime에서 OC는 우리에게 특정한 방법을 제공한다.
//    
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
//       
id objc_getAssociatedObject(id object, const void *key)
//       
void objc_removeAssociatedObjects(id object)

그중의 매개 변수objectvalue는 각각 관련되고 관련된 대상으로key를 통해 연결된다.objc_AssociationPolicy는 일괄 유형으로 발송 정책을 지정하고 관련 유형의 속성을 지정한다.
OBJC_ASSOCIATION_ASSIGN = 0,      
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1,
OBJC_ASSOCIATION_COPY_NONATOMIC = 3,  
OBJC_ASSOCIATION_RETAIN = 01401,      
OBJC_ASSOCIATION_COPY = 01403    

실전 사용
  • 대상에 연락을 추가한다. 예를 들어 우리는 하나의 데이터에 대한 설명을 추가해야 한다. 이런 관련 관계를 이용하여 활용할 수 있다.
  • const NSString *accociate_name_key = @"accociate_name_key";
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        
        NSArray *names = @[@"  ",@"  ",@"  "];
        NSString *describe = @"    3          ";
        //         
        objc_setAssociatedObject(names, &accociate_name_key, describe, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
        
        //       ,      
        NSString *obj = objc_getAssociatedObject(names, &accociate_name_key);
        NSLog(@"%@",obj);
    }
    
  • set/get 생성 방법category를 사용하면 개인 속성을 증가시킬 수 없지만 이 방법을 통해 setter/getter 생성 방법을 연결할 수 있습니다.이런 종류는 정의된 속성과 전송을 실현할 수 있다.
  • #import 
    @interface UIViewController (Tool)
    @property (copy, nonatomic)NSString *otherTitle;
    @end
    
    #import "UIViewController+Tool.h"
    #import 
    
    static const NSString *associatedKey = @"associate_otherTitle_key";
    
    @implementation UIViewController (Tool)
    
    - (NSString *)otherTitle {
        return objc_getAssociatedObject(self, &associatedKey);
    }
    
    - (void)setOtherTitle:(NSString *)otherTitle {
        objc_setAssociatedObject(self, &associatedKey, otherTitle, OBJC_ASSOCIATION_COPY_NONATOMIC);
    }
    
  • 사용자 정의 응답 이벤트는 어떤 방법을 호출할 때 응답하는 이벤트는 다른 방법으로 처리해야 한다. 예를 들어 UIAlertView이다. 팝업 상자를 눌렀을 때 응답하는 이벤트는delegate에서 보아야 한다. 코드량이 많으면 시간을 찾아야 하지만 관련 방식을 통해delegate 처리 시간을 같은 방법으로 옮겨서 보기 편리하게 할 수 있다.
  • - (void)viewDidLoad {
        [super viewDidLoad];
        
        UIAlertView *alterView = [[UIAlertView alloc]initWithTitle:@"  "
                                                           message:@"  "
                                                          delegate:self
                                                 cancelButtonTitle:@"  "
                                                 otherButtonTitles:@"  ", nil];
        
        //        
        void (^block)(NSInteger) = ^(NSInteger buttonIndex) {
            NSLog(@"    ");
        };
        
        objc_setAssociatedObject(alterView, &accociate_name_key, block, OBJC_ASSOCIATION_COPY);
    }
    
    - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
        void (^block)(NSInteger) = objc_getAssociatedObject(alertView, &accociate_name_key);
        block(buttonIndex);
    }
    

    이렇게 하면 응답 이벤트가 무엇인지 알고 싶으면 같은 방법으로 처리할 수 있지만 이런 방식은 순환 인용 문제와 관련이 있기 때문에 처리해야 한다는 것을 기억해야 한다.

    방법 교환


    시스템의 방법에 대해 어떤 방식을 호출하면 운행 기간에 해석하거나 바꿀 수 있는지,runtime가 제공하는 방법이 상호작용하는 흑과학 기술은 우리가 하위 클래스를 계승하지 않는 상황에서 운행 기간에 특정한 선택자에 대해 이 클래스 자체의 기능을 바꿀 수 있게 한다.잘 활용하면 방법교환(Method Swizzling)이 큰 도움이 될 것이다.
    방법 교환과 관련된 몇 가지runtime 방법:
    //         
    Method class_getInstanceMethod(Class cls, SEL name)
    //        
    Method class_getClassMethod(Clas cls, SEL name)
    //         
    const char *method_getTypeEncoding(Method m)
    //         IMP    
    IMP method_getImplementation(Method m) 
    //         IMP    
    IMP method_setImplementation(Method m, IMP imp) 
    //       IMP  
    void method_exchangeImplementations(Method m1, Method m2) 
    //            
    BOOL class_addMethod(Class cls, SEL name, IMP imp, const char * types) 
    //          IMP  
    IMP class_replaceMethod(Class cls, SEL _Nonnull name, IMP imp,  const char * types) 
    

    클래스마다 메시지를 전달할 수 있는 검색에 대응하기 위한 방법 목록이 있습니다. 방법 목록에는 이 열의 모든 선택 하위 이름과 IMP 포인터가 포함되어 있습니다.우리가 방법 교환을 하는 본질은 IMP 지침의 지향을 바꾸어 원래의 선택자에게 메시지를 전달할 때 실현된 기능을 바꾸는 것이다.
    그렇다면 이 방법을 실현할 수 있는 방법은 세 가지가 있다.
  • class_replaceMethod를 통해 원래의 방법의 IMP 바늘의 방향을 바꾼다.
  • method_exchangeImplementations를 통해 두 IMP 포인터의 방향을 교환합니다.
  • IMP 포인터를 method_setImplementation 재설정하여 구현합니다.

  • class_replaceMethod 교체
    사용자는 클래스의 방법 목록을 바꿀 수 있고, 선택자를 새로 추가할 수도 있고, 선택자의 대응 방법을 바꿀 수도 있다.class_replaceMethod그렇습니다.보통 load 방법에서 호출되며, 이 방법은 시스템 실행이 시작될 때 한 번만 실행됩니다.
    + (void)load {
        swizzleMethod([self class], @selector(viewWillAppear:), @selector(exchangeViewWillAppear:));
    }
    void swizzleMethod(Class class, SEL originalSelector, SEL swizzledSelector)
    {
      //         
        Method originalMethod = class_getInstanceMethod(class, originalSelector);
        Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
        //         ,         IMP      
        BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
      //          
       class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
     
    }
    - (void)exchangeViewWillAppear:(BOOL)animal {
        //       ,        viewWillAppear       
        [self exchangeViewWillAppear:animal];
    }
    

    방법 교환의 본뜻은 어떤 방법에 일부 기능을 추가하는 것이다. 예를 들어 매점, 수조가 경계를 넘거나 디버깅을 할 때 몇 가지 일을 통일적으로 처리하는 등이다. 사용 방법 교환은 매우 편리하다.
    방법교환은 일반적으로 프로그램이 실행될 때 한 번만 호출할 수 있기 때문에 호출이 없으면 스스로 처리해야 한다.이상의 코드를 통해 컨트롤러가 viewWillAppear에 있을 때 exchangeViewWillAppear에 들어갈 수 있고 호출[self exchangeViewWillAppear:animal];에 있을 때 진정으로 실행할 수 있다viewWillAppear.따라서 exchangeViewWillAppear에 사용자 정의 처리나 기능을 추가할 수 있다.
    method_exchangeImplementations 교체
    이 방법은 사용법이 비교적 간단하고 직접 교환된다.
    void swizzleMethod(Class class, SEL originalSelector, SEL swizzledSelector)
    {
      //         
        Method originalMethod = class_getInstanceMethod(class, originalSelector);
        Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
      //     IMP  
        method_exchangeImplementations(originalMethod, swizzledMethod);
    }
    

    method_setImplementation 대체
    IMP 포인터를 이용하여 방법의 대상을 조작하고 방법의 교환을 실현한다. 이런 방법은 시스템이 사용하는 IMP 포인터는 되돌아오는 값이 없다. 왜냐하면 대상을 직접 가져오면 컴파일 실패나 오류 보고가 발생하기 때문이다.따라서 선택한 하위 유형과 매개변수에 따라 IMP 포인터의 유형을 스스로 정의하는 방식이 있습니다.
    //         IMP
    typedef id (*_IMP) (id, SEL, ...);
    //         IMP
    typedef void (*_VIMP) (id, SEL, ...);
    
    void swizzleMethod(Class class, SEL originalSelector, SEL swizzledSelector)
    {
      //         
        Method originalMethod = class_getInstanceMethod(class, originalSelector);
        //        _IMP,  IMP    
        _IMP origialIMP = (_IMP)method_getImplementation(originalMethod);
        //     IMP       
        method_setImplementation(originalMethod, imp_implementationWithBlock(^(id target, SEL action) {
           //         
            origialIMP(target, action);
        }));
        
    }
    

    IMP 포인터를 다시 가리키는 방식으로 설정하여 교환하는 기능을 실현합니다.

    좋은 웹페이지 즐겨찾기