Objective - C 소스 코드 (5) Associated Objects 의 실현 원리

원본 링크:http://blog.leichunfeng.com/blog/2015/06/26/objective-c-associated-objects-implementation-principle/  
    Objective - C 에 서 는 Category 를 통 해 기 존의 클래스 에 속성 을 추가 할 수 있 지만 인 스 턴 스 변 수 를 추가 할 수 없다 는 것 을 알 고 있 습 니 다. 이것 은 Objective - C 의 뚜렷 한 단판 이 된 것 같 습 니 다.그러나 다행히도 우 리 는 Associated Objects 를 통 해 이 부족 함 을 보완 할 수 있다.
   
    본 고 를 읽 는 과정 에서 독 자 는 다음 과 같은 세 가지 문제 에 중심 을 두 어야 한다.
  • 관련 대상 은 어디 에 저장 되 어 있 습 니까? 관련 대상 자체 의 메모리 에 저장 되 어 있 습 니까?
  • 관련 대상 의 다섯 가지 관련 전략 은 어떤 차이 가 있 고 어떤 구덩이 가 있 습 니까?
  • 관련 대상 의 생명 주 기 는 어떻게 되 고 언제 풀 려 나 며 언제 제거 되 나 요?

  •     
        Associated Objects 와 관련 된 함 수 는 주로 세 가지 가 있 습 니 다. runtime 소스 코드 의 runtime. h 파일 에서 설명 을 찾 을 수 있 습 니 다.
    OBJC_EXPORT void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
    OBJC_EXPORT id objc_getAssociatedObject(id object, const void *key)
    OBJC_EXPORT void objc_removeAssociatedObjects(id object)

        이 세 함수 의 이름 은 쉽게 알 아 볼 수 있다.
  • objc_setAssociated Object 는 대상 에 관련 대상 을 추가 하 는 데 사용 되 며, nil 에 들 어가 면 기 존의 관련 대상 을 제거 할 수 있 습 니 다.
  • objc_getAssociatedObject 는 관련 대상 을 가 져 오 는 데 사 용 됩 니 다.
  • objc_removeAssociated Objects 는 대상 의 모든 관련 대상 을 제거 하 는 데 사 용 됩 니 다.

  •     주: obj cremoveAssociated Objects 함 수 는 일반적으로 사용 할 수 없습니다. 이 함 수 는 한 대상 의 모든 관련 대상 을 제거 하고 이 대상 을 '원시' 상태 로 복원 하기 때 문 입 니 다.이렇게 하면 다른 사람 이 추가 한 관련 대상 도 함께 제거 할 가능성 이 높다. 이것 은 우리 가 원 하 는 것 이 아니다.그래서 일반적인 방법 은 obbcsetAssociated Object 함수 가 nil 에 들 어 와 기 존 관련 대상 을 제거 합 니 다.
        
        Key 값:
        앞의 두 함수 중의 key 값 은 우리 가 중점적으로 주목 해 야 할 점 입 니 다. 이 key 값 은 하나의 대상 등급 (왜 대상 등급 입 니까? 아래 장 을 보면 알 수 있 습 니 다) 의 유일한 상수 입 니 다.일반적으로 다음 과 같은 세 가지 추천 키 값 이 있 습 니 다.
  • static char kAssociatedObjectKey 성명;,키 값 으로 & kAssociated ObjectKey 사용 하기;
  • static void * kAssociatedObjectKey = & kAssociatedObjectKey;,kAssociated ObjectKey 를 key 값 으로 사용 하기;
  • selector 를 사용 하여 getter 방법의 이름 을 key 값 으로 합 니 다.

  •      세 번 째 종 류 를 추천 합 니 다. 사용 하기 쉽 고 코드 량 을 절약 합 니 다.
        
        관련 정책
        관련 정책                                                        등가 속성                                    설명 OBJCASSOCIATION_ASSIGN                       @property (assign) or                 약 인용 관련 대상
                                                                      @property (unsafe_unretained)
    OBJC_ASSOCIATION_RETAIN_NONATOMIC    @property (strong, nonatomic)    관련 대상 을 강하 게 인용 하고 비원 자 조작
    OBJC_ASSOCIATION_COPY_NONATOMIC       @property (copy, nonatomic)      관련 대상 을 복사 하고 비원 자 조작
    OBJC_ASSOCIATION_RETAIN                        @property (strong, atomic)         관련 대상 을 강하 게 인용 하고 원자 조작
    OBJC_ASSOCIATION_COPY                           @property (copy, atomic)           관련 대상 복사 및 원자 조작
        원자 문 제 는 여기 서 토론 하지 않 고 다음 에 주로 앞의 세 가지 형식 을 토론 한다.
        
        실현 원리
        코드 Github 원본 링크:https://github.com/leichunfeng/AssociatedObjects 
        테스트 코드 에서 알 수 있 듯 이:
  • 관련 대상 의 석방 시기 와 제거 되 는 시기 가 항상 일치 하지 않 습 니 다. 예 를 들 어 위의 self. associated Objectassign 이 가리 키 는 대상 은 ViewController 가 나타 나 자마자 풀 려 났 지만 self. associated Objectassign 은 여전히 값 이 있 습 니까? 저 장 된 대상 의 주소 입 니까?나중에 self. associated Object 를 사용 하면assign 은 Crash 를 만 들 수 있 으 므 로 약 한 인용 대상 을 사용 할 때 매우 조심해 야 합 니 다.
  • 한 대상 의 모든 관련 대상 은 이 대상 이 풀 려 날 때 호출 되 는object_remove_assocations 함수 에서 제거 되 었 습 니 다.

  •     obj c - references. mm 를 열 고 obj c 를 찾 습 니 다.setAssociated Object 함수 가 최종 호출 된 함수;
        
    void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
        // retain the new value (if any) outside the lock.
        ObjcAssociation old_association(0, nil);
        id new_value = value ? acquireValue(value, policy) : nil;
        {
            AssociationsManager manager;
            AssociationsHashMap &associations(manager.associations());
            disguised_ptr_t disguised_object = DISGUISE(object);
            if (new_value) {
                // break any existing association.
                AssociationsHashMap::iterator i = associations.find(disguised_object);
                if (i != associations.end()) {
                    // secondary table exists
                    ObjectAssociationMap *refs = i->second;
                    ObjectAssociationMap::iterator j = refs->find(key);
                    if (j != refs->end()) {
                        old_association = j->second;
                        j->second = ObjcAssociation(policy, new_value);
                    } else {
                        (*refs)[key] = ObjcAssociation(policy, new_value);
                    }
                } else {
                    // create the new association (first time).
                    ObjectAssociationMap *refs = new ObjectAssociationMap;
                    associations[disguised_object] = refs;
                    (*refs)[key] = ObjcAssociation(policy, new_value);
                    object->setHasAssociatedObjects();
                }
            } else {
                // setting the association to nil breaks the association.
                AssociationsHashMap::iterator i = associations.find(disguised_object);
                if (i !=  associations.end()) {
                    ObjectAssociationMap *refs = i->second;
                    ObjectAssociationMap::iterator j = refs->find(key);
                    if (j != refs->end()) {
                        old_association = j->second;
                        refs->erase(j);
                    }
                }
            }
        }
        // release the old value (outside of the lock).
        if (old_association.hasValue()) ReleaseValue()(old_association);
    }

        
        obj c - references. mm 의 obj cgetAssociated Object 함수 가 최종 호출 된 함수:
    id _object_get_associative_reference(id object, void *key) {
        id value = nil;
        uintptr_t policy = OBJC_ASSOCIATION_ASSIGN;
        {
            AssociationsManager manager;
            AssociationsHashMap &associations(manager.associations());
            disguised_ptr_t disguised_object = DISGUISE(object);
            AssociationsHashMap::iterator i = associations.find(disguised_object);
            if (i != associations.end()) {
                ObjectAssociationMap *refs = i->second;
                ObjectAssociationMap::iterator j = refs->find(key);
                if (j != refs->end()) {
                    ObjcAssociation &entry = j->second;
                    value = entry.value();
                    policy = entry.policy();
                    if (policy & OBJC_ASSOCIATION_GETTER_RETAIN) ((id(*)(id, SEL))objc_msgSend)(value, SEL_retain);
                }
            }
        }
        if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {
            ((id(*)(id, SEL))objc_msgSend)(value, SEL_autorelease);
        }
        return value;
    }

       
        objc_removeAssociatedObjects
     이 함 수 는 한 대상 의 모든 관련 대상 을 제거 하고 구체 적 으로 실현 하 는 것 도 대상 의 주소 에 따라 해당 하 는 Object Association Map 대상 을 가 져 온 다음 모든 관련 구 조 를 하나의 vector 에 저장 한 다음 vector 에 저 장 된 모든 관련 대상 을 방출 합 니 다.앞의 실험 에서 관찰 한 상황 에 따 르 면 한 대상 이 풀 려 날 때 도 바로 이 함수 로 모든 관련 대상 을 제거 합 니 다.
        클래스 대상 에 관련 대상 추가
        소스 코드 를 본 후에 우 리 는 대상 주소 와 Associations HashMap 해시 표 가 일일이 대응 한 다 는 것 을 알 게 되 었 다.그렇다면 우 리 는 이런 문 제 를 생각 할 것 이다. 유형 대상 에 게 관련 대상 을 추가 할 수 있 을 까?답 은 긍정 적 이다.우 리 는 클래스 대상 에 게 똑 같은 방식 으로 관련 대상 을 추가 할 수 있 습 니 다. 그러나 우 리 는 일반적인 상황 에서 이렇게 하지 않 습 니 다. 왜냐하면 우 리 는 static 변 수 를 통 해 클래스 등급 의 변 수 를 실현 할 수 있 기 때 문 입 니 다.나 는 분류 ViewController + Associated Objects 에서 ViewController 클래스 대상 에 관련 대상 associated Object 를 추가 했다. 。
        총결산
  • 관련 대상 은 관련 대상 자체 의 저장 과 직접적인 관 계 를 가지 지 않 고 단독 해시 표 에 저 장 된 것 이다.
  • 관련 대상 의 다섯 가지 관련 전략 은 속성의 한정 부적 과 매우 유사 하 다. 절대 다수의 경우 우 리 는 OBJC 를 사용한다.ASSOCIATION_RETAIN_NONATOMIC 의 관련 전략 은 우리 가 관련 대상 을 가지 고 있 음 을 보증 할 수 있다.
  • 관련 대상 의 석방 시기 와 제거 시기 가 항상 일치 하지 않 습 니 다. 예 를 들 어 실험 에서 관련 전략 으로 OBJCASSOCIATION_ASSIGN 이 연관 성 을 가 진 대상 은 이미 오래전 에 풀 려 났 지만 제거 되 지 않 았 고, 이 연관 성 대상 을 다시 사용 할 경우 Crash 가 발생 할 수 있다.
  • 좋은 웹페이지 즐겨찾기