Runtime--Message、Message Forwarding

9215 단어
간단한 소개
Objective-C 프로그램은 세 가지 차원에서runtime 시스템과 상호작용할 수 있다. Objective-C Source Code, NSObject Methods, Runtime Functions이다.
Objective-C Source Code
이 단계에서runtime 함수는 자동으로 호출됩니다.Runtime function의 주요 기능 중 하나는 메시징과 같이 메시지를 보내는 것이다.
NSObject Methods
거의 모든 클래스는 NSObject 클래스를 상속하기 때문에 정의된 방법을 가지고 있습니다.NSObject의 방법은 하위 클래스의 표현 방식을 정의했지만, 소수의 경우 템플릿만 정의했을 뿐 모든 구체적인 실현은 정의하지 않았다.예를 들어 실례적인 방법인 description, 기본 사항인 프린터 클래스 이름과 메모리 주소;하위 클래스는 다시 작성하여 더 많은 설명 정보를 반환할 수 있습니다 (예: NSArray 구현).
그 중 일부 방법은 실행 시 정보를 얻을 수 있다. 예를 들어
//  Class
+ (Class)class;
//       
- (BOOL)isKindOfClass:(Class)aClass;
- (BOOL)isMemberOfClass:(Class)aClass;
//      
- (BOOL)conformsToProtocol:(Protocol *)aProtocol;
//        
- (BOOL)respondsToSelector:(SEL)aSelector;
//      
- (IMP)methodForSelector:(SEL)aSelector;

Runtime Functions
Runtime system은 동적 공유 라이브러리로 일련의 함수 인터페이스와 데이터 구조체를 헤드 파일에 제공하고 경로는 /usr/include/objc이다.자세한 내용은 Objective-C Runtime Reference를 참조하십시오.
Messaging
Objective-C는 실행 기간이 되어서야 귀속 방법의 실현을 실현하고 컴파일러는 방법 호출을 메시지로 전송objcmsgSend.예를 들어 [receiver message]
//      :receiver, selector
objc_msgSend(receiver, selector)

//      
objc_msgSend(receiver, selector, arg1, arg2, ...)

objc_msgSend 프로세스
그중 objcmsgSend의 동적 연결 과정은 다음과 같습니다. 1, 우선 selector에 따라 Method의implementation을 찾습니다.같은 종류가 아니기 때문에 같은 방법을 실현할 수 있기 때문에 매개 변수receiver에 따라 찾습니다.2. 그리고receiver와 파라미터를 selector에 전달합니다.3. 마지막 반환값.참고: 컴파일러가 자동으로 objc 호출msgSend, 당신이 쓴 코드에서 직접 호출해서는 안 됩니다.
메시지 발송의 관건은 컴파일러가 모든 클래스와 대상에게 생성한 구조체이다. 각 클래스의 구조체는 두 가지 중요한 부분을 포함한다. 첫째, 부류의 바늘 2, 방법 스케줄러(dispatch table): 각 클래스가 정의한 방법의 주소 입구
지침
새로운 대상을 생성할 때마다 메모리alloc가 완료되고 실례 변수가 초기화됩니다.그 중에서 첫 번째 실례 변수는 이사 지침으로 대상이 속한 클래스의 구조체를 가리키며 속한 클래스를 통해 전체 계승 체인을 찾을 수 있다.
엄밀히 말하면 이사는 Objective-C에 속하지 않지만 Objective-C runtime 시스템에는 필수적이다.NSObject 클래스를 상속하는 경우 모두 isa 변수를 자동으로 생성하며 자신의 루트 클래스를 생성하지 않도록 합니다.
대상이 방법을 호출할 때 objcmsgSend는 이사에 따라 대상이 속한 클래스를 찾고 디스패치 테이블에서 selector를 찾으며 없으면 부모 포인터에 따라 부모 클래스를 계속 찾고 상속 관계 체인에 따라 NSObject까지 찾습니다.selector를 찾으면 바로 호출합니다.이 과정은 운행 기간에 완성된다. 이른바 동적 귀속이다.
빠른 방법으로 속도를 호출하기 위해서,runtime 시스템은 모든 클래스에 독립된 공간을 열어 selector와addresses를 캐시합니다.디스패치 테이블을 찾기 전에 캐시를 찾습니다. (이미 호출된 방법)만약 selector가 캐시에 있다면, 호출 속도는 함수 호출보다 조금 느릴 뿐입니다.프로그램이 모든 방법을 캐시할 수 있는 충분한 시간을 실행하면 거의 모든 방법을 캐시에서 찾을 수 있습니다.그 중에서 캐시는 새로운 방법을 수납하기 위해 동적으로 증가할 수 있다.
동적 바인딩 피하기
동적 연결을 피하는 유일한 방법은 Method의address를 가져와 함수처럼 직접 호출하는 것입니다.같은 방법을 여러 번 사용하지만 시간 비용을 줄이고 싶은 장면에 사용한다.다음 방법으로address를 얻을 수 있습니다
//1、      ,aSelector     
//2、   ,aSelector    
- (IMP)methodForSelector:(SEL)aSelector;

