【iOS】 Block再学习

iOS Block再学习

文章目录

前言

笔者之前学习过block的相关内容,但是掌握不牢,今天再重新学习一遍

Block的三种类型

__ NSGlobalBlock__

objc 复制代码
void(^block)(void) = ^ {
        NSLog(@"testBlock");
    };
    NSLog(@"%@", block);

此时block没有参数也没有返回值,属于全局block

如果访问全局变量:

从上面可以看出无论是否创建block变量,只要访问全局变量的话,他就会创建一个全局区的block

__ NSMallocBlock__

objc 复制代码
int loaclA = 10;
    void(^block)(void) = ^ {
        NSLog(@"testBlock %ld", loaclA);
    };
    NSLog(@"%@", block);

此时block捕获了一个临时变量,就是底层拷贝a,所以是堆区block

__ NSStackBlock__

objc 复制代码
NSLog(@"%@", [^{
        NSLog(@"%d", loaclA);
    } class]);

这里在自由变量没有处理之前是栈区block处理之后是堆区block,目前的栈区block就越来越少了

这里是不创建Block变量,且访问自由变量的时候才会出现这里的一个栈区Block

小结
  • block如果不访问自由变量的话,都是存储在全局区的,如果访问全局变量的话,也是存储在全局区的Block
  • block如果访问自由变量的话
    • 如果没有创建block变量,才会创建一个栈区Block变量
    • 创建了一个Block变量,且访问自由变量,才会创建出一个堆区的Block,这里创建出堆区Block的原因是 栈区的Block执行了拷贝操作

Block底层分析

Block的结构

先看这段代码:

objc 复制代码
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        extern NSString* nanString;
        int localA = 3;
        void(^block)(void) = ^{
            NSLog(@"123%ld", localA);
        };
    }
    return 0;
}

这里是一段司空见惯的Block捕获自由变量的代码.这里我们来分析一下这里的源码来认识一下底层内容:

objc 复制代码
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int localA; // 捕获的自由变量
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _localA, int flags=0) : localA(_localA) {
    impl.isa = &_NSConcreteStackBlock; // 设置isa指针
    //&_NSConcreteStackBlock:标识 Block 初始类型为 栈 Block(未进行 copy 操作时)
    impl.Flags = flags;
    impl.FuncPtr = fp; // 代码块的函数赋值
    Desc = desc;
  } // block的一个构造函数,创建Block调用的内容
};

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  int localA = __cself->localA; // bound by copy 这里是一个值拷贝

            NSLog((NSString *)&__NSConstantStringImpl__var_folders_rb_0ts7dvhs3zg9fc6kk2nvtlr00000gn_T_main_3d8947_mi_0, localA);
        }

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)};
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        extern NSString* nanString;
        int localA = 3;
        void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, localA)); //相当于block等于__main_block_impl_0,是一个函数
    }
    return 0;
}

这里我们先看block的一个结构体,这里可以说明block是一个 __main_block_impl_0类型的对象.从构造函数可以看出他又一个isa指针

总结

  • block其实是一个对象,结构体,函数,又因为block没有名称,所以也被叫做,匿名函数

如果从更加底层的角度来看:

捕获自由变量

可以看到Block的实现结构体里面新增了localA变量,它在上面这是一个单纯的一个值拷贝

这里我看一下这段核心代码:

objc 复制代码
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  int localA = __cself->localA; // bound by copy 这里是一个值拷贝

            NSLog((NSString *)&__NSConstantStringImpl__var_folders_rb_0ts7dvhs3zg9fc6kk2nvtlr00000gn_T_main_3d8947_mi_0, localA);
        }

这里我们可以看到这里如果对于他的值其实是只读的,它并不会修改原来block中持有的变量的值,所以是有问题的

block捕获外界变量时,在内部会自动生成同一个属性来保存

捕获全局(静态)变量

这里我们看一下如果是一个全局变量会是什么形式?

