ObjC의 RunTime(아래)

8002 단어 Runtime
이전에 공식 문서를 학습함으로써 런타임에 대한 초보적인 인식을 가지게 되었다. 다음은 런타임이 도대체 어디에 사용되고 우리의 프로그램을 어떻게 개선할 수 있는지 연구해야 한다.
본문도 icocoa에서 조회할 수 있다.
Swizzling
스위즈링은 method swizzling과 class(isa) swizzling 두 종류로 나눌 수 있다.말 그대로 방법/클래스를 런타임에 교체하는 것입니다.
Method Swizzling
실행할 때 어떤 방법을 바꾸거나 수정합니다. 자기가 쓴 방법일 수도 있고 체계적인 방법일 수도 있습니다. 물론 일반적으로 프레임 클래스를 바꾸는 방법입니다.
//ZJView.m -Swizzling
+ (void)swizzleSetFrame
{
    SEL originalSel = @selector(setFrame:);
    Class myClass = [self class];
    Method originMethod = class_getInstanceMethod(myClass, originalSel);
    const char *originType = method_getTypeEncoding(originMethod);
    originalIMP = (void *)method_getImplementation(originMethod);
    class_replaceMethod(myClass, originalSel, (IMP)mySetFrame, originType);

}
static void mySetFrame(id self, SEL _cmd,CGRect frame)
{
    NSLog(@"run mySetFrame");
    if (originalIMP)
    {
        frame.origin.y += 20;
        originalIMP(self, _cmd, frame);
    }
}

예를 들어 사용자 정의View 클래스에서 setFrame 방법을 바꿨습니다. (이렇게 하는 것은 실제 인코딩에서 의미가 없습니다. 이것은 완전히 계승을 통해 할 수 있기 때문에 코드의 측면에서만 method swizzling을 이해할 수 있습니다.)교체 방법은 +load에 넣거나 자체적으로 호출할 수 있습니다.여기서 주의해야 할 것은 Stackoverflow에는 이렇게 교체되는 것이 많다는 것이다.
if(class_addMethod() )
{
    class_replaceMethod();
}
else
{
    method_exchangeImplementations();
}