그리고 포인터를 통해 방법을 호출하지만, 이 포인터는 반환값과 매개 변수 형식을 포함하여 적당한 함수 포인터로 전환해야 합니다.간단한 Data 클래스 설명
@interface Data : NSObject

-(void)ivarMethod;
+(void)classMethod;

@end

대량의 for순환은 시간을 절약하는 것을 가장 잘 나타낼 수 있다. 이상의 방법은 다른 영향을 피하기 위해 공허하게 실현된다.인스턴스 메서드는 다음과 같이 호출됩니다.
//    
Data *data = [[Data alloc] init];
    
//    
for (int i = 0; i < 1000000; i++)
{
   if (i == 0 || i == 999999)
   {
       NSLog(@"***   ***");
   }
   [data ivarMethod];
   
}
    
//        
void (*setter)(id, SEL)= (void (*)(id, SEL))[data methodForSelector:@selector(ivarMethod)];
for (int i = 0; i < 1000000; i++)
{
   if (i == 0 || i == 999999)
   {
       NSLog(@"***   ***");
   }
   setter(self,@selector(ivarMethod));
   
}

//                      

클래스 메서드는 다음과 같이 호출됩니다.
//    
for (int i = 0; i < 1000000; i++)
{
   if (i == 0 || i == 999999)
   {
       NSLog(@"***   ***");
   }
   [Data classMethod];
   
}
    
//        
Class class_data = [Data class];
    
SEL sel_data = @selector(classMethod);

IMP imp_class = [class_data methodForSelector:sel_data];
    
void (*setter_classMethod)(id, SEL)= (void (*)(id, SEL))imp_class;
    
for (int i = 0; i < 1000000; i++)
{
   if (i == 0 || i == 999999)
   {
       NSLog(@"***   ***");
   }
   setter_classMethod(class_data ,sel_data);
   
}

//                      

objc_msgSend 호출
//               
void objc_msgSend(void /* id self, SEL op, ... */ )
//     
[Data classMethod];
((void(*)(id,SEL))objc_msgSend)([Data class],@selector(classMethod));
    
//      
Data *data = [[Data alloc] init];
[data ivarMethod];
((void(*)(id,SEL))objc_msgSend)(data,@selector(ivarMethod));

//    
  classMethod
  classMethod

  ivarMethod
  ivarMethod

Message Forwarding
대상에게 처리할 수 없는 메시지를 보내면 오류가 발생하지만 런타임 시스템은 알 수 없는 메시지를 처리할 수 있는 두 번째 기회를 줍니다.과정을 소개하기 전에 두 가지 핵심 방법을 먼저 설명한다.
//  1
- (void)forwardInvocation:(NSInvocation *)anInvocation OBJC_SWIFT_UNAVAILABLE("");

이 방법에 대한 설명: 1. 이 방법은 기본 NSObject 성명에 의해 실현된다.모든 종류를 사용할 수 있다는 뜻이다.2. 기본적으로 호출doesNotRecognizeSelector: 방법을 실행하고 이상 NSInvalid Argument Exception을 던지면 프로그램이 종료됩니다.3, 다시 이 방법 을 실현 할 수 있 는 메시지 전달 Message Forwarding 이다.4. 대상이 자체적으로 이루어지지 않는 방법에 응답하려면 methodSignatureForSelector: 방법을 다시 써야 한다.메시지 전달 메커니즘은 method signature 정보를 가져와 NSInvocation을 생성한 다음에 호출 방법 15를 사용해야 하기 때문에 이 방법의 상세한 재작성 과정은 다음 문장을 보십시오.
//  2
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector OBJC_SWIFT_UNAVAILABLE("");

