美文网首页iOS
block(三)

block(三)

作者: dandelionYD | 来源:发表于2019-03-21 10:18 被阅读11次

    引言

    我们在前面知道了block内部捕获外部的变量blcok(一)blcok(二)

    block_07.png

    发现修改不了被捕获的变量

    解决:

    int main(int argc, const char * argv[]) {
        @autoreleasepool {
          __block int age = 10;
            void (^myBlock)(void) = ^{
                age = 20;
                NSLog(@"age = %d",age);
            };
            myBlock();
            NSLog(@"age = %d",age);
        }
        return 0;
    }
    打印:
    12.__block的使用[34938:11235669] age = 20
    12.__block的使用[34938:11235669] age = 20
    
    • __block的作用:用于解决block内部无法修改auto变量值的问题
    • 注意:__block不能修饰全局变量、静态变量(static)

    我们看下底层的c++源码

    xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp

    block_08.png

    底层:编译器会将__block变量包装成一个对象

    #import <Foundation/Foundation.h>
    typedef void(^myBlock)(void);
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            NSMutableArray *arr = [NSMutableArray array];
            myBlock block = ^{
                [arr addObject:@"1"];
                [arr addObject:@"2"];
            };
            block();
            NSLog(@"arr = %@",arr);
        }
        return 0;
    }
    打印:
    13.__block的使用2[35103:11287535] arr = (
        1,
        2
    )
    
    不会报错:因为只是来用arr的,而不是来修改arr的(arr = nil)
    
    说明:block在修改NSMutableArray,需不需要添加__block?
    为数组增删改的时候不需要
    修改指针的对象的时候需要
    

    __block的细节

    #import <Foundation/Foundation.h>
    struct __block_impl {
        void *isa;
        int Flags;
        int Reserved;
        void *FuncPtr;
    };
    
    struct __main_block_desc_0 {
        size_t reserved;
        size_t Block_size;
    };
    
    struct __Block_byref_age_0 {
        void *__isa; // 8
        struct __Block_byref_age_0 *__forwarding; //8
        int __flags; //4
        int __size; //4
        int age;
    };
    
    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(^myBlock)(void);
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            __block int age = 10;
            myBlock block = ^{
                age = 20;
                NSLog(@"%d",age);
            };
            block();
            
            struct __main_block_impl_0 *blockImpl = (__bridge struct __main_block_impl_0*)block;
            NSLog(@"%p",&age);
            NSLog(@"End"); //打个断点
        }
        return 0;
    }
    
    打印:
    14._ _block细节[35269:11308052] 20
    14._ _block细节[35269:11308052] 0x100711318
    (lldb) p/x blockImpl->age
    (__Block_byref_age_0 *) $0 = 0x0000000100711300
    (lldb) p/x &(blockImpl->age->age)
    (int *) $1 = 0x0000000100711318
    (lldb) p/x &age
    (int *) $2 = 0x0000000100711318
    (lldb) 
    
    发现:age的地址就是内部结构体的age地址
    &age  ==   &(blockImpl->age->age)
    
    (blockImpl->age)的地址 + 8 + 8 + 4 +4 =  (blockImpl->age->age)的地址值
    

    __block的内存管理

    • 当block在栈上的时候,并不会对__block变量产生强引用
    • 当block被copy到堆时
      • 会调用block内部的copy函数
      • copy函数内部会调用_ Block _ object _assign函数
      • _ Block_ object _assign函数会对 _ block 变量形成强引用(retain)
    block_09.png
    • 当block从堆中移除的时候
      • 会调用block内部的dispose函数
      • dispose函数内部会调用_Block _object _ dispose函数
      • _Block _object _ dispose函数会自动释放引用的 _block变量(release)
    block_10.png

    _ _ block的_forwarding指针

    还是以【上述__block细节】代码

    我们发现

    block_11.png

    _ _ block的_forwarding指针:指向的是 自身的isa

    block_12.png

    对象类型的auto变量、__block变量

    • 当block在栈上时,对他们不会产生强引用

    • 当block拷贝到堆上的时,都会通过copy函数来处理他们

      • __block变量(假设为变量a)
        • _Block _object _ assigin ( (void*)&dst->a , (void*)src->a,8/ *BLOCK_FIELD_IS_BYREF*/);
      • 对象类型的auto变量(假设为变量p)
        • _Block _object _ assigin ( (void*)&dst->p , (void*)src->p,3/ *BLOCK_FIELD_IS__OBJECT*/);
    • 当block从 堆上移除的时候,都会通过dispose来移除他们

      • __block变量(假设为变量a)
        • _Block _object _dispose ((void*)src->a,8/ *BLOCK_FIELD_IS_BYREF*/);
      • 对象类型的auto变量(假设为变量p)
        • _Block _object _dispose ((void*)src->p,3/ *BLOCK_FIELD_IS__OBJECT*/);
    对象 BLOCK_FIELD_IS__OBJECT
    __block变量 BLOCK_FIELD_IS_BYREF

    被__block修饰的对象类型

    • 当__blcok变量在栈上时,不会对指向的对象产生强引用
    • 当__block变量被copy到堆上的时,
      • 会调用__block变量内部的copy函数
      • copy函数内部会调用_Block _object _assign函数
      • Block object assign函数会根据所指向对象的修饰符( strong、_ weak、__ unsafe _unretained) 做出相应的操作,形成强引用(retain)或者弱引用(注意:这里只要RAC有retain,MRC不会retain)
    • 如果__block变量从堆上移除
      • 会调用__block内部的dispose函数
      • dispose函数内部会调用_Block _object _ dispose函数
      • _Block _object _ dispose函数会自动释放指向的对象(release)
    #import <Foundation/Foundation.h>
    
    @interface myPerson:NSObject
    @end
    @implementation myPerson
    @end
    
    typedef void(^myBlock)(void);
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            myPerson *p = [[myPerson alloc]init];
            __block __weak myPerson *weakP = p;
            
            myBlock blcok = ^{
                NSLog(@"person = %@",weakP);
            };
            blcok();
    
        }
        return 0;
    }
    

    分析见下图:

    block_13.png

    循环引用

    我们看下下面的例子:

    #import <Foundation/Foundation.h>
    typedef void(^myBlock)(void);
    @interface myPerson : NSObject
    @property(nonatomic,copy)myBlock block;
    @end
    
    #import "myPerson.h"
    @implementation myPerson
    -(void)dealloc{
        NSLog(@"%s",__FUNCTION__);
    }
    @end
    
    #import <Foundation/Foundation.h>
    #import "myPerson.h"
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            myPerson *p = [[myPerson alloc]init];
            p.block = ^{
                NSLog(@"%@",p);
            };
            NSLog(@"End");
        }
        return 0;
    }
    
    发现:p对象并没有被销毁
    
    block_14.png

    分析:

    p对象里面持有block,而block里面又持有person —>造成了循环引用啦

    接下来我们来看看循环引用的解决:【ARC】

    #import <Foundation/Foundation.h>
    #import "myPerson.h"
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            myPerson *p = [[myPerson alloc]init];
            __weak myPerson *weakP = p;
            p.block = ^{
                NSLog(@"%@",weakP);
            };
            NSLog(@"End");
        }
        return 0;
    }
    打印:
    17.循环引用[35636:11420439] End
    17.循环引用[35636:11420439] -[myPerson dealloc]
    此时:person对象被释放了
    
    #import <Foundation/Foundation.h>
    #import "myPerson.h"
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            myPerson *p = [[myPerson alloc]init];
            __weak typeof(p) weakP = p;
            p.block = ^{
                NSLog(@"%@",weakP);
            };
            NSLog(@"End");  
        }
        return 0;
    }
    打印:
    17.循环引用[35659:11425413] End
    17.循环引用[35659:11425413] -[myPerson dealloc]
    
    #import <Foundation/Foundation.h>
    #import "myPerson.h"
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            myPerson *p = [[myPerson alloc]init];
            __unsafe_unretained  myPerson * weakP = p;
            p.block = ^{
                NSLog(@"%@",weakP);
            };
            NSLog(@"End");
        }
        return 0;
    }
    打印:
    17.循环引用[35674:11429822] End
    17.循环引用[35674:11429822] -[myPerson dealloc]
    
    #import <Foundation/Foundation.h>
    #import "myPerson.h"
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            myPerson *p = [[myPerson alloc]init];
            __block  myPerson * weakP = p;
            p.block = ^{
                NSLog(@"%@",weakP);
                weakP = nil;
            };
            p.block();
            NSLog(@"End");
        }
        return 0;
    }
    注意:必须要调用block才行
    打印:
    17.循环引用[35724:11431748] <myPerson: 0x1005053c0>
    17.循环引用[35724:11431748] End
    17.循环引用[35724:11431748] -[myPerson dealloc]
    

    总结:

    • 使用: __ weak、__ unsafe_unretained

      • __ weak:不会产生强引用,指向的对象销毁时,会自动让指针置为nil
      • __ unsafe_unretained,不会产生强引用 ,不安全,指向的对象销毁时,指针存储的地址值不变
    block_15.png
    • 使用:__ block(必须要调用block并且要做置空处理)
    block_16.png

    解决循环引用:【MRC】

    由于:MRC下不支持__weak

    所以 :只有__ unsafe_unretained、__ block方法

    mrc下不会对__block的对象进行强引用,所以mrc下不需要置位nil)

    #import <Foundation/Foundation.h>
    #import "myPerson.h"
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            myPerson *p = [[myPerson alloc]init];
            __unsafe_unretained  myPerson * weakP = p;
            p.block = ^{
                NSLog(@"%@",weakP);
            };
            NSLog(@"End");
        }
        return 0;
    }
    
    --------------
    #import <Foundation/Foundation.h>
    #import "myPerson.h"
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            myPerson *p = [[myPerson alloc]init];
            __block  myPerson * weakP = p;
            p.block = ^{
                NSLog(@"%@",weakP);
            };
            p.block();
            NSLog(@"End");
        }
        return 0;
    }
    

    补充:__ weak 和 __ strong

    myPerson.h
    #import <Foundation/Foundation.h>
    typedef void(^myBlock)(void);
    @interface myPerson : NSObject
    @property(nonatomic,copy)myBlock block;
    -(void)run;
    @end
    
    myPerson.m
    #import "myPerson.h"
    @implementation myPerson
    -(void)dealloc{
        NSLog(@"%s",__FUNCTION__);
    }
    -(void)run{
        NSLog(@"run----");
    }
    @end
    
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        myPerson *p = [[myPerson alloc]init];
        __weak myPerson *weakP = p;
        p.block = ^{
            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                [NSThread sleepForTimeInterval:5];
                NSLog(@"%@",weakP);
                [weakP  run];
            });
        };
        p.block();
        NSLog(@"End");
    }
    打印:
    block2[36791:11582899] End
    block2[36791:11582899] -[myPerson dealloc]
    block2[36791:11582959] (null)
    
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        myPerson *p = [[myPerson alloc]init];
        __weak myPerson *weakP = p;
        p.block = ^{
            __strong  myPerson *p = weakP;
            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                [NSThread sleepForTimeInterval:5];
                NSLog(@"%@",p);
                [p  run];
            });
        };
        p.block();
        NSLog(@"End");
    }
    打印:
    block2[36767:11581355] End
    block2[36767:11581544] <myPerson: 0x600001fc6be0>
    block2[36767:11581544] run----
    block2[36767:11581544] -[myPerson dealloc]
    

    分析见下图:

    block_17.png block_18.png

    友情链接:

    相关文章

      网友评论

        本文标题:block(三)

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