美文网首页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

友情链接:

相关文章

  • iOS block 为什么官方文档建议用 copy 修饰

    一、block 的三种类型block 三种类型:全局 block,堆 block、栈 block。全局 block...

  • Block

    一、Block本质 二、 BlocK截获变量 三、__block 修饰变量 四、Block内存管理 五、Block...

  • 关于block--你想了解的几乎都在这里了

    一.block定义二.block的本质三.block变量捕获(Capture)四.block的类型五.block的...

  • iOS Block

    Block的分类 Block有三种类型:全局Block,堆区Block,栈区Block 全局Block 当Bloc...

  • block使用及其底层原理

    一 block基本使用 二 block底层结构 三 block变量捕获 四 block的类型 五 block对象类...

  • Block 与 Closure

    Block In OC block 分为以下三种: _NSConcreteStackBlock:栈block,引用...

  • iOS 常见面试题 -- block

    一、block的本质 二、block的类型 block 有三种类型分别如下: 如何区分block的类型 三、blo...

  • block的三种形式

    一、block的三种形式 堆block: 在堆上的block,用__block修饰的外部参数,会将block拷贝(...

  • iOS ---block

    block是封装了函数调用和调用环境的oc对象 (一)block的三种形式 block有三种形式,全局block、...

  • iOS Block 部分二

    主要讲解 Block 的分类和变量捕获的强弱引用; Block部分一Block部分二Block部分三Block知识...

网友评论

    本文标题:block(三)

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