이런 절차로 처리하는 것은 클라스에서 교체할 selector가 있는지 확인하기 위해서입니다.그러나 사실runtime는 이미 이런 상황을 고려했기 때문에classreplaceMethod를 사용하면 됩니다.일반적으로 우리는 계승을 통해 어떤 방법을 다시 불러올 수 있지만, 계승 관계가 없는 종류의 방법은 ObjC를 다시 불러오는 데 Category를 제공한다.예를 들어 iOS5에 앞서 NavigationBar의 배경을 사용자 정의하려면 category를 만들어서drawRect를 다시 불러옵니다.하지만 이렇게 Category를 사용하면 다음과 같은 단점이 있습니다.
  • 방법의 원래 실현은 완전히 재부팅되어 원래의 실현을 호출할 수 없다.특히 라이브러리 종류의 방법을 다시 불러올 때, 우리는 간단한 전체적인 교체가 아니라 원래의 실현을 원한다.
  • category가 여러 개 있을 때 어느 쪽이 이길지 장담할 수 없다.

  • 그래서 category는 프레임 클래스에 추가하는 방법에 사용됩니다.이런 상황에서 method swizzling은 좋은 선택이다.현재 iOS의 버전도 날로 많아지고 있기 때문에 어떤 종류의 방법도 서로 다른 iOS 아래에서 서로 다른 표현을 하는 것을 만날 수 있다.그러면 우리는 실제 상황에 따라 서로 다른 iOS 버전을 모집하여 기본적인 실현이나 사용자 정의의 실현, 또는 이들의 결합을 계속 선택할 수 있다.또한 충돌을 피하기 위해method swizzling은 +load 함수에서 호출하는 것이 좋습니다.실천에서 method swizzling을 사용하는 장소가 비교적 적고 개인적인 체험이 깊지 않기 때문에 일시적으로 개인적인 이해는 이 단계에 있을 수밖에 없다.나는 Stakoverflow에서 한 편의 게시물를 찾았고method swizzling을 사용할 때 주의해야 할 부분에 대해 상세한 설명을 했다.
    Class(isa) Swizzling
    runtime object를 통해setClass는 동적으로 대상의class를 대체합니다.주의해야 할 것은 새 클라스의 길이는 원래 클라스의 길이와 일치해야 한다는 것이다.이 밖에 KVO의 실현 이사 swizzling을 이용했고 iOS6PTL에서도 이에 대해 설명했다.예를 들어 대상 a는 b의 어떤 속성을 관찰해야 한다.observer를 추가할 때 시스템은 중간 클래스를 생성하고 b의isa 바늘을 이 새 클래스를 가리킨다.이것 또한 runtime에서 KVO를 처리하기 때문에 KVO를 사용할 때 반드시 해당하는 명칭 규범을 따라야 한다는 것을 설명한다.
    연결 포인터
    연관 포인터는 runtime에서 객체에 변수를 추가하여 기존 클래스에 영향을 주지 않는 것을 의미합니다. 이것은 ObjC 확장(Extension)보다 우수한 부분이며 주로 다음과 같은 방법을 사용합니다.
    objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);
    objc_getAssociatedObject(id object, const void *key);
    void objc_removeAssociatedObjects(id object);

    프레임 클래스에 대해 원본 코드가 없기 때문에 이런 방식으로 변수를 추가할 수 있습니다.예를 들어 UIView, UIAlertView는 tag를 추가하여 나중에 다시 얻을 수 있으며, 우리는 관련 지침을 통해 특정한 대상과 연결할 수 있다.
    내부
    Reflection(반사)라고도 할 수 있을 것 같은데 확실하지 않아요.Introspection는 OO언어가 모두 갖추어야 하는 특성으로 런타임에 대상이 요청을 통해 자신의 유형의 관건적인 정보를 조회할 수 있는 능력을 가리킨다.먼저 ObjC 언어 자체에는 다음과 같은 인터페이스가 있습니다.
    -isKindOfClass:; -isMemberOfClass:
    -respondsToSelector: ;-conformsToProtocol:
    -isEqual:

    이러한 기능은 다음과 같습니다.
  • 소속 클래스와 클래스의 계승 관계를 확인한다.
  • 어떤 방법이나 협의가 이루어졌는지 확인
  • 대상의 상등 여부를 판단
  • 이러한 기능은 NSObject 클래스와 프로토콜에 정의되어 있으며 일반적으로 iOS의 클래스는 모두 사용할 수 있다.다음은 두 개의 개원 라이브러리를 소개할 것이다. MantleOvercoat 그들은 내성의 중요한 응용이다.실제적으로, 우리는 보통 프로그램에서 Json과 Object 간의 전환에 사용할 모델 층을 설계한다.비교적 완비된 모델 클래스는 다음을 고려합니다.
  • NSString 속성
  • NSString을 통해 생성된 NSURL 등의 속성
  • NSNumber, NSDate 등의 속성 포맷
  • NSCoding 구현, NSCopying 프로토콜 등
  • ....

  • 일반적인 상황에서 프로그램 안의 모델들이 하나밖에 없을 것이다. 모두 이렇게 실현된다면 상당 부분의 코드는'冗余'이지만 계승 같은 방법을 통해 회피할 수 없다.Mantle은 Model 디자인에 집중할 수 있는 아주 좋은 선택입니다. 실현 부분은 다음과 같은 필수적인 방법만 필요합니다.
    + (NSDictionary *)JSONKeyPathsByPropertyKey
    {
        //  Json key Model      ,  key           
    }
    + (NSValueTransformer *)JSONTransformerForKey:(NSString *)key
    {
        //             key       
    }

    Introspection이 Mantle에서 적용되는 것은runtime일 때classcopyPropertyList는 클래스의 속성 목록을 가져와서 작업을 간소화합니다.오버코트는 AFNetworking와 맨틀의 결합이다.AFNetworking은 ASINetwork에 이어 iOS와 OSX에서 유명한 네트워크 라이브러리로 유지보수 업데이트가 활발하다.오버코트의 주요 업무는 AFnetworking을 통해 얻은 결과를 대상으로 바꾸는 것이다. 전환하는 과정은 맨틀을 사용하고 이 부분의 업무를 백엔드에 놓고 진행하는 것이다.Overcoat는 하나의 예ReadingList를 제공했으니 잘 연구해 보십시오.ReadingList는 코코푸드를 사용해야 하는데, 모르는 친구, out라~
    동적 속성(메서드)
    전제에서 언급한 바와 같이 우리는runtime에 방법을 추가할 수 있다. 더욱 나아가 우리는 성명을 실현하지 않고 동적으로 속성을 추가할 수 있다. 아래의 코드는 gist에서 나온다.
    #import <objc/runtime.h>
    #import <Foundation/Foundation.h>
    
    @interface Person : NSObject
    @property (nonatomic,strong) NSMutableDictionary *properties;
    @end
    
    @implementation Person
    
        -(id) init {
            self = [super init];
            if (self){
                _properties = [NSMutableDictionary new];
            }
            return self;
        }
    
        // generic getter
        static id propertyIMP(id self, SEL _cmd) {
            return [[self properties] valueForKey:NSStringFromSelector(_cmd)];
        }
    
        // generic setter
        static void setPropertyIMP(id self, SEL _cmd, id aValue) {
    
            id value = [aValue copy];
            NSMutableString *key = [NSStringFromSelector(_cmd) mutableCopy];
    
            // delete "set" and ":" and lowercase first letter
            [key deleteCharactersInRange:NSMakeRange(0, 3)];
            [key deleteCharactersInRange:NSMakeRange([key length] - 1, 1)];
            NSString *firstChar = [key substringToIndex:1];
            [key replaceCharactersInRange:NSMakeRange(0, 1) withString:[firstChar lowercaseString]];
    
            [[self properties] setValue:value forKey:key];
        }
    
        + (BOOL)resolveInstanceMethod:(SEL)aSEL {
            if ([NSStringFromSelector(aSEL) hasPrefix:@"set"]) {
                class_addMethod([self class], aSEL, (IMP)setPropertyIMP, "v@:@");
            } else {
                class_addMethod([self class], aSEL,(IMP)propertyIMP, "@@:");
            }
            return YES;
        }
    
    @end
    
    int main(int argc, char *argv[]) {
    	@autoreleasepool {
    		Person *p = [Person new];
    		[p setName:@"Jon"];
    		NSLog(@"%@",[p name]);
    	}
    }

    이상은 현 단계에서runtime에 대한 총결이고 더 많은 내용은 더 많은 탐색이 필요하며 함께 공부하고 토론하는 것을 환영합니다.

    좋은 웹페이지 즐겨찾기