iOS에서 blcok의 본질 설명

25063 단어 iOS

더 좋은 독서 체험을 원하신다면, 저의 개발 노트를 방문하시기 바랍니다.


blcok의 본질은 하나의 대상이다

main.m의 코드
int main(int argc, const char * argv[]) {

    int age = 18;
    static int height = 55;
    void (^block)(int) = ^(int pa) {
        NSLog(@"--block-  auto     --%d",age);
        NSLog(@"--block-  static     --%d",height);
    };

    NSLog(@"---%@",[block class]);
    NSLog(@"---%@",[[block class] superclass]);
    NSLog(@"---%@",[[[block class] superclass] superclass]);
    NSLog(@"---%@",[[[[block class] superclass] superclass] superclass]);

    block(10);
}

보이는 Block을 인쇄하는 것도 oc 대상입니다.
2019-06-08 11:38:23.353777+0800 Tes[41864:3485021] —NSStackBlock 2019-06-08 11:38:23.354053+0800 Tes[41864:3485021] —__NSStackBlock 2019-06-08 11:38:23.354091+0800 Tes [41864:3485021] - NSBlock 2019-06-08 11:3811:285275+0800 Tes [41864:3485021] - Block-국부 auto 변수의 포획 - 18864:08 11:38:38:2834344+0800 Tes [41864:3485021] - 국부 auto 변수의 포획 - 국부 변수
상기 코드를 xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m를 통해 c++ 코드로 전환하면 block의 구조를 볼 수 있습니다
//block    
struct __main_block_impl_0 {

    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
    int age;
    int *height;

    //    
    __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age, int *_height, int flags=0) : age(_age), height(_height) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
    }
};

struct __block_impl {
    void *isa;
    int Flags;
    int Reserved;
    void *FuncPtr;
};

static struct __main_block_desc_0 {
    size_t reserved;
    size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};


blcok의 실현은 첫 번째 매개 변수로 전송되고 포획된 변수는 뒤에 매개 변수로 되어 __main_block_impl_0 구조체의 구조 함수를 호출하여 Block의 생성을 실현한다.
int main(int argc, const char * argv[]) {

    int age = 18;
    static int height = 55;
    void (*block)(int) = ((void (*)(int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age, &height));

    //              
    void (*block)(int) = &__main_block_impl_0(
    __main_block_func_0,
    &__main_block_desc_0_DATA,
    age,
    &height);
}

block의 구체적인 구현 봉인된 함수
static void __main_block_func_0(struct __main_block_impl_0 *__cself, int pa) {
    int age = __cself->age; // bound by copy
    int *height = __cself->height; // bound by copy

    NSLog((NSString *)&__NSConstantStringImpl__var_folders_pg_vnxk1kl519z1ks1hddc4mr0h0000gn_T_main_53cf21_mi_0,age);
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_pg_vnxk1kl519z1ks1hddc4mr0h0000gn_T_main_53cf21_mi_1,(*height));
}

Block의 호출: block(10);block타입이기 때문에 __main_block_impl_0타입__block_impl타입imp을 추출한 다음imp추출FuncPtr을 통해 호출합니다.그런데 여기는 왜 직접 blcok__block_impl 유형으로 강제 전환했습니까?impl__main_block_impl_0 구조체의 첫 번째 요소로서 __main_block_impl_0 변수의 주소가 바로 그 첫 번째 요소의 주소이기 때문에 여기서 강제 변환이 실현될 수 있다.
((void (*)(__block_impl *, int))((__block_impl *)block)->FuncPtr)((__block_impl *)block, 10);
//              
(block->FuncPtr)(block, 10);

//       
(block->imp->FuncPtr)(block, 10);


