iOS 에서 block 변수 캡 처 원리 에 대한 상세 한 분석

블록 개요
Block 은 C 언어 등급 과 실행 시의 특징 입 니 다.Block 은 코드 논 리 를 패키지 하고{}로 묶 었 습 니 다.표준 C 언어의 함수/함수 포인터 와 비슷 합 니 다.그 밖 에 block 은 정의 환경 에서 변 수 를 참조 할 수 있 습 니 다.이 점 은 다른 각종 언어 에서 말 하 는'폐쇄'와 매우 유사 한 개념 이다.iOS 에서 block 은 코드 패 키 징 을 매개 변수 로 전달 하 는 등 응용 장면 이 많 습 니 다.이 는 dispatch 병행(Operation 에 도 BlockOperation)과 completion 비동기 반전 등 을 사용 하 는 데 널리 활용 된다.
  • Block 은 애플 이 공식 적 으로 추천 한 데이터 유형 으로 사용 장면 이 비교적 광범 위 하 다
  • 애니메이션
    다 중 스 레 드
    집합
    네트워크 리 셋 요청
    Block 의 역할
    4.567917.특정한 코드 를 저장 하 는 데 사용 되 며 적당 한 시기 에 다시 호출 할 수 있 습 니 다기능 은 함수 와 방법 과 유사 하 다.
    변수 캡 처
    1:캡 처 가능 변수 변경 불가
    부분 변수
    2:캡 처 가능 하 며 변 수 를 수정 할 수 있 습 니 다.
    전역 변수
    정적 변수
  • __block 수식 부분 변수
    원리 분석:
    1.부분 변 수 는 왜 캡 처 되 었 는 지 수정 할 수 없습니다.
    
    int a = 10;
    void (^blcok)() = [^{
     NSLog(@"%d",a);
    } copy];
    a=20;
    blcok(); // log : a = 10
    결 과 는 모두 가 알 고 있 을 텐 데 왜 그 럴 까?
    클 램 핑 으로 한번 바 꿔 보도 록 하 겠 습 니 다.

    block 정의 로 볼 때
    
    void (*blcok)() = (void (*)())((id (*)(id, SEL))(void *)objc_msgSend)((id)((void (*)())&__ZMX__blockTest_block_impl_0((void *)__ZMX__blockTest_block_func_0, &__ZMX__blockTest_block_desc_0_DATA, a)), sel_registerName("copy")); 
    block 의 실현 은ZMX__blockTest_block_impl_0.구조 체 의 구조 방법 으로 정 의 된 것 입 니 다.이 구조 체 를 살 펴 보 겠 습 니 다.
    
    struct __ZMX__blockTest_block_impl_0 {
     struct __block_impl impl;
     struct __ZMX__blockTest_block_desc_0* Desc;
     int a;
     __ZMX__blockTest_block_impl_0(void *fp, struct __ZMX__blockTest_block_desc_0 *desc, int _a, int flags=0) : a(_a) {
     impl.isa = &_NSConcreteStackBlock;
     impl.Flags = flags;
     impl.FuncPtr = fp;
     Desc = desc;
     }
    };
    impt:
    
    struct __block_impl {
     void *isa;
     int Flags;
     int Reserved;
     void *FuncPtr;
    };
    isa:Class 를 가리 키 는 지침
    flags:일부 표지
    reserced:보 존 된 변수 들
    함수 포인터
    __ZMX__blockTest_block_desc_0:
    
    static struct __ZMX__blockTest_block_desc_0 {
     size_t reserved;
     size_t Block_size;
    } __ZMX__blockTest_block_desc_0_DATA = { 0, sizeof(struct __ZMX__blockTest_block_impl_0)};
    reserced:보류 변수
    크기:메모리 크기
    __ZMX__blockTest_block_impl_0 구조 방법
    우 리 는 이 구조 방법 에 네 개의 매개 변수 가 있다 는 것 을 볼 수 있다.
    
    void *fp:    
    struct __ZMX__blockTest_block_desc_0 *desc: desc   
    int _a:   
    int flags=0:       
    우 리 는 Block 의 정 의 를 간소화 합 니 다.
    
    void (*blcok)() = ((void (*)())&__ZMX__blockTest_block_impl_0((void *)__ZMX__blockTest_block_func_0, &__ZMX__blockTest_block_desc_0_DATA, a));
    우리 가 정의 할 때 이미 a 를 매개 변수 로 전달 한 것 을 볼 수 있다.즉,정 의 를 내 릴 때 우리 의 block 은 a 의 값 을 얻 었 고 뒤에서 a 의 값 을 어떻게 수정 하 든 간 에.우리 가 block 내부 에서 가 져 온 a 는 모두 정의 할 때 들 어 오 는 값 입 니 다.이것 은 block 이 국부 변 수 를 포착 할 수 있 지만 수정 할 수 없 는 이유 입 니 다.
    2.1 전역 변 수 는 캡 처 할 수도 있 고 수정 할 수도 있 습 니 다.
    
    (void)blockTest
    {
     void (^blcok)() = [^{
     NSLog(@"%d",a);
     } copy]; 
     a = 20;
     blcok(); // log : 20 
    } 
    클 램 핑 으로 한번 바 꿔 보도 록 하 겠 습 니 다.

    같은 부분 을 반복 하지 않 겠 습 니 다.이 럴 때 blcok 의 구조 함 수 를 정의 하 는 것 은 이전 인자 a 가 들 어 오지 않 았 음 을 알 수 있 습 니 다.
    NSLog 함 수 를 호출 합 니 다=위ZMX__blockTest_block_func_0 함수
    
    static void __ZMX__blockTest_block_func_0(struct __ZMX__blockTest_block_impl_0 *__cself) {
     NSLog((NSString *)&__NSConstantStringImpl__var_folders_47_6nlw9jbn3fb7c8lb1km1rzmm0000gn_T_ZMX_70ee3a_mi_0,a);
     }
    우리 가 block 을 호출 할 때,만약 당신 이 이전에 a 의 값 을 수정 했다 면,인쇄 한 것 은 반드시 새 값 입 니 다.
    2.2   정적 변 수 는 캡 처 할 수도 있 고 수정 할 수도 있 습 니 다.
    
     (void)blockTest
    {
     static int a = 10;
     void (^blcok)() = [^{
     NSLog(@"%d",a);
     } copy]; 
     a = 20; 
     blcok(); //log : 20 
    }
    클 램 핑 으로 한번 바 꿔 보도 록 하 겠 습 니 다.

    구조 함 수 를 통 해 우 리 는 볼 수 있 습 니 다.이때 입 삼 에 int* 가 하나 더 들 어 갔 습 니 다.a,a 의 주 소 를 전 달 했 습 니 다.인쇄 함수ZMX__blockTest_block_func_0 도 마찬가지 로 같은 메모리 주소 의 값 을 가 져 오 는 작업 입 니 다.so,우 리 는 a 를 방문 할 수 있 을 뿐만 아니 라 a 도 수정 할 수 있 습 니 다.
    2.3   __block 수식 변 수 는 캡 처 할 수도 있 고 수정 할 수도 있 습 니 다.
    
    (void)blockTest
    {
     __block int a = 10;
     void (^blcok)() = [^{
     NSLog(@"%d",a);
     } copy]; 
     a = 20; 
     blcok();// log : 20 
    }
    클 램 핑 으로 한번 바 꿔 보도 록 하 겠 습 니 다.

    아이고!이때 의 구조 체ZMX__blockTest_block_impl_0 의 a 는 구조 체 지침 이 되 었 다.이상 하 다.이 구조 체 를 살 펴 보 자.
    
    struct __Block_byref_a_0 {
     void *__isa;
    __Block_byref_a_0 *__forwarding;
     int __flags;
     int __size;
     int a;
    };
    
    isa:   Class  
    forwarding:    a     
    flags:  
    size:  
    a:   
    저희 블록 테스트 함수 다시 한 번 볼 게 요.
    
    static void _I_ZMX_blockTest(ZMX * self, SEL _cmd) {
     __attribute__((__blocks__(byref))) __Block_byref_a_0 a = {(void*)0,(__Block_byref_a_0 *)&a, 0, sizeof(__Block_byref_a_0), 10};
     void (*blcok)() = (void (*)())((id (*)(id, SEL))(void *)objc_msgSend)((id)((void (*)())&__ZMX__blockTest_block_impl_0((void *)__ZMX__blockTest_block_func_0, &__ZMX__blockTest_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344)), sel_registerName("copy"));
     (a.__forwarding->a) = 20;
     ((void (*)(__block_impl *))((__block_impl *)blcok)->FuncPtr)((__block_impl *)blcok);
    }
    이때 변수 a 는 로 변 했다.Block_byref_a_0 구조 체,우리 가 초기 화 할 때 a 에 게 준 주소 와 a 의 값 이 모두 들 어 가 는 것 을 볼 수 있 습 니 다.
    
    a = 20 -> (a.__forwarding->a) = 20
    다시 값 을 부여 합 니 다.저 희 는 a 가 가리 키 는 메모리 주소 의 value 를 수정 하여 a 의 값 을 수정 합 니 다.
    인쇄 함수
    
    static void __ZMX__blockTest_block_func_0(struct __ZMX__blockTest_block_impl_0 *__cself) {
     __Block_byref_a_0 *a = __cself->a; // bound by ref
      NSLog((NSString *)&__NSConstantStringImpl__var_folders_47_6nlw9jbn3fb7c8lb1km1rzmm0000gn_T_ZMX_c9e1ad_mi_0,(a->__forwarding->a));
     }
    블록 이 캡 처 한 a 의 메모리 주소 에 대응 하 는 value 를 먼저 가 져 온 다음 인쇄 합 니 다.
    그래서 우 리 는 a 의 값 을 캡 처 하고 수정 할 수 있 습 니 다.
    총결산
    이상 은 이 글 의 전체 내용 입 니 다.본 논문 의 내용 이 여러분 의 학습 이나 업무 에 어느 정도 참고 학습 가치 가 있 기 를 바 랍 니 다.궁금 한 점 이 있 으 시 면 댓 글 을 남 겨 주 셔 서 저희 에 대한 지지 에 감 사 드 립 니 다.
  • 좋은 웹페이지 즐겨찾기