이 방법에 대한 설명: 1. 이 방법은 기본 NSObject 성명에 의해 실현된다.모든 종류를 사용할 수 있다는 뜻이다.2.aSelector 방면명;receiver는 실례 대상이고 방법은 실례 방법이다.receiver는 클래스이고 방법은 클래스입니다.3. 이 방법은protocols를 실현하는 데 자주 사용되는 것 외에 NSInvocation 대상을 만들 때 예를 들어 메시지 전달 메시지 forwarding에 자주 사용된다.4. 이 방법의 상세한 재작성 과정은 다음 문장을 보십시오.
메시지 전달 안내
정상 프로세스: 대상이 자신이 실현하지 못한 방법 (즉 알 수 없는 메시지를 보내는 것) 을 호출하면runtime는 대상의 forwardInvocation: 방법을 호출하고 NSObject의 기본값은 이상을 던지고 프로그램이 종료됩니다.전송 과정: 만약에 대상이 자신이 실현하지 못한 방법(즉 미지의 메시지를 보내는 것)을 호출하면runtime는 대상의 forwardInvocation: 방법을 호출하고 대상은 다시 이 방법을 사용하여 미지의 메시지를 스스로 처리한다.
재작성 과정
1、forwardInvocation:
방법의 실현에는 두 가지 임무가 있다. 첫째, 상응하는 메시지를 전달할 수 있는 대상을 확정하고, 서로 다른 메시지는 서로 다른 대상을 전달할 수 있다. 둘째, anInvocation으로 메시지를 전달하고, 매개 변수를 전달한다. 예를 들어
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
    if ([someOtherObject respondsToSelector:
            [anInvocation selector]])
        [anInvocation invokeWithTarget:someOtherObject];
    else
        [super forwardInvocation:anInvocation];
}

1. 모든 반환값은 정상적으로 반환된다. 2. 이 방법은 메시지 전송 센터로 할 수 있다. 미지의 메시지를 처리한 다음에 전달한다.메시지 전환 센터: 모든 메시지를 같은 목표로 전송하고 A를 B로 전환하며 차단(응답 없음 오류 없음), 다양한 메시지 처리를 일종의 응답으로 한다.
2、methodSignatureForSelector:
예컨대
- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector
{
    NSMethodSignature* signature = [super methodSignatureForSelector:selector];
    if (!signature) {
       signature = [surrogate methodSignatureForSelector:selector];
    }
    return signature;
}

다음은 예를 들어, 먼저 새 프로젝트에 ViewController 클래스가 있고, 그 다음에 Data 클래스가 새로 만들어집니다
//       
@interface Data : NSObject

-(void)ivarMethod;
@end

-(void)ivarMethod
{
    NSLog(@"  ivarMethod");
}

ViewController
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
    NSString *sel = NSStringFromSelector(anInvocation.selector);
    NSLog(@"      %@",sel);
    
    if ([sel isEqualToString:@"ivarMethod"])
    {
        [anInvocation invokeWithTarget:[[Data alloc] init]];

    }
    else
    {
        [super forwardInvocation:anInvocation];
    }
}

-(NSMethodSignature*)methodSignatureForSelector:(SEL)selector
{
    NSMethodSignature *signature = [super methodSignatureForSelector:selector];
    
     //      
    if (! signature)
    {
        //       
        if (sel_isEqual(selector, @selector(ivarMethod)))
        {
            signature = [[[Data alloc] init] methodSignatureForSelector:selector];
        }
       
    }
    return signature;
}

호출 방법
- (void)viewDidLoad 
{
[super viewDidLoad];

[(Data*)self ivarMethod];
 
}
  
//    
      ivarMethod
  ivarMethod

메시지 전달 및 상속
forwarding과 계승은 비슷하지만 NSObject는 두 개념을 혼동한 적이 없다.예를 들어 respondsToSelector:isKindOfClass: 검색 과정은 계승 체인에서만 진행되고 전송 체인에서 진행되지 않는다.
if ( [aWarrior respondsToSelector:@selector(negotiate)] )
    ...

대부분의 경우 NO로 돌아가지만 만약에 에이전트 클래스를 구축하거나 특정한 클래스의 기능을 확대했다면 이때 전송 메커니즘은 계승처럼 다시 써야 한다respondsToSelector:isKindOfClass:. 예를 들어
- (BOOL)respondsToSelector:(SEL)aSelector
{
    if ( [super respondsToSelector:aSelector] )
        return YES;
    else {
        /* Here, test whether the aSelector message can     *
         * be forwarded to another object and whether that  *
         * object can respond to it. Return YES if it can.  */
    }
    return NO;
}

메시지 전달과 다중 계승
Objective-C 자체는 다중 상속을 지원하지 않지만 메시지 전달은 비슷한 효과를 낼 수 있습니다.메시지 전달은 다중 계승의 대부분 기능을 제공하지만 양자는 본질적으로 다르다. 다중 계승은 서로 다른 기능을 한데 통합시켜 큰 추세가 된다.메시지 전송은 기능을 서로 다른 대상에 분산시켜 실현하고 작아지는 추세(하지만 호출층에 대해 투명하게 보이지 않는다).주의: 메시지 전송은 계승을 교체하는 데 사용되지 않습니다.
참고 문헌: Messaging, Message Forwarding, Objective-C Runtime Reference.

좋은 웹페이지 즐겨찾기