美文网首页Objective-C
iOS:Block(二)

iOS:Block(二)

作者: 码小菜 | 来源:发表于2020-01-29 23:29 被阅读0次
风景

推荐阅读:Block原理分析(一)

目录
一,修改变量
二,基本数据类型的__block变量
三,__forwarding指针
四,对象类型的__block变量
五,循环引用

一,修改变量

1,在block内部可以修改static变量和全局变量,但不能修改auto变量

  • 实例代码
int age3 = 3;
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        auto int age1 = 1;
        static int age2 = 2;
        void(^block)(void) = ^{
            //age1 = 4;
            age2 = 5;
            age3 = 6;
            NSLog(@"%d---%d---%d", age1, age2, age3);
        };
        block();
    }
    return 0;
}

// 打印
1---5---6
  • 底层代码(用clang进行转换)
int age3 = 3;

int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        auto int age1 = 1;
        static int age2 = 2;
        // 传递age1的值,传递age2的地址
        void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &age2, age1));
        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
    }
    return 0;
}

struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
    int age1; // 保存age1的值
    int *age2; // 保存age2的地址
    __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_age2, int _age1, int flags=0) : age2(_age2), age1(_age1) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
    }
};

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
     // 获取age1的值
    int age1 = __cself->age1; // bound by copy
    // 获取age2的地址
    int *age2 = __cself->age2; // bound by copy
    // 修改age2
    (*age2) = 5;
    // 修改age3
    age3 = 6;
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_0d_7m56tzw16qs_vsxqtxhnsq2m1myhmv_T_main_c767a8_mi_0, age1, (*age2), age3);
}
  • 分析

auto变量:age1是定义在main函数里面的,block只捕获了age1的值,在__main_block_func_0函数中可以通过block获取age1的值,但无法拿到age1,所以不能修改它

static变量:age2虽然也定义在main函数里面,但block捕获了age2的地址,在__main_block_func_0函数中可以通过block获取age2的地址,所以可以修改它

全局变量:age3是定义在所有函数之外的,在任何函数中都可以修改它

2,在block内部可以修改__block修饰的auto变量

  • 实例代码
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        __block int age = 1;
        void(^block)(void) = ^{
            age = 2;
            NSLog(@"%d", age);
        };
        block();
    }
    return 0;
}

// 打印
2
  • 底层代码
struct __Block_byref_age_0 {
    void *__isa; // OC对象的标识
    __Block_byref_age_0 *__forwarding; // 指向自身
    int __flags;
    int __size;
    int age; // 存储变量值
};

int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
        // 将age包装成对象
        __attribute__((__blocks__(byref))) __Block_byref_age_0 age = {
            (void*)0,
            (__Block_byref_age_0 *)&age,
            0,
            sizeof(__Block_byref_age_0),
            1
        };
        // 传递对象的地址
        void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_age_0 *)&age, 570425344));
        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
    }
    return 0;
}

struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
    __Block_byref_age_0 *age; // 保存对象的地址
    __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;
    }
};

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
    // 获取对象的地址
    __Block_byref_age_0 *age = __cself->age; // bound by ref
    // 修改对象中存储的变量值
    (age->__forwarding->age) = 2;
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_0d_7m56tzw16qs_vsxqtxhnsq2m1myhmv_T_main_5332f7_mi_0, (age->__forwarding->age));
}
  • 分析

1>系统会将__block修饰的auto变量包装成对象,并将变量值存储在该对象中
2>block会捕获该对象的地址
3>在__main_block_func_0函数中先通过block获取该对象的地址,然后再修改该对象中存储的变量值

  • 注意点

1>__block不能修饰static变量和全局变量

2>不需要__block修饰的情况

// 只是使用,并没有修改

NSMutableArray *array = [NSMutableArray new];
void(^block)(void) = ^{
    [array addObject:@"111"];
};