objc 复制代码
int localA = 10;

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {

            NSLog((NSString *)&__NSConstantStringImpl__var_folders_rb_0ts7dvhs3zg9fc6kk2nvtlr00000gn_T_main_4d4ace_mi_0, localA++);
        }

这里我们可以看到它对于全局变量就是直接引用了全局变量的内容

捕获静态变量

这里我们看一下如果是一个静态局部变量:

objc 复制代码
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        extern NSString* nanString;
        static int loaclB = 10;
        void(^block)(void) = ^{
            loaclB++;
            NSLog(@"123%ld", localA++);
        };
    }
    return 0;
}

如果是一个静态局部变量的话他是通过指针来获取的,同时静态局部变量与Block建立关联的是指针(int *),也就是说Block捕获的静态局部变量捕获的是变量的指针,因此当我们对静态局部变量进行修改时,Block内部的静态局部变量的值也会随之改变.

objc 复制代码
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int *loaclB;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_loaclB, int flags=0) : loaclB(_loaclB) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

__block修饰符

如果给localA添加一个修饰符__block,然后在block中对localA进行一个加加操作

objc 复制代码
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        extern NSString* nanString;
        __block int localA = 3;
        void(^block)(void) = ^{
            NSLog(@"123%ld", localA++);
        };
    }
    return 0;
}

这时候我们在把它重写成我们的结构体:

objc 复制代码
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_localA_0 *localA; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_localA_0 *_localA, int flags=0) : localA(_localA->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->localA, (void*)src->localA, 8/*BLOCK_FIELD_IS_BYREF*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->localA, 8/*BLOCK_FIELD_IS_BYREF*/);}

//重新看一下函数部分
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_localA_0 *localA = __cself->localA; // bound by ref

            NSLog((NSString *)&__NSConstantStringImpl__var_folders_rb_0ts7dvhs3zg9fc6kk2nvtlr00000gn_T_main_fd8242_mi_0, (localA->__forwarding->localA)++);
        }
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        extern NSString* nanString;
        //创建一个__Block_byref_a_0 是一个结构体,相当于把外界对象村对象
        __attribute__((__blocks__(byref))) __Block_byref_localA_0 localA = {(void*)0,(__Block_byref_localA_0 *)&localA, 0, sizeof(__Block_byref_localA_0), 3};
        void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_localA_0 *)&localA, 570425344));
    }
    return 0;
}

这里我们发现原先的localA变成了__Block_byref_localA_0的部分

objc 复制代码
struct __Block_byref_localA_0 { //__block修饰的外界变量的结构体
  void *__isa;
__Block_byref_localA_0 *__forwarding;
 int __flags;
 int __size;
 int localA;
};

总结:

  • 外界变量如果被__block修饰会变成这个结构体
  • 结构体用来保存变量的指针和值
  • 将变量生成的结构体对象中的指针地址 传递给block, 然后在block内部就可以对外界变量进行操作了

两种拷贝对比如下

  • 值拷贝 - 深拷贝,只是拷贝数值,且拷贝的值不可更改,指向不同的内存空间,案例中普通变量loacaA就是值拷贝
  • 指针拷贝 - 浅拷贝,生成的对象指向同一片内存空间,案例中经过__block修饰的变量localA就是指针拷贝
forwarding指针

这里我们首先看下面这段代码:

objc 复制代码
__block int val = 0;
    void (^block)(void) = ^{
        val++;
    };
    ++val;
    block();
    NSLog(@"%ld", val);

这里我们看一下这里的一个val在堆区上面,一个在栈区上面,如果我们不做处理的话对于他的数据计算会有问题:

所以这就是forwarding这个指针的使命所在--确保可以正确的访问__block变量

我们把它转译成c++源码来看一下:

objc 复制代码
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        extern NSString* nanString;
        __attribute__((__blocks__(byref))) __Block_byref_loaclB_0 loaclB = {(void*)0,(__Block_byref_loaclB_0 *)&loaclB, 0, sizeof(__Block_byref_loaclB_0), 10};
        void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_loaclB_0 *)&loaclB, 570425344));
        (loaclB.__forwarding->loaclB)++;
    }
    return 0;
}
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_loaclB_0 *loaclB = __cself->loaclB; // bound by ref

            (loaclB->__forwarding->loaclB)++;
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_rb_0ts7dvhs3zg9fc6kk2nvtlr00000gn_T_main_589979_mi_0, localA++);
        }

从上面我们可以看出对于这种自由变量的加减操作,都是通过我们的(loaclB->__forwarding->loaclB)++这一句指令来执行的,无论是堆区的block还是栈区的block都是这样操作的,这时候我们看一下Block copy的相关源码:

这部分源码内容会在下面的Block的拷贝部分讲一下,这里先不提起,先让笔者了解一下有关于__Block变量拷贝的图

当block变量从stack copy到heap上面的时候,stack上的forwarding被修改程指向heap上的block变量,通过这个机制,保证我们无论是在stack和heap上面都可以访问到同一个block变量

Block的copy时机

在 Objective-C 中,Block 最初是在栈上创建的。栈上的 Block(NSStackBlock)生命周期与其定义的作用域相关联,一旦该作用域结束,栈上的 Block 将不再有效。这意味着如果你需要在 Block 的定义作用域外使用它,比如将它作为回调传递或保存为后续使用,你需要将它复制到堆上(成为 NSMallocBlock)。

就好比我们在子线程回调到主线程的时候,我们的Block如果在栈上的话,那么超出作用域就会被销毁,无法回到主线程被调用,因此需要拷贝到堆上

在ARC中进行了很多优化的内容,Block的copy操作会在下面这些情况下执行:

block作为函数返回值

objc 复制代码
- (nxBlock)test {
    int a = 0;
    return ^{
        
        NSLog(@"1233123 %d", a);
    };
}
nxBlock testBlock = [self test];
NSLog(@"%@", [testBlock class]);

将block赋给__strong指针

objc 复制代码
int loaclA = 10;
    void(^block)(void) = ^ {
        NSLog(@"testBlock %ld", loaclA);
    };
    NSLog(@"%@", block);

block作为函数中方法名含有usingBlock的方法参数时

objc 复制代码
NSArray *array = @[@1,@4,@5];
[array enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            
}];

block作为GCD API的方法参数时候

objc 复制代码
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
            
}); 

Block的三层拷贝

这里有一个Block的三层copy,我们先分析学习有关于

objc 复制代码
static void *_Block_copy_internal(const void *arg, const int flags) 
{
    struct Block_layout *aBlock;
    const bool wantsOne = (WANTS_ONE & flags) == WANTS_ONE;

    if (!arg) return NULL;
    
    aBlock = (struct Block_layout *)arg; // 强制转化成Block_layout对象,防止对外界造成影响
    if (aBlock->flags & BLOCK_NEEDS_FREE)         // NSConcreteMallocBlock
      //是否需要释放
    {
        latching_incr_int(&aBlock->flags);
        return aBlock;
    }
    else if (aBlock->flags & BLOCK_IS_GLOBAL)     // NSConcreteGlobalBlock
      //如果是全局block,直接返回
    {
        return aBlock;
    }
		//	为栈block或者是堆区block,由于堆区需要申请内存,所以是栈区的block的操作
                                                 // Its a stack block.  Make a copy.
    struct Block_layout *result = malloc(aBlock->descriptor->size);
    if (!result) return (void *)0;
  //通过memmove内存拷贝,将aBlock拷贝到result
    memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first
    // reset refcount
    result->flags &= ~(BLOCK_REFCOUNT_MASK);    // XXX not needed
    result->flags |= BLOCK_NEEDS_FREE | 1;
    result->isa = _NSConcreteMallocBlock;
    if (result->flags & BLOCK_HAS_COPY_DISPOSE) 
    {
            (*aBlock->descriptor->copy)(result, aBlock); // do fixup
    }
        
    return result;
}