blcok의 변수 포획

  • 국부 변수의 포획은 조사__main_block_impl_0 구조체를 통해 여러 개int age int *height
  • 를 보았다.
    struct __main_block_impl_0 {
        struct __block_impl impl;
        struct __main_block_desc_0* Desc;
        int age;
        int *height;
    }
    
    //     
    static void __main_block_func_0(struct __main_block_impl_0 *__cself, int pa) {
    
        //     ,   block      
        int age = __cself->age; // bound by copy
        int *height = __cself->height; // bound by copy
    }
    
    
    auto로 수식된 국부 변수(국부 변수, 기본값은 auto): 국부 변수이기 때문에 자동으로 소각되고, Block 호출 전에 소각되면 Block 호출 시 얻은 값에 문제가 발생하기 때문에 값 전달을 실행합니다.static로 수식된 국부 변수: 이것도 국부 변수이지만 static로 수식되었기 때문에 변수 작용역이 생겨서 자동으로 소각되지 않습니다. 그러면 Block이 호출될 때 정확한 값을 얻을 수 있기 때문에 바늘 전달에 문제가 되지 않습니다.
  • 글로벌 변수는 캡처되지 않음
  • int age_ = 18;
    static int height_ = 55;
    
    int main(int argc, const char * argv[]) {
    
        void (^block)(int) = ^(int pa) {
            NSLog(@"--block-  auto     --%d",age_);
            NSLog(@"--block-  static     --%d",height_);
        };
    
        NSLog(@"---%@",[block class]);
        NSLog(@"---%@",[[block class] superclass]);
        NSLog(@"---%@",[[[block class] superclass] superclass]);
        NSLog(@"---%@",[[[[block class] superclass] superclass] superclass]);
    
        block(10);
    }
    

    c++로 전환blcok된 구조체는 age_height_가 없는 것을 발견했다. 그들은 전역 변수이기 때문에 언제든지 어디든지 접근할 수 있기 때문에 포획할 필요가 없다.
    struct __main_block_impl_0 {
        struct __block_impl impl;
        struct __main_block_desc_0* Desc;
    }
    //     
    static void __main_block_func_0(struct __main_block_impl_0 *__cself, int pa) {
    
        //     ,        
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_pg_vnxk1kl519z1ks1hddc4mr0h0000gn_T_main_ccc8d0_mi_2,age_);
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_pg_vnxk1kl519z1ks1hddc4mr0h0000gn_T_main_ccc8d0_mi_3,height_);
    }
    

    글로벌 변수는 캡처되지 않습니다.

    blcok 유형

    int main(int argc, const char * argv[]) {
    
        int age = 18;
    
        //__NSStackBlock__
        NSLog(@"----%@",[^(int pa) {
            NSLog(@"--block---%d",age);
        } class]);
    
        //__NSGlobalBlock__
        void (^block)(int) = ^(int pa) {
            NSLog(@"---");
        };
        NSLog(@"----%@",[block class]);
    
        //__NSMallocBlock__
        void (^block1)(int) = ^(int pa) {
            NSLog(@"--block---%d",age);
        };
        NSLog(@"----%@",[block1 class]);
    
    }
    
    

    인쇄:
    2019-06-08 11:51:08.351813+0800 Tes[42036:3500881] ----NSStackBlock 2019-06-08 11:51:08.352169+0800 Tes[42036:3500881] ----NSGlobalBlock 2019-06-08 11:51:08.352201+0800 Tes[42036:3500881] ----NSMallocBlock
    메모리에서 지상 주소에서 높은 주소로:
  • NSGlobalBlock: auto 형식의 국부 변수(GlobalBlock)에 접근하지 않고 데이터 세그먼트에 저장
  • NSMallocBlock: (MallocBlock입니다), 더미에 저장
  • NSStackBlock: auto 유형의 부분 변수에 액세스하여 스택에 보관
  • __NSStackBlock__형식의blcok는oc대상에 강한 인용이 없습니다

    typedef void(^RTBlock)(int);
    int main(int argc, const char * argv[]) {
    
        RTBlock block;
        {
            RTPerson *person = [[RTPerson alloc] init];
    
            NSLog(@"--block---%@",[^(int pa) {
                NSLog(@"--block---%@",person);
            } class]);
        }
    
        NSLog(@"--block---%@",[block class]);
    
    }
    

    인쇄:
    2019-06-10 13:04:08.687481+0800 Tes[42877:3569172] --block—NSStackBlock 2019-06-10 13:04:08.687816+0800 Tes[42877:3569172] dealloc 2019-06-10 13:04:08.687839+0800 Tes[42877:3569172] --block—(null) block의 유형은 stack이기 때문에 person를 강제로 인용하지 않습니다.

    만NSMallocBlock__형식의blcok는oc대상에 강한 인용이 있습니다

    int main(int argc, const char * argv[]) {
    
    RTPerson *person = [[RTPerson alloc] init];
    
        //__NSMallocBlock__
        void (^block1)(int) = ^(int pa) {
            NSLog(@"--block---%@",person);
        };
        NSLog(@"----%@",[block1 class]);
    
    }
    
    

    c++ 코드로 전환한 후 __main_block_desc_0 구조체에 두 개의 함수 바늘이 더 있는 것을 발견하였다
    static struct __main_block_desc_0 {
        size_t reserved;
        size_t Block_size;
        void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
        void (*dispose)(struct __main_block_impl_0*);
    } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
    

    __main_block_copy_0, 이 함수는 이 Block에서 포획한oc 대상의 유형이 약한 인용인지 강한 인용인지 여부에 따라 강한 인용을 할 수 있습니다
    static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
        _Block_object_assign((void*)&dst->person, (void*)src->person, 3/*BLOCK_FIELD_IS_OBJECT*/);
    }
    

    __main_block_dispose_0, 이 함수는 강력한 인용을 방출하는 데 쓰인다
    static void __main_block_dispose_0(struct __main_block_impl_0*src) {
        _Block_object_dispose((void*)src->person, 3/*BLOCK_FIELD_IS_OBJECT*/);
    }
    

    Block 내부 변수 수정

  • 국부static 수식의 변수와 전역 변수는 Block 내부에서 수정할 수 있으며 위의blcok구조에 따라 볼 수 있다
  • 국부static 변수용 *age = 10 수정
  • 전역 변수 직접 수정
  • __Block 수식 변수는 Block 내부에서 수정할 수 있습니다
  • typedef void(^RTBlock)(int);
    int main(int argc, const char * argv[]) {
    
        __block int age = 18;
        RTBlock block = ^(int pa) {
            NSLog(@"--block---%d",age);
            age = pa;
            NSLog(@"--block---%d",age);
        };
    
        block(1);
    }
    

    c++ 코드로 전환한 후 이전의 구조와 차이가 있음을 발견했다. 이전의 변수 유형은 변수의 자체 유형이고age는__Block_byref_age_0 유형이 되었다.
    struct __main_block_impl_0 {
        struct __block_impl impl;
        struct __main_block_desc_0* Desc;
        __Block_byref_age_0 *age; // by ref
        __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_age_0 *_age, int flags=0) : age(_age->__forwarding) {
            impl.isa = &_NSConcreteStackBlock;
            impl.Flags = flags;
            impl.FuncPtr = fp;
            Desc = desc;
        }
    };
    
    __Block_byref_age_0 구조체의 코드는
    struct __Block_byref_age_0 {
        void *__isa;
        __Block_byref_age_0 *__forwarding;
        int __flags;
        int __size;
        int age;
    };
    
    __block int age = 18;에서 c++ 코드로 변경:
    __attribute__((__blocks__(byref))) __Block_byref_age_0 age = {(void*)0,(__Block_byref_age_0 *)&age, 0, sizeof(__Block_byref_age_0), 18};
    
    //    
    __Block_byref_age_0 age = {
    0,
    &age, //__forwarding       
    0, 
    sizeof(__Block_byref_age_0), 
    18  //  
    }; 
    

    Block을 만들 때 위에서 생성된 __Block_byref_age_0 age의 주소를 자신의 내부에 부여age
    RTBlock block = (
    (void (*)(int))&__main_block_impl_0((void *)__main_block_func_0, 
    &__main_block_desc_0_DATA,
    (__Block_byref_age_0 *)&age,
    570425344));
    
    __main_block_impl_0 함수 내부에서age의 값을 수정한 다음에 수정age의 값을 실현하였다.
    static void __main_block_func_0(struct __main_block_impl_0 *__cself, int pa) {
    __Block_byref_age_0 *age = __cself->age; // bound by ref
    
    ...
    
    (age->__forwarding->age) = pa;
    
    ...
    }
    

    그런데 왜 __Block_byref_age_0에서age의 값을 수정하고 바깥의 값도 바뀌었을까요? 그들의 주소는 같을까요?다음은 프린트 주소를 통해 증명합니다
    struct __main_block_desc_0 {
        size_t reserved;
        size_t Block_size;
    //    void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
    //    void (*dispose)(struct __main_block_impl_0*);
    //                 ,       void,     
        void (*copy)(void);
        void (*dispose)(void);
    };
    
    struct __Block_byref_age_0 {
        void *__isa;
        struct __Block_byref_age_0 *__forwarding;
        int __flags;
        int __size;
        int age;
    };
    
    struct __block_impl {
        void *isa;
        int Flags;
        int Reserved;
        void *FuncPtr;
    };
    
    struct __main_block_impl_0 {
        struct __block_impl impl;
        struct __main_block_desc_0* Desc;
        struct __Block_byref_age_0 *age; // by ref
    };
    
    typedef void(^RTBlock)(int);
    int main(int argc, const char * argv[]) {
    
        __block int age = 18;
        RTBlock block = ^(int pa) {
            NSLog(@"--block---%d",age);
            age = pa;
            NSLog(@"--block---%d",age);
        };
    
        struct __main_block_impl_0 *s_block = (__bridge struct __main_block_impl_0 *) block;
    
        NSLog(@"--   age  ---%p",&age);
    
        NSLog(@"--      age  ---%p",&s_block->age->age);
    }
    

    인쇄 결과:
    2019-06-08 14:30:48.880799+0800 Tes [43886:3665093] - 외부age 주소 - 0x10180cd28 2019-06-08 14:30:48.881041+0800 Tes [43886:3665093] - 구조체 내부age 주소 - 0x10180cd28
  • 주의점1은 변수__block 를 사용했기 때문에__main_block_desc_0 구조체에도copydispose지침
  • 이 나타났다
    static struct __main_block_desc_0 {
        size_t reserved;
        size_t Block_size;
        void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
        void (*dispose)(struct __main_block_impl_0*);
    } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
    

    그래서block__Block_byref_age_0 *age 이 구조체 지침을 쌓아 올리다
    static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->age, (void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);}
    
    static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);}
    
    
  • 주의 사항 2block OC 객체를 손질할 때Block_byref_person_0 이 구조체는 두 개의 함수 포인터__Block_byref_id_object_copy__Block_byref_id_object_dispose가 더 나와 OC 대상에 대한 메모리 관리
  • struct __Block_byref_person_0 {
        void *__isa;
        __Block_byref_person_0 *__forwarding;
        int __flags;
        int __size;
        void (*__Block_byref_id_object_copy)(void*, void*);
        void (*__Block_byref_id_object_dispose)(void*);
        RTPerson *person;
    };
    

    __Block_byref_id_object_copy_131, 이 함수는 이 Block에서 포획한oc 대상의 유형에 따라 약인용인지 강인용인지 여부에 따라 강인용을 합니다
    static void __Block_byref_id_object_copy_131(void *dst, void *src) {
        _Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131);
    
        /*__isa: 8  ,
        __forwarding: 8  ,
        int __flags: 4  ,
        int __size: 4  ,
        __Block_byref_id_object_copy: 8  
        __Block_byref_id_object_dispose: 8  
          40  
    
          dst + 40   person   
        */
    }
    

    __Block_byref_id_object_dispose_131, 이 함수는 강력한 인용을 방출하는 데 쓰인다
    static void __Block_byref_id_object_dispose_131(void *src) {
        _Block_object_dispose(*(void * *) ((char*)src + 40), 131);
    }
    

    예:
    사용하지 않음weak
    RTBlock block;
    {
        __block RTPerson *person = [[RTPerson alloc] init];
    
        block = ^(int pa) {
            NSLog(@"--block---%@",person);
        };
    }
    NSLog(@"--block  ---%@",[block class]);
    

    인쇄:
    2019-06-08 14:12:13.198105+0800 Tes [45184:3784877] - Block 유형 - NSMallocBlock 2019-06-08 14:12:17.398071+0800 Tes [45184:378487] RTPerson dealloc
    사용weak손질
    RTBlock block;
    {
        RTPerson *person = [[RTPerson alloc] init];
    
        __block __weak RTPerson *weakP = person;
        block = ^(int pa) {
            NSLog(@"--block---%@",weakP);
        };
    }
    NSLog(@"--block  ---%@",[block class]);
    

    인쇄:
    2019-06-08 14:13:22.198105+0800 Tes [45184:378487] RTPerson dealloc 2019-06-08 14:13:26.398071+0800 Tes [45184:3784877] - Block 유형 - NSMallocBlock
    참고:
    MRC 시__Block_byref_id_object_copy_131는 OC 대상을 강제로 인용하지 않습니다!!!
    RTBlock block;
    {
        __block RTPerson *person = [[RTPerson alloc] init];
    
        block = [^(int pa) {
            NSLog(@"--block---%@",person);
        } copy] ;
    
        [person release];
    }
    
    NSLog(@"--block  ---%@",[block class]);
    

    인쇄:
    2019-06-08 14:17:43.198105+0800 Tes [45184:378487] RTPerson dealloc 2019-06-08 14:17:47.398071+0800 Tes[45184:3784877] - Block 유형 - NSMallocBlock

    더 좋은 독서 체험을 원하신다면, 저의 개발 노트를 방문하시기 바랍니다.

    좋은 웹페이지 즐겨찾기