Person *person = [Person new];
void(^block)(void) = ^{
    person.name = @"111";
};
二,基本数据类型的__block变量

1,底层代码

static struct __main_block_desc_0 {
    size_t reserved;
    size_t Block_size;
    // 指向__main_block_copy_0
    void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
    // 指向__main_block_dispose_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};

static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
    // 强引用__block变量
    _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变量
    _Block_object_dispose((void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);
}

2,分析

系统会将__block修饰的auto变量包装成对象,所以在__main_block_desc_0函数中会多两个指针,在底层代码中会多两个函数,用来管理该对象的内存

  • block复制到堆上时

1>同时将__block变量也复制到堆上
2>通过copy指针调用__main_block_copy_0函数,此函数会对__block变量产生强引用

Block0复制到堆上 Block1复制到堆上
  • block从堆上移除时

1>通过dispose指针调用__main_block_dispose_0函数,此函数会释放引用的__block变量
2>因为没有持有者,__block变量也会从堆上移除

Block从堆上移除 Block0和Block1从堆上移除

⚠️注意⚠️:当block在栈上时,对__block变量不会产生强引用

3,__block变量与对象类型auto变量的比较

  • 相同点

1>当block在栈上时,对它们都不会产生强引用
2>当block复制到堆上时,都会通过copy指针来引用它们
3>当block从堆上移除时,都会通过dispose指针来释放它们

  • 不同点

1>__block变量传值是8(BLOCK_FIELD_IS_BYREF);auto变量传值是3(BLOCK_FIELD_IS_OBJECT)
2>block__block变量产生的一定是强引用;blockauto变量产生的可以是强引用,也可以是弱引用

  • 实例代码
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        __block int age = 1;
        NSObject *object = [NSObject new];
        __weak typeof(object) weakObject = object;
        void(^block)(void) = ^{
            NSLog(@"%d", age);
            NSLog(@"%@", object);
            NSLog(@"%@", weakObject);
        };
        block();
    }
    return 0;
}
  • 底层代码
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*/);
    // 强引用
    _Block_object_assign((void*)&dst->object, (void*)src->object, 3/*BLOCK_FIELD_IS_OBJECT*/);
    // 弱引用
    _Block_object_assign((void*)&dst->weakObject, (void*)src->weakObject, 3/*BLOCK_FIELD_IS_OBJECT*/);
}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {
    _Block_object_dispose((void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);
    _Block_object_dispose((void*)src->object, 3/*BLOCK_FIELD_IS_OBJECT*/);
    _Block_object_dispose((void*)src->weakObject, 3/*BLOCK_FIELD_IS_OBJECT*/);
}
三,__forwarding指针

1,指向

  • block在栈上时,__forwarding指针指向自身
  • block复制到堆上时,栈上的__forwarding指针指向堆上的结构体,堆上的__forwarding指针指向自身
__forwarding指针

2,原因

  • 底层代码
struct __Block_byref_age_0 {
    void *__isa;
    __Block_byref_age_0 *__forwarding;
    int __flags;
    int __size;
    int age;
};

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
    __Block_byref_age_0 *age = __cself->age; // bound by ref
    // 修改变量值
    (age->__forwarding->age) = 2;
}
  • 分析

无论访问的是栈上的还是堆上的结构体,通过__forwarding指针来修改变量值,最终都能将变量值存储到堆上的结构体中

四,对象类型的__block变量

1,访问强指针

  • 实例代码
typedef void(^Block)(void);

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Block block;
        {
            __block Person *person = [Person new];
            block = ^{
                NSLog(@"%@", person);
            };
        }
        block();
    }
    return 0;
}

// 打印
<Person: 0x1006361e0>
Person dealloc
  • 底层代码
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*);
    Person *__strong person; // 指向外部person
};