下面简单讲述一下这个步骤:

  • 如果需要释放,如果需要就直接释放
  • 如果是globalBlock不需要copy,直接返回
  • 否则就只有两种情况,栈区block和堆区block,但是由于需要拷贝,所以堆区Block需要申请内存,所以最后是有关于栈区Block的拷贝代码
    • 通过malloc申请内存来接受block
    • 通过memmove将block拷贝到新申请的内存中
    • 设置block对象的类型为堆区block,就是result->isa = _NSConcreteMallocBlock;
外界变量的类型
objc 复制代码
// Runtime support functions used by compiler when generating copy/dispose helpers

// Values for _Block_object_assign() and _Block_object_dispose() parameters
enum {
    // see function implementation for a more complete description of these fields and combinations
    //普通对象,即没有其他的引用类型,也就是我们任何的一个Object都是这个逻辑
    BLOCK_FIELD_IS_OBJECT   =  3,  // id, NSObject, __attribute__((NSObject)), block, ...
    //block类型作为变量
    BLOCK_FIELD_IS_BLOCK    =  7,  // a block variable
    //经过__block修饰的变量和对象
    BLOCK_FIELD_IS_BYREF    =  8,  // the on stack structure holding the __block variable
    //weak 弱引用变量
    BLOCK_FIELD_IS_WEAK     = 16,  // declared __weak, only used in byref copy helpers
    //返回的调用对象 - 处理block_byref内部对象内存会加的一个额外标记,配合flags一起使用
    BLOCK_BYREF_CALLER      = 128, // called from __block (byref) copy/dispose support routines.
};

这里我们在看一下这里的_Block_object_assign的源码,以及和下面这段Block拷贝的代码进行对比:

objc 复制代码
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        extern NSString* nanString;
        int loaclB = 10;
        __block NSObject* obj = [[NSObject alloc] init];
         NSMutableArray* ary = [NSMutableArray array];
        void(^block)(void) = ^{
            
            [ary addObject:@"123"];
            NSLog(@"123%ld %@", loaclB, obj);
        };
        block();
        [ary addObject:@"23333"];
        NSLog(@"%@", ary);
        loaclB++;
    }
    return 0;
}

//反编译之后变成下面这样:
//block的拷贝函数
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->ary, (void*)src->ary, 3/*BLOCK_FIELD_IS_OBJECT*/);_Block_object_assign((void*)&dst->obj, (void*)src->obj, 8/*BLOCK_FIELD_IS_BYREF*/);} //这里调用了这个_Block_object_assign方法

//block里面的匿名函数
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_obj_0 *obj = __cself->obj; // bound by ref
  NSMutableArray *ary = __cself->ary; // bound by copy
  int loaclB = __cself->loaclB; // bound by copy

            ((void (*)(id, SEL, ObjectType _Nonnull))(void *)objc_msgSend)((id)ary, sel_registerName("addObject:"), (id _Nonnull)(NSString *)&__NSConstantStringImpl__var_folders_rb_0ts7dvhs3zg9fc6kk2nvtlr00000gn_T_main_d58752_mi_0);
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_rb_0ts7dvhs3zg9fc6kk2nvtlr00000gn_T_main_d58752_mi_1, loaclB, (obj->__forwarding->obj));
        }
