iOS 메시지 전송 및 전송 예시 상세 설명
Objective-C 는 동적 언어 로 많은 정적 언어 를 컴 파일 과 링크 시기 에 하 는 일 을 실행 할 때 처리 합 니 다.이런 특성 을 갖 출 수 있 는 이 유 는 런 타임 이라는 라 이브 러 리 를 떠 날 수 없다.런 타임 은 운영 시기 에 어떻게 호출 방법 을 찾 느 냐 는 문 제 를 잘 해결 했다.다음은 더 이상 할 말 이 없 으 니 같이 공부 합 시다.
메시지 전송
Objective-C 에서 대상 에 게 메 시 지 를 보 내 는 방법 을 호출 합 니 다.
// MyClass
@interface MyClass: NSObject
- (void)printLog;
@end
@implementation MyClass
- (void)printLog {
NSLog(@"print log !");
}
@end
MyClass *myClass = [[MyClass alloc] init];
[myClass printLog];
// : print log !
위 코드 의[my Class printLog]도 이렇게 쓸 수 있 습 니 다.
((void (*)(id, SEL))(void *) objc_msgSend)(myClass, @selector(printLog));
[my Class printLog]컴 파일 을 거 친 후 obbc 를 호출 합 니 다.msgSend 방법.이 방법의 문서 정 의 를 봅 시다.
id objc_msgSend(id self, SEL op, ...);
self:메시지 수신 자 op:메시지 의 방법 명,C 문자열...:매개 변수 목록Runtime 은 어떻게 실례 방법의 구체 적 인 실현 을 찾 습 니까?
기초 개념
말 하기 전에 우 리 는 먼저 몇 가지 기초 개념 을 이해 해 야 한다.Objective-C 는 표면적 으로 대상 을 향 한 언어 이 고 대상 은 실례 대상,클래스 대상,클래스 대상 과 루트 대상 으로 나 뉜 다.그것들 은 isa 라 는 지침 을 통 해 연 결 됩 니 다.구체 적 인 관 계 는 다음 과 같 습 니 다.
우리 가 위의 코드 를 예 로 들 면:
MyClass *myClass = [[MyClass alloc] init];
상호 간 의 관 계 를 정리 하 다.위의 그림 의 위치 관 계 는 다음 과 같 습 니 다.
이어서 우 리 는 코드 로 위의 관 계 를 검증 합 니 다.
MyClass *myClass = [[MyClass alloc] init];
Class class = [myClass class];
Class metaClass = object_getClass(class);
Class metaOfMetaClass = object_getClass(metaClass);
Class rootMetaClass = object_getClass(metaOfMetaClass);
Class superclass = class_getSuperclass(class);
Class superOfSuperclass = class_getSuperclass(superclass);
Class superOfMetaOfSuperclass = class_getSuperclass(object_getClass(superclass));
NSLog(@"MyClass :%p",myClass);
NSLog(@"MyClass :%p",class);
NSLog(@"MyClass :%p",metaClass);
NSLog(@"MyClass :%p",metaOfMetaClass);
NSLog(@"MyClass :%p",rootMetaClass);
NSLog(@"MyClass :%@",class_getSuperclass(class));
NSLog(@"MyClass :%@",superOfSuperclass);
NSLog(@"MyClass :%@",superOfMetaOfSuperclass);
NSLog(@"NSObject :%p",object_getClass([NSObject class]));
NSLog(@"NSObject :%@",[[NSObject class] superclass]);
NSLog(@"NSObject :%@",[object_getClass([NSObject class]) superclass]);
// :
MyClass :0x60c00000b8d0
MyClass :0x109ae3fd0
MyClass :****0x109ae3fa8
MyClass :****0x10ab02e58**
MyClass :0x10ab02e58
MyClass :NSObject
MyClass :(null)
MyClass :NSObject
NSObject :0x10ab02e58
NSObject :(null)
NSObject :NSObject
출력 결 과 는 우리 의 결론 에 완전히 부합 한 다 는 것 을 알 수 있 습 니 다!이제 우 리 는 각종 대상 간 의 관 계 를 알 수 있다.
인 스 턴 스 대상 은 isa 지침 을 통 해 클래스 대상 Class 를 찾 습 니 다.클래스 대상 역시 isa 지침 을 통 해 원 류 대상 을 찾 습 니 다.원류 대상 도 isa 지침 을 통 해 뿌리 원류 대상 을 찾 습 니 다.마지막 으로 루트 대상 의 isa 지침 은 자신 을 가리킨다.NSObject 는 전체 메시지 체제 의 핵심 이 고 절대 다수의 대상 이 이 를 계승 한 다 는 것 을 알 수 있다.
흐름 찾기
위 에서 언급 한 바 와 같이 Objective-C 방법 은 obbc 로 컴 파일 됩 니 다.msgSend,이 함 수 는 두 개의 기본 매개 변수,id 형식의 self,SEL 형식의 op 이 있 습 니 다.우리 먼저 id 의 정 의 를 봅 시다.
typedef struct objc_object *id;
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
우 리 는 볼 수 있다.object 구조 체 에는 Class 형식 을 가리 키 는 isa 포인터 만 있 습 니 다.Class 의 정 의 를 다시 봅 시다.
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class _Nullable super_class OBJC2_UNAVAILABLE;
const char * _Nonnull name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
안에 많은 인자 가 있 습 니 다.이 줄 을 눈 에 띄 게 볼 수 있 습 니 다.
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
이름 을 봐 도 쉽게 이해 할 수 있 습 니 다.이 methodLists 는 방법 목록 을 저장 하 는 데 사 용 됩 니 다.우리 다시 보 자 obbcmethod_list 이 구조 체:
struct objc_method_list {
struct objc_method_list * _Nullable obsolete OBJC2_UNAVAILABLE;
int method_count OBJC2_UNAVAILABLE;
#ifdef __LP64__
int space OBJC2_UNAVAILABLE;
#endif
/* variable length structure */
struct objc_method method_list[1] OBJC2_UNAVAILABLE;
}
안의 obbcmethod,즉 우리 가 잘 아 는 Method:
struct objc_method {
SEL _Nonnull method_name OBJC2_UNAVAILABLE;
char * _Nullable method_types OBJC2_UNAVAILABLE;
IMP _Nonnull method_imp OBJC2_UNAVAILABLE;
}
Method 에 세 개의 인자 가 저장 되 어 있 습 니 다:
MyClass *myClass = [[MyClass alloc] init];
[myClass printLog];
((void (*)(id, SEL))(void *) objc_msgSend)(myClass, @selector(printLog));
위의 글 에서 우 리 는 인 스 턴 스 대상 이 isa 지침 을 통 해 클래스 대상(Class)에 저 장 된 방법 목록 의 구체 적 인 실현 을 찾 았 다 는 것 을 알 고 있 습 니 다.
예 를 들 면:
MyClass *myClass = [[MyClass alloc] init];
[myClass printLog];
printLog 방법 은 MyClass 에 저 장 된 것 으로 이해 할 수 있 습 니 다.그렇다면 이런 방법 이 라면 어디 에 저장 되 어 있 을 까?
클래스 를 돌 이 켜 보 겠 습 니 다. 정의:
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class _Nullable super_class OBJC2_UNAVAILABLE;
const char * _Nonnull name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
이 줄 을 발견 할 수 있 습 니 다:
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
이곳 의 isa 역시 Class 를 가리 키 는 지침 입 니 다.위의 글 에서 우 리 는 유형 대상 의 isa 지침 이 원류 대상 을 가리 키 는 것 임 을 알 게 되 었 다.그렇게 어렵 지 않다.클래스 대상 의 클래스 방법 은 클래스 대상 에 저 장 됩 니 다!
클래스 대상 과 클래스 대상 은 모두 Class 유형 은 서비스의 대상 이 다 를 뿐 입 니 다.원류 대상 을 찾 으 면 자 연 스 럽 게 원류 대상 중의 methodLists 를 찾 을 수 있 습 니 다.다음은 실례 대상 의 방법 과 같은 절 차 를 찾 을 수 있 습 니 다.
부모 클래스 에 대하 여(superclass)
Objective-C 에서 하위 클래스 는 하나의 방법 을 호출 합 니 다.하위 클래스 가 실현 되 지 않 으 면 부모 클래스 가 실 현 됩 니 다.부모 클래스 의 실현 을 호출 합 니 다.위의 글 에서 methodLists 를 찾 은 후 Method 를 찾 는 과정 은 다음 과 같 습 니 다.
어떻게 방법 찾기 의 효율 을 높 입 니까?
위의 글 에서 우 리 는 방법 이 isa 지침 을 통 해 Class 의 methodLists 를 찾 는 것 임 을 대충 알 고 있 습 니 다.만약 하위 클래스 가 대응 하 는 방법 을 실현 하지 못 한다 면,부 류 를 따라 찾 아 볼 것 이다.전체 공사 에 수만 억 개의 방법 이 있 을 수 있 는데 어떻게 성능 문 제 를 해결 합 니까?
예 를 들 면:
for (int i = 0; i < 100000; ++i) {
MyClass *myObject = myObjects[i];
[myObject methodA];
}
이러한 고주파 호출 methodA 는 호출 할 때마다 옮 겨 다 녀 야 한다 면 성능 이 매우 떨어진다.그래서 Class Cache 메커니즘 을 도입 했다.Class Cache 는 하나의 방법 이 호출 되면 나중에 호출 될 가능성 이 높다 고 생각 합 니 다.
방법 을 찾 을 때 캐 시 에서 찾 아 직접 되 돌려 줍 니 다.찾 을 수 없습니다.Class 방법 목록 에서 찾 으 세 요.
위의 글 에서 Class 의 정의 에서 우 리 는 발견 할 수 있 습 니 다. cache:
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;
캐 시 는 클래스 에 존재 하 는 것 을 설명 합 니 다.각 클래스 는 하나의 방법 으로 캐 시 를 합 니 다.모든 종류의 object 가 한 부 를 저장 하 는 것 이 아 닙 니 다.메시지 전달
방법 목록(methodLists)에서 해당 하 는 selector 를 찾 지 못 하면?
// ViewController.m ( myTestPrint )
[self performSelector:@selector(myTestPrint:) withObject:@", !"];
시스템 은 세 차례 의 보완 기 회 를 제공 할 것 이다.처음
+ (BOOL)resolveInstanceMethod:(SEL)sel {} ( )
+ (BOOL)resolveClassMethod:(SEL)sel {} ( )
이 두 가지 방법 중 하 나 는 실례 를 겨냥 한 방법 이다.유형 에 맞 는 방법반환 값 은 모두 Bool 입 니 다.사용 예시:
// ViewController.m
void myMethod(id self, SEL _cmd,NSString *nub) {
NSLog(@"ifelseboyxx%@",nub);
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
if (sel == @selector(myTestPrint:)) {
#pragma clang diagnostic pop
class_addMethod([self class],sel,(IMP)myMethod,"v@:@");
return YES;
}else {
return [super resolveInstanceMethod:sel];
}
}
우 리 는 resolve InstanceMethod:방법 에서 class 를 이용 해 야 합 니 다.addMethod 방법,구현 되 지 않 은 my TestPrint:my Method 에 연결 하면 퍼 가기 가 완료 되 고 마지막 으로 YES 로 돌아 갑 니 다.두 번 째
- (id)forwardingTargetForSelector:(SEL)aSelector {}
이 방법 은 id 를 되 돌려 달라 고 요구 합 니 다.장면 을 사용 하 는 것 은 보통 A 류 의 특정한 방법 을 B 류 의 실현 에 전달 하 는 것 이다.사용 예시:
Person 클래스 에 전송 하고 싶 은-my TestPrint:방법 중:
@interface Person : NSObject
@end
@implementation Person
- (void)myTestPrint:(NSString *)str {
NSLog(@"ifelseboyxx%@",str);
}
@end
// ViewController.m
- (id)forwardingTargetForSelector:(SEL)aSelector {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
if (aSelector == @selector(myTestPrint:)) {
#pragma clang diagnostic pop
return [Person new];
}else{
return [super forwardingTargetForSelector:aSelector];
}
}
세 번 째
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {}
- (void)forwardInvocation:(NSInvocation *)anInvocation {}
첫 번 째 는 한 가지 방법 으로 서명 하고 두 번 째 방법 으로 구체 적 인 실현 을 전달 해 야 한다.두 사람 은 서로 의존 하고 정확 한 방법 으로 서명 해 야 두 번 째 방법 을 실행 할 수 있다.이번 리 트 윗 작용 은 두 번 째 와 유사 하 게 A 류 의 어떤 방법 을 B 류 의 실현 에 리 트 윗 하 는 것 이다.다른 것 은 세 번 째 퍼 가기 가 두 번 째 퍼 가기 보다 더욱 유연 하 다 는 것 이다.forwardingTargetForSelector:한 대상 에 게 만 고정 적 으로 퍼 가기;forwardInvocation: 여러 대상 에 게 전달 할 수 있 습 니 다.
인 스 턴 스 사용:
Person 류 및 Animal 류 에 전달 하고 싶 은-my TestPrint:방법 중:
@interface Person : NSObject
@end
@implementation Person
- (void)myTestPrint:(NSString *)str {
NSLog(@"ifelseboyxx%@",str);
}
@end
@interface Animal : NSObject
@end
@implementation Animal
- (void)myTestPrint:(NSString *)str {
NSLog(@"tiger%@",str);
}
@end
// ViewController.m
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
if (aSelector == @selector(myTestPrint:)) {
#pragma clang diagnostic pop
return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
}
return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
Person *person = [Person new];
Animal *animal = [Animal new];
if ([person respondsToSelector:anInvocation.selector]) {
[anInvocation invokeWithTarget:person];
}
if ([animal respondsToSelector:anInvocation.selector]) {
[anInvocation invokeWithTarget:animal];
}
}
⚠️ 세 번 째 기회 가 되 어도 해당 하 는 실현 을 찾 지 못 하면 crash:
unrecognized selector sent to instance 0x7f9f817072b0
총결산여기까지 우 리 는 메시지 발송 과 전달 과정 을 대충 알 수 있 습 니 다.절차 도 를 동봉 합 니 다.
자,이상 이 이 글 의 전체 내용 입 니 다.본 논문 의 내용 이 여러분 의 학습 이나 업무 에 어느 정도 참고 학습 가치 가 있 기 를 바 랍 니 다.궁금 한 점 이 있 으 시 면 댓 글 을 남 겨 주 셔 서 저희 에 대한 지지 에 감 사 드 립 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Swift의 패스트 패스Objective-C를 대체하기 위해 만들어졌지만 Xcode는 Objective-C 런타임 라이브러리를 사용하기 때문에 Swift와 함께 C, C++ 및 Objective-C를 컴파일할 수 있습니다. Xcode는 S...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.