int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
        Block block;
        {
            // 创建结构体person
            __attribute__((__blocks__(byref))) __Block_byref_person_0 person = {
                (void*)0,
                (__Block_byref_person_0 *)&person,
                33554432,
                sizeof(__Block_byref_person_0),
                __Block_byref_id_object_copy_131,
                __Block_byref_id_object_dispose_131,
                ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"),
                sel_registerName("new"))
            };
            block = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_person_0 *)&person, 570425344));
        }
        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
    }
    return 0;
}

struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
    __Block_byref_person_0 *person; // 指向结构体person
    __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_person_0 *_person, int flags=0) : person(_person->__forwarding) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
    }
};

2,访问弱指针

  • 实例代码
typedef void(^Block)(void);

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Block block;
        {
            Person *person = [Person new];
            __block __weak Person *weakPerson = person;
            block = ^{
                NSLog(@"%@", weakPerson);
            };
        }
        block();
    }
    return 0;
}

// 打印
Person dealloc
(null)
  • 底层代码
struct __Block_byref_weakPerson_0 {
    void *__isa;
    __Block_byref_weakPerson_0 *__forwarding;
    int __flags;
    int __size;
    void (*__Block_byref_id_object_copy)(void*, void*);
    void (*__Block_byref_id_object_dispose)(void*);
    Person *__weak weakPerson; // 指向外部weakPerson
};

int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
        Block block;
        {
            Person *person = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("new"));
            // 创建结构体weakPerson
            __attribute__((__blocks__(byref))) __attribute__((objc_ownership(weak))) __Block_byref_weakPerson_0 weakPerson = {
                (void*)0,
                (__Block_byref_weakPerson_0 *)&weakPerson,
                33554432,
                sizeof(__Block_byref_weakPerson_0),
                __Block_byref_id_object_copy_131,
                __Block_byref_id_object_dispose_131,
                person
            };
            block = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_weakPerson_0 *)&weakPerson, 570425344));
        }
        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
    }
    return 0;
}

struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
    __Block_byref_weakPerson_0 *weakPerson; // 指向结构体weakPerson
    __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_weakPerson_0 *_weakPerson, int flags=0) : weakPerson(_weakPerson->__forwarding) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
    }
};

3,分析

  • 图解
访问强指针 访问弱指针
  • 结论

1>block对结构体一定是强引用
2>结构体对外部变量可以是强引用,也可以是弱引用

4,copydispose

  • 底层代码
static struct __main_block_desc_0 {
    size_t reserved;
    size_t Block_size;
    // 指向__main_block_copy_0
    void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
    // 指向__main_block_dispose_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};

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, 8/*BLOCK_FIELD_IS_BYREF*/);
}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {
    // 释放结构体
    _Block_object_dispose((void*)src->person, 8/*BLOCK_FIELD_IS_BYREF*/);
}

// ---------------------------

struct __Block_byref_person_0 {
    void *__isa;
    __Block_byref_person_0 *__forwarding;
    int __flags;
    int __size;
    // 指向__Block_byref_id_object_copy_131
    void (*__Block_byref_id_object_copy)(void*, void*);
    // 指向__Block_byref_id_object_dispose_131
    void (*__Block_byref_id_object_dispose)(void*);
    Person *__strong person;
};

static void __Block_byref_id_object_copy_131(void *dst, void *src) {
    // 强/弱引用外部变量
    _Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131);
}

static void __Block_byref_id_object_dispose_131(void *src) {
    // 释放外部变量
    _Block_object_dispose(*(void * *) ((char*)src + 40), 131);
}
  • 分析

block复制到堆上时

1>同时将结构体也复制到堆上(此时结构体会通过__Block_byref_id_object_copy指针调用__Block_byref_id_object_copy_131函数,此函数会对外部变量产生强/弱引用)
2>通过copy指针调用__main_block_copy_0函数,此函数会对结构体产生强引用

block从堆上移除时