//block的结构
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  NSMutableArray *ary;
  int loaclB;
  __Block_byref_obj_0 *obj; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSMutableArray *_ary, int _loaclB, __Block_byref_obj_0 *_obj, int flags=0) : ary(_ary), loaclB(_loaclB), obj(_obj->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

这里我们看到了这里有两种类型:

BLOCK_FIELD_IS_OBJECTBLOCK_FIELD_IS_BYREF这里在这三层拷贝的最后一层进行一个讲解

这里如果有__block修饰的变量就会先调用一次这个方法:

objc 复制代码
static struct Block_byref *_Block_byref_copy(const void *arg) {
    
    //强转为Block_byref结构体类型,保存一份
    struct Block_byref *src = (struct Block_byref *)arg;

    if ((src->forwarding->flags & BLOCK_REFCOUNT_MASK) == 0) {
        // src points to stack 申请内存
        struct Block_byref *copy = (struct Block_byref *)malloc(src->size);
        copy->isa = NULL;
        // byref value 4 is logical refcount of 2: one for caller, one for stack
        copy->flags = src->flags | BLOCK_BYREF_NEEDS_FREE | 4;
        //block内部持有的Block_byref 和 外界的Block_byref 所持有的对象是同一个,这也是为什么__block修饰的变量具有修改能力
        //copy 和 scr 的地址指针达到了完美的同一份拷贝,目前只有持有能力
        copy->forwarding = copy; // patch heap copy to point to itself // 把堆区的指针指向自己
        src->forwarding = copy;  // patch stack to point to heap copy // 把栈区的指针指向堆区,保证数值一致
        copy->size = src->size;
        //如果有copy能力
        if (src->flags & BLOCK_BYREF_HAS_COPY_DISPOSE) { // 有自己的一个copy的逻辑
            // Trust copy helper to copy everything of interest
            // If more than one field shows up in a byref block this is wrong XXX
            //Block_byref_2是结构体,__block修饰的可能是对象,对象通过byref_keep保存,在合适的时机进行调用
            struct Block_byref_2 *src2 = (struct Block_byref_2 *)(src+1);
            struct Block_byref_2 *copy2 = (struct Block_byref_2 *)(copy+1);
            copy2->byref_keep = src2->byref_keep; // 调用自定义复制逻辑
            copy2->byref_destroy = src2->byref_destroy;

            if (src->flags & BLOCK_BYREF_LAYOUT_EXTENDED) {
                struct Block_byref_3 *src3 = (struct Block_byref_3 *)(src2+1);
                struct Block_byref_3 *copy3 = (struct Block_byref_3*)(copy2+1);
                copy3->layout = src3->layout;
            }
            //等价于 __Block_byref_id_object_copy
            (*src2->byref_keep)(copy, src);
        }
        else {
            // Bitwise copy.
            // This copy includes Block_byref_3, if any.
            memmove(copy+1, src+1, src->size - sizeof(*src)); //如果为简单类型就直接进行一个内存拷贝
        }
    }
    // already copied to heap
    else if ((src->forwarding->flags & BLOCK_BYREF_NEEDS_FREE) == BLOCK_BYREF_NEEDS_FREE) {
        latching_incr_int(&src->forwarding->flags);
    }
    
    return src->forwarding;
}

这个函数完成了copy之后会调用我们的最后一个函数_Block_object_assign这个函数由编译器来处理

第三层拷贝是block对传入对象的变量进行_Block_object_assign,将block内部将要使用的对象的变量拷贝到block内部。

上面我们自己给出的那段代码中有BLOCK_FIELD_IS_OBJECTBLOCK_FIELD_IS_BYREF

这两种都会进入我们的最后的_Block_object_assign然后进行不同的处理

  • 如果是普通对象,则交给系统ARC处理,并拷贝对象指针,就是引用计数甲一,所以外界变量不能释放
  • 如果是block类型的变量,则通过_Block_copy操作,将block拷贝到堆区
  • 如果是__block修饰的变量,就通过_Block_byref_copy函数进行内存拷贝以及常规处理

三层拷贝总结:

第一层通过_Block_copy实现对象的自身拷贝,从栈区拷贝到堆区

第二层通过调用_Block_byref_copy这个来实现对于对象拷贝成Block_byref类型

第三次调用_Block_object_assign对于__block修饰的当前变量内部对象的内存管理

当且仅当用__block变量的时候才会有三次拷贝.

Block循环引用

先认识一下什么是循环引用:

  • 正常使用: 是指A持有B的引用,当A调用dealloc方法,给B发送release信号,B收到release信号,如果此时B的retainCount为0的时候,则调用B的dealloc方法
  • 循环引用:A, B互相持有,所以导致A无法调用dealloc方法给Breleasse信号,所以B也无法接受到release信号,所以A,B此时都无法释放

解决循环引用

objc 复制代码
self.name = @"123";
self.testBlock = ^(void){
    NSLog(@"%@", self.name);
};
UIView animateWithDuration:1 animations:^{
    NSLog(@"%@",self.name);
};

代码第一种发生了一个循环引用的问题,因为在block中持有了外部变量name,导致block也持有了self,而self本来是持有block的,所以导致了self和block的互相持有代码二中没有循环引用,因为self中没有持有animation的block,不构成互相持有

weak-strong-dance

如果block内部并未嵌套block,直接使用__weak修饰self即可

objc 复制代码
self.name = @"123";
    __weak typeof (self) weakSelf = self;
    self.testBlock = ^(void){
        NSLog(@"%@", weakSelf.name);
    };

此时的weakSelfself指向同一片内存空间,且使用__weak不会导致self的引用计数发生变化

objc 复制代码
__weak typeof (self) weakSelf = self;
    self.testBlock = ^(void){
        NSLog(@"%@", weakSelf.name);
    };

如果block里面嵌套了block,那么就要同时使用__weak__strong

objc 复制代码
self.testBlock = ^(void){
        __strong typeof(weakSelf) strongSelf = weakSelf;
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"%@", weakSelf.name);
        });
    };

