iOS 런타임의 간단한 사용

8704 단어

메시지 보내기


개발 사용 장면: 노출되지 않은 방법, 전제 조건, 이 방법은 이미 실현되었다.
  • 가져오기#import
  • -> Build Settings -> Enable Strict Checking of objc_msgSend Calls -> No
  • objc_msgSend 예외 처리
  • 방법 호출의 본질은 바로 대상에게 메시지를 보내는 것이다.
  • objc_msgSend, 대상만 메시지를 보낼 수 있기 때문에 objc로 시작합니다.

  • objc_msgSend 호출 방법 방법 방법 설명 필요 없음objcmsgSend 매개 변수
  • 첫 번째는 누구에게 메시지를 보낼지,
  • 두 번째는 보낼 메시지, 즉 실행하는 방법
  • 이다.
  • 앞으로 전달할 수 있는 매개 변수
  • CallMeBoy 클래스 코드는 다음과 같습니다.
    #import 
    @interface CallMeBoy : NSObject
    //-(void)callMeNow;
    //+(void)callMeNow;
    //-(NSString *)callMeNowWithName:(NSString *)name;
    @end
    
    #import "CallMeBoy.h"
    @implementation CallMeBoy
    -(void)callMeNow{
        NSLog(@"-%s",__func__);
    }
    +(void)callMeNow{
        NSLog(@"+%s",__func__);
    }
    -(NSString *)callMeNowWithName:(NSString *)name{
        if ([name isEqualToString:@" "]) {
            return @"GLW";
        }
        return @"HZ";
    }
    @end
    

    1. 대상 방법 호출

    CallMeBoy *callMe = [[CallMeBoy alloc] init];
    //1  
    [callMe callMeNow];
    //   ( )
    objc_msgSend(callMe,@selector(callMeNow));
    ((void (*) (id, SEL)) (void *)objc_msgSend)(callMe, @selector(callMeNow));
    ((void (*) (id, SEL)) (void *)objc_msgSend)(callMe, sel_registerName("callMeNow"));
    
    // 
    NSString *str  = [callMe callMeNowWithName:@" "];
    NSString *str1 = objc_msgSend(callMe, @selector(callMeNowWithName:), @" ");
    NSString *str2 = ((NSString* (*)(id,SEL,NSString *))objc_msgSend)(callMe, @selector(callMeNowWithName:), @" ");
    NSLog(@"%@--%@--%@",str,str1,str2);
    

    2. 클래스 방법 호출

    //2  
    [CallMeBoy callMeNow];// 
    [[CallMeBoy class] callMeNow];// 
    objc_msgSend([CallMeBoy class],@selector(callMeNow));
    objc_msgSend([CallMeBoy class],sel_registerName("callMeNow"));
    ((void (*) (id, SEL)) (void *)objc_msgSend)([CallMeBoy class], @selector(callMeNow));
    ((void (*) (id, SEL)) (void *)objc_msgSend)([CallMeBoy class], sel_registerName("callMeNow"));
    

    2. 교환 방법


    개발 사용 장면: 시스템 자체의 방법 기능이 부족하고 시스템 자체의 방법에 기능을 확장하고 원래의 기능을 유지한다.
  • 방식1: 계승 시스템의 종류, 재작성 방법.
  • 방식2:runtime를 사용하여 교환 방법.

  • 예: [UIImage imageNamed:@" "]; 이 그림은 비어 있을 때 인쇄2017-08-17 14:27:35.599 SocketGG[13015:1396365] 창설UIImage분류#import "UIImage+image.h" 코드는 다음과 같다.
    #import 
    @interface UIImage (image)
    @end
    
    #import "UIImage+image.h"
    #import 
    @implementation UIImage (image)
    +(void)load{
        //  
        //   imageWithName 
        Method imageWithName = class_getClassMethod(self, @selector(imageWithName:));
        //   imageNamed 
        Method imageName = class_getClassMethod(self, @selector(imageNamed:));
        //  , 
        method_exchangeImplementations(imageWithName, imageName);
    }
    //  imageNamed, , super.
    //  
    + (instancetype)imageWithName:(NSString *)name {
        //  imageWithName, imageName
        UIImage *image = [self imageWithName:name];
        if (image == nil) {
            NSLog(@" ");
        }
        return image;
    }
    @end
    

    3. 동적 추가 방법


    개발 사용 장면: 만약에 하나의 클래스 방법이 매우 많으면 클래스를 메모리에 불러올 때도 자원을 소모하기 때문에 모든 방법에 맵을 생성해야 한다. 동태적으로 특정한 클래스에 주고 방법을 추가하여 해결할 수 있다.
    이 메커니즘에서 다루는 방법은 주로 두 가지가 있다.
    + (BOOL)resolveInstanceMethod:(SEL)sel
    + (BOOL)resolveClassMethod:(SEL)sel
    

    CallMeBoy 분류 코드는 다음과 같습니다classaddMethod 네 번째 매개 변수 의미 홈페이지 링크
    #import "CallMeBoy+CallWhat.h"
    #import 
    
    // void(*)() //  
    void callMeBaby(id self,SEL _cmd) {
        NSLog(@"%@ %@",self,NSStringFromSelector(_cmd));
    }
    //  
    void callYouThen(id self, SEL _cmd, NSString *name) {
        NSLog(@"call you %@ ", name);
    }
    void callYouThenClass(id self, SEL _cmd, NSString *name) {
        NSLog(@"call you %@ class", name);
    }
    @implementation CallMeBoy (CallWhat)
    
    +(BOOL)resolveInstanceMethod:(SEL)sel{
        if (sel == @selector(callMeBaby)) {
            //  : 
            //  : 
            //  : ( )
            //  : ,( + ) v:void @: ->self : SEL->_cmd
            class_addMethod(self, sel, (IMP)callMeBaby, "v@:");
            return YES;
        }
        if (sel == @selector(callName)) {
            class_addMethod([self class], sel, (IMP)callYouThen, "v@:@");
            return YES;
        }
        return [super resolveInstanceMethod:sel];
    }
    +(BOOL)resolveClassMethod:(SEL)sel{
        if (sel == @selector(callNameClass)) {
            class_addMethod(objc_getMetaClass("CallMeBoy"), sel,(IMP)callYouThenClass,"v#:@");
            return YES;
        }
        return [super resolveClassMethod:sel];
    }
    
    @end
    

    외부 호출 방법
    // 
    [callMe performSelector:@selector(callMeBaby)];
    objc_msgSend(callMe,@selector(callMeBaby));
    objc_msgSend(callMe,@selector(callName),@" ");
    [callMe performSelector:@selector(callName) withObject:@" "];
    // 
    objc_msgSend([CallMeBoy class], @selector(callNameClass), @" ");
    

    인쇄 결과
    2017-08-17 16:13:38.611 SocketGG[18190:1936576]  callMeBaby
    2017-08-17 16:13:38.612 SocketGG[18190:1936576]  callMeBaby
    2017-08-17 16:13:38.612 SocketGG[18190:1936576] call you   
    2017-08-17 16:13:38.612 SocketGG[18190:1936576] call you   
    2017-08-17 16:13:42.037 SocketGG[18190:1936576] call you   class
    

    확장: performSelector Cocoa 내장에는 두 개의 매개 변수만 지원되고 여러 개의 매개 변수는 처리됩니다.


    증가 방법
    -(id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2 withObject:(id)object3 withObject:(id)object4 withObject:(id)object5{
        NSMethodSignature *meSig = [self methodSignatureForSelector:aSelector];
        if (meSig) {
            NSInvocation* invo = [NSInvocation invocationWithMethodSignature:meSig];
            [invo setTarget:self];
            [invo setSelector:aSelector];
            if (object1) {
                [invo setArgument:&object1 atIndex:2];
            }
            if (object2) {
                [invo setArgument:&object2 atIndex:3];
            }
            if (object3) {
                [invo setArgument:&object3 atIndex:4];
            }
            if (object4) {
                [invo setArgument:&object4 atIndex:5];
            }
            if (object5) {
                [invo setArgument:&object5 atIndex:6];
            }
            [invo invoke];
            if (meSig.methodReturnLength) {
                id anObject;
                [invo getReturnValue:&anObject];
                return anObject;
            } else {
                return nil;
            }
        } else {
            return nil;
        }
    }
    

    사용
    void callYouThenMany(id self, SEL _cmd, NSString *one, NSString *two, NSString *three) {
        NSLog(@"call you %@--%@--%@ ", one,two,three);
    }
    +(BOOL)resolveInstanceMethod:(SEL)sel{
        if (sel == @selector(callNameMany)) {
            class_addMethod([self class], sel, (IMP)callYouThenMany, "v@:@@@");
        }
        return [super resolveInstanceMethod:sel];
    }
    
    [callMe performSelector:@selector(callNameMany) withObject:@"1" withObject:@"2" withObject:@"3" withObject:nil withObject:nil];
    

    4. 분류에 속성 추가


    원리: 하나의 클래스에 속성을 설명하는데 사실 본질은 이 클래스에 관련을 추가하는 것이다. 이 값의 메모리 공간을 클래스 메모리 공간에 직접 추가하는 것이 아니다.
    분류에서 변수를 정의할 수 없고, Getter와setter 방법을 실현할 수 없으며,runtime 클래스로만 실현할 수 있습니다
    #import 
    @interface NSObject (Objc)
    @property(nonatomic,copy)NSString *name;
    @end
    
    #import "NSObject+Objc.h"
    #import 
    //  key
    static const char *key = "name";
    @implementation NSObject (Objc)
    
    - (NSString *)name {
        //  key, 。
        return objc_getAssociatedObject(self, key);
    }
    - (void)setName:(NSString *)name {
        //  :  
        //  :  key, key 
        //  :  value
        //  :  
        /*
         OBJC_ASSOCIATION_ASSIGN;            //assign 
         OBJC_ASSOCIATION_COPY_NONATOMIC;    //copy 
         OBJC_ASSOCIATION_RETAIN_NONATOMIC;  //retain 
         OBJC_ASSOCIATION_RETAIN;
         OBJC_ASSOCIATION_COPY;
         */
        objc_setAssociatedObject(self, key, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
    }
    @end
    

    외부 사용
    NSObject *objc = [[NSObject alloc] init];
    objc.name = @" ";
    NSLog(@"name--%@",objc.name);
    
    2017-08-17 16:36:26.967 SocketGG[19114:2092692] name--

    맺다


    공부는 고통스럽고 즐거운 일이다. 배우지 않으면 재능을 넓힐 수 없고, 뜻이 없으면 학문을 이룰 수 없고, 순서에 따라 점진적으로 나아가면 천천히 하면 반드시 적게 축적하여 많이 될 수 있다.어려움을 알면 물러서고, 반은 알고 반은 알면서도, 그대와 함께 격려하지 마라.

    좋은 웹페이지 즐겨찾기