1>通过dispose指针调用__main_block_dispose_0函数,此函数会释放引用的结构体
2>因为没有持有者,结构体也会从堆上移除(此时结构体会通过__Block_byref_id_object_dispose指针调用__Block_byref_id_object_dispose_131函数,此函数会对释放引用的外部变量)

5,MRC环境下结构体不会对外部变量产生强引用

// MRC环境
typedef void(^Block)(void);

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        __block Person *person = [Person new];
        Block block = [^{
            NSLog(@"%@", person);
        } copy];
        [person release];
        NSLog(@"block未释放:%@", [block class]);
    }
    return 0;
}

// 打印
Person dealloc
block未释放:__NSMallocBlock__
五,循环引用

1,循环引用

  • 实例代码
// Person
@interface Person : NSObject
// person持有block
@property (nonatomic, copy) void(^block)(void);
- (void)test;
@end

@implementation Person
- (void)test {
    // 循环引用
    self.block = ^{
        NSLog(@"%@", self);
    };
}
- (void)dealloc {
    //[super dealloc]; // MRC环境打开
    NSLog(@"Person dealloc");
}
@end

// main
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *person = [Person new];
        [person test];
        //[person release]; // MRC环境打开
    }
    NSLog(@"person作用域结束");
    return 0;
}

// 打印
person作用域结束
  • 底层代码
struct __Person__test_block_impl_0 {
    struct __block_impl impl;
    struct __Person__test_block_desc_0* Desc;
    Person *const __strong self; // block持有person
    __Person__test_block_impl_0(void *fp, struct __Person__test_block_desc_0 *desc, Person *const __strong _self, int flags=0) : self(_self) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
    }
};
  • 图解
循环引用

2,ARC环境下的解决方法

  • __unsafe_unretained__weak

1>实例代码

- (void)test {
    //__unsafe_unretained typeof(self) weakSelf = self;
    __weak typeof(self) weakSelf = self;
    self.block = ^{
        NSLog(@"%@", weakSelf);
    };
}

// 打印
Person dealloc
person作用域结束

2>底层代码

struct __Person__test_block_impl_0 {
    struct __block_impl impl;
    struct __Person__test_block_desc_0* Desc;
    //Person *const __unsafe_unretained weakSelf;
    Person *const __weak weakSelf; // block弱引用person
    __Person__test_block_impl_0(void *fp, struct __Person__test_block_desc_0 *desc, Person *const __unsafe_unretained _weakSelf, int flags=0) : weakSelf(_weakSelf) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
    }
};

3>图解

弱引用

4>注意点

注意一:对象释放后,__unsafe_unretained指针不会自动置nil,易引起野指针crash,而__weak指针会自动置nil,不会出现野指针crash,所以一般都使用__weak

注意二:在某些情况下,__weak需要结合__strong一起使用,以保证在block整个执行过程中对象都不会释放

// 不用__strong

- (void)viewDidLoad {
    [super viewDidLoad];

    Person *person = [Person new];
    __weak typeof(person) weakPerson = person;
    person.block = ^{
        NSLog(@"1---%@", weakPerson);
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"2---%@", weakPerson);
        });
    };
    person.block();
}

// 打印
1---<Person: 0x6000035c5140>
Person dealloc
2---(null)
// 用__strong

- (void)viewDidLoad {
    [super viewDidLoad];

    Person *person = [Person new];
    __weak typeof(person) weakPerson = person;
    person.block = ^{
        __strong typeof(weakPerson) strongPerson = weakPerson;
        NSLog(@"1---%@", strongPerson);
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"2---%@", strongPerson);
        });
    };
    person.block();
}

// 打印
1---<Person: 0x600001ad7990>
2---<Person: 0x600001ad7990>
Person dealloc
  • __block

1>实例代码

- (void)test {
    __block Person *weakSelf = self;
    self.block = ^{
        NSLog(@"%@", weakSelf);
        weakSelf = nil; // 必须执行
    };
    self.block(); // 必须执行
}