如果不采用强弱共舞的话,可能会出现一个过了一会self被释放导致我们获取不到对应的数据,所以要采用__strong修饰一下,

其中strongSelf是一个临时变量,在cjlBlock的作用域内,即内部block执行完就释放strongSelf,所以并不会出现block持有self的一个情况导致这些问题

__block修饰符

我们可以采用__block修饰符,在主动调用完后手动释放self

objc 复制代码
__block PersonViewController* vc = self;
    self.testBlock = ^(void){
        NSLog(@"%@", vc.name);
        vc = nil;
    };
self.testBlock();

需要注意的是这里的block必须调用如果不调用block的话,vc就不会主动置为nil,那么仍旧是循环引用,self和block都不会被释放

对象self作为参数

主要是把对象self作为参数,提供给block内部使用,不会有引用计数问题

objc 复制代码
self.testBlock = ^(PersonViewController* vc){
        NSLog(@"%@", vc.name);
    }; 

小结

笔者这里简单总结了有关于Block的一个知识,复习了有关于Block捕获变量,以及使用__block修饰的时候forwarding指针的有一个变化,还有一个它发生拷贝的一个时机,以及复习了几种有关于解决Block循环引用的内容.

相关推荐
liucan23322 分钟前
JS执行速度似乎并不比Swift或者C语言慢
前端·ios
虾球xz37 分钟前
CppCon 2015 学习:CLANG/C2 for Windows
开发语言·c++·windows·学习
蓝婷儿1 小时前
6个月Python学习计划 Day 17 - 继承、多态与魔术方法
开发语言·python·学习
持续前进的奋斗鸭2 小时前
Postman测试学习(1)
学习·postman
hello kitty w2 小时前
Python学习(7) ----- Python起源
linux·python·学习
一叶知秋秋2 小时前
python学习day39
人工智能·深度学习·学习
永日456703 小时前
学习日记-day24-6.8
开发语言·学习·php
pop_xiaoli3 小时前
OC学习—命名规范
学习·ios
jackson凌3 小时前
【Java学习笔记】String类(重点)
java·笔记·学习