// 打印
<Person: 0x1020338c0>
Person dealloc
person作用域结束

2>底层代码

struct __Person__test_block_impl_0 {
    struct __block_impl impl;
    struct __Person__test_block_desc_0* Desc;
    __Block_byref_weakSelf_0 *weakSelf; // block持有__block变量
    __Person__test_block_impl_0(void *fp, struct __Person__test_block_desc_0 *desc, __Block_byref_weakSelf_0 *_weakSelf, int flags=0) : weakSelf(_weakSelf->__forwarding) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
    }
};

struct __Block_byref_weakSelf_0 {
    void *__isa;
    __Block_byref_weakSelf_0 *__forwarding;
    int __flags;
    int __size;
    void (*__Block_byref_id_object_copy)(void*, void*);
    void (*__Block_byref_id_object_dispose)(void*);
    Person *__strong weakSelf; // __block变量持有person
};

static void __Person__test_block_func_0(struct __Person__test_block_impl_0 *__cself) {
    __Block_byref_weakSelf_0 *weakSelf = __cself->weakSelf; // bound by ref
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_0d_7m56tzw16qs_vsxqtxhnsq2m1myhmv_T_Person_3a900e_mi_0, (weakSelf->__forwarding->weakSelf));
    (weakSelf->__forwarding->weakSelf) = __null; // 释放__block变量对person的持有
}

3>图解

置nil前 置nil后

3,MRC环境下的解决方法

  • __unsafe_unretained(MRC不支持__weak
// MRC环境
- (void)test {
    __unsafe_unretained typeof(self) weakSelf = self;
    self.block = ^{
        NSLog(@"%@", weakSelf);
    };
}

// 打印
Person dealloc
person作用域结束
  • __block(MRC环境下__block变量不会强引用对象)

1>实例代码

// MRC环境
- (void)test {
    __block typeof(self) weakSelf = self;
    self.block = ^{
        NSLog(@"%@", weakSelf);
    };
}

// 打印
Person dealloc
person作用域结束

2>图解


__block

4,不会出现循环引用的情况

// block引用了self,但self没有引用block

[UIView animateWithDuration:0.3 animations:^{
    NSLog(@"%@", self);
}];

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    NSLog(@"%@", self);
});

[self.twoView mas_makeConstraints:^(MASConstraintMaker *make) {
    make.width.equalTo(self.oneView);
}];

相关文章

  • block系列文章总结

    iOS源码解析:Block的本质<一>iOS源码解析:Block的本质<二>Objective C block背后...

  • OC--Block总结

    参考 Block编译代码解读:block没那么难(一、二、三)iOS进阶——iOS(Objective-C) 内存...

  • Block - block简单的使用

    参考文档 iOS Block详解 一、忘记block格式? 样例一.png 样例二.png 二、Block的定义 ...

  • iOS-2 Block

    block块 系列文章: iOS Block浅浅析 - 简书 iOS Block实现原理 iOS Block __...

  • Block

    xx_cc iOS底层原理总结 - 探寻block的本质(一)iOS底层原理总结 - 探寻block的本质(二) ...

  • iOS Block存储域及循环引用

    系列文章:iOS Block概念、语法及基本使用iOS Block实现原理iOS Block __block说明符...

  • iOS - block原理解读(三)

    前言 在阅读该篇文章前,推荐阅读ios - block原理解读(一)ios - block原理解读(二) 本文解决...

  • iOS Block实现原理

    系列文章:iOS Block概念、语法及基本使用iOS Block __block说明符iOS Block存储域及...

  • iOS Block __block说明符

    系列文章:iOS Block概念、语法及基本使用iOS Block实现原理iOS Block存储域及循环引用 上一...

  • iOS复习之Block

    iOS面试中如何优雅回答Block iOS block循环引用

网友评论

    本文标题:iOS:Block(二)

    本文链接:https://www.haomeiwen.com/subject/hsjszctx.html