ios 添加block 类别_iOS-Block 浅谈

前言:Block 是开发过程中常用便捷的回调方式,本文简单介绍 Block

一、Block 简介

Block 对象是 C 级别的语法和运行时特性,和标准的 C 函数类似,除了可执行代码外,还可能包含变量自动绑定(栈)和内存托管(堆)。一个 Block 维护一个状态集。

闭包 = 一个函数「或指向函数的指针」+ 该函数执行的外部的上下文变量「也就是自由变量」;Block 是 Objective-C 对于闭包的实现。

可以嵌套定义,定义 Block 方法和定义函数方法相似

Block 可以定义在方法内部或外部

只有调用 Block 时候,才会执行其{}体内的代码

本质是对象,使代码高聚合

使用 clang 将 OC 代码转换为 C++ 文件查看 block 的方法:

在命令行输入代码 clang -rewrite-objc 需要编译的OC文件.m

这时查看当前的文件夹里 多了一个相同的名称的 .cpp 文件,在命令行输入 open main.cpp 查看文件

1.1 定义和使用

1.无参数无返回值

void (^ MyBlockOne)(void) = ^(void){

NSLog(@"无参数,无返回值");

};

MyBlockOne();//block的调用

2.有参数无返回值

void(^MyblockTwo)(int a) = ^(int a){

NSLog(@"@ = %d我就是block,有参数,无返回值",a);

};

MyblockTwo(100);

3.有参数有返回值

int(^MyBlockThree)(int,int) = ^(int a,int b){

NSLog(@"%d我就是block,有参数,有返回值",a + b);

return a + b;

};

MyBlockThree(12,56);

4.无参数有返回值

int(^MyblockFour)(void) = ^{

NSLog(@"无参数,有返回值");

return45;

};

MyblockFour();

5.定义声明

声明

typedef void (^Block)();

typedef int (^MyBlock)(int , int);

typedef void(^ConfirmBlock)(BOOL isOK);

typedef void(^AlertBlock)(NSInteger alertTag);

定义属性

@property (nonatomic,copy) MyBlock myBlockOne;

使用

self.myBlockOne = ^int (int ,int){

//TODO

}

1.2 Block与外界变量

1、截获自动变量(局部变量)值

(1)默认情况

对于 block 外的变量引用,block 默认是将其复制到其数据结构中来实现访问的。也就是说block的自动变量截获只针对block内部使用的自动变量, 不使用则不截获, 因为截获的自动变量会存储于block的结构体内部, 会导致block体积变大。特别要注意的是默认情况下block只能访问不能修改局部变量的值。

int age = 10;

myBlock block = ^{

NSLog(@"age = %d", age);

};

age = 18;

block();

输出结果:

age = 10

(2) __block 修饰的外部变量

对于用 __block 修饰的外部变量引用,block 是复制其引用地址来实现访问的。block可以修改__block 修饰的外部变量的值。

__block int age = 10;

myBlock block = ^{

NSLog(@"age = %d", age);

};

age = 18;

block();

输出为:

age = 18

2、__block 修饰的外部变量的值就可以被block修改

我们使用 clang 将 OC 代码转换为 C++ 文件:

clang -rewrite-objc 源代码文件名

__block int val = 10;

转换成

__Block_byref_val_0 val = {

0,

&val,

0,

sizeof(__Block_byref_val_0),

10

};

会发现一个局部变量加上__block修饰符后竟然跟block一样变成了一个__Block_byref_val_0结构体类型的自动变量实例。

此时我们在block内部访问val变量则需要通过一个叫__forwarding的成员变量来间接访问val变量。

1.3 Block 存储

1、Block的存储域及copy操作

由C/C++/OBJC编译的程序占用内存分布的结构:

内存分布结构

block有三种类型:

全局块(_NSConcreteGlobalBlock)

栈块(_NSConcreteStackBlock)

堆块(_NSConcreteMallocBlock)

三种block各自的存储域:

全局块存在于全局内存中, 相当于单例.

栈块存在于栈内存中, 超出其作用域则马上被销毁

堆块存在于堆内存中, 是一个带引用计数的对象, 需要自行管理其内存

block存储域

简而言之,存储在栈中的Block就是栈块、存储在堆中的就是堆块、既不在栈中也不在堆中的块就是全局块。

1.4 判断Block的存储位置

(1)Block不访问外界变量(包括栈中和堆中的变量)

Block 既不在栈又不在堆中,在代码段中,ARC和MRC下都是如此。此时为全局块。

(2)Block访问外界变量

MRC 环境下:访问外界变量的 Block 默认存储栈中。

ARC 环境下:访问外界变量的 Block 默认存储在堆中(实际是放在栈区,然后ARC情况下自动又拷贝到堆区),自动释放。

ARC下,访问外界变量的 Block为什么要自动从栈区拷贝到堆区呢?

栈上的Block,如果其所属的变量作用域结束,该Block就被废弃,如同一般的自动变量。当然,Block中的__block变量也同时被废弃。

为了解决栈块在其变量作用域结束之后被废弃(释放)的问题,我们需要把Block复制到堆中,延长其生命周期。开启ARC时,大多数情况下编译器会恰当地进行判断是否有需要将Block从栈复制到堆,如果有,自动生成将Block从栈上复制到堆上的代码。Block的复制操作执行的是copy实例方法。Block只要调用了copy方法,栈块就会变成堆块。

栈块copy堆块

在ARC的Block是配置在栈上的,所以返回函数调用方时,Block变量作用域就结束了,Block会被废弃。种情况编译器会自动完成复制。

在非ARC情况下则需要开发者调用copy方法手动复制。

将Block从栈上复制到堆上相当消耗CPU,所以当Block设置在栈上也能够使用时,就不要复制了,因为此时的复制只是在浪费CPU资源。

Block的复制操作执行的是copy实例方法。不同类型的Block使用copy方法的效果如下表:

Block的复制操作

根据表得知,Block在堆中copy会造成引用计数增加,这与其他Objective-C对象是一样的。虽然Block在栈中也是以对象的身份存在,但是栈块没有引用计数,因为不需要,我们都知道栈区的内存由编译器自动分配释放。

不管Block存储域在何处,用copy方法复制都不会引起任何问题。在不确定时调用copy方法即可。在ARC有效时,多次调用copy方法完全没有问题:

blk = [[[[blk copy] copy] copy] copy];

// 经过多次复制,变量blk仍然持有Block的强引用,该Block不会被废弃。

1.5 __block变量与__forwarding

在copy操作之后,既然__block变量也被copy到堆上去了, 那么访问该变量是访问栈上的还是堆上的呢?__forwarding 终于要闪亮登场了。通过__forwarding, 无论是在block中还是 block外访问__block变量, 也不管该变量在栈上或堆上, 都能顺利地访问同一个__block变量。

__block与__forwarding

1.6 Block 循环引用

Block 循环引用的情况:

某个类将 block 作为自己的属性变量,然后该类在 block 的方法体里面又使用了该类本身。

self.someBlock = ^(Type var){

[self dosomething];

};

解决办法:

(1)ARC 下:使用 __weak

__weak typeof(self) weakSelf = self;

self.someBlock = ^(Type var){

[weakSelf dosomething];

};

(2)MRC 下:使用 __block

__block typeof(self) blockSelf = self;

self.someBlock = ^(Type var){

[blockSelf dosomething];

};

解决办法:

//1.使用__weak ClassName

__block XXViewController* weakSelf = self;

self.blk = ^{

NSLog(@"In Block : %@",weakSelf);

};

//2.使用__weak typeof(self)

__weak typeof(self) weakSelf = self;

self.blk = ^{

NSLog(@"In Block : %@",weakSelf);

};

//3.Reactive Cocoa中的@weakify和@strongify

@weakify(self);

self.blk = ^{

@strongify(self);

NSLog(@"In Block : %@",self);

};

二、Block 应用

2.1 Block 应用

1、Block作为变量(Xcode快捷键:inlineBlock)

int (^sum) (int, int); // 定义一个 Block 变量 sum

// 给 Block 变量赋值

// 一般 返回值省略:sum = ^(int a,int b)…

sum = ^int (int a,int b){

return a+b;

}; // 赋值语句最后有 分号

int a = sum(10,20); // 调用 Block 变量

2、Block作为属性(Xcode 快捷键:typedefBlock)

// 1. 给 Calculate 类型 sum变量 赋值「下定义」

typedef int (^Calculate)(int, int); // calculate就是类型名

Calculate sum = ^(int a,int b){

return a+b;

};

int a = sum(10,20); // 调用 sum变量

// 2. 作为对象的属性声明,copy 后 block 会转移到堆中和对象一起

@property (nonatomic, copy) Calculate sum; // 使用 typedef

@property (nonatomic, copy) int (^sum)(int, int); // 不使用 typedef

// 声明,类外

self.sum = ^(int a,int b){

return a+b;

};

// 调用,类内

int a = self.sum(10,20);

3、作为 OC 中的方法参数

// ---- 无参数传递的 Block ---------------------------

// 实现

- (CGFloat)testTimeConsume:(void(^)())middleBlock {

// 执行前记录下当前的时间

CFTimeInterval startTime = CACurrentMediaTime();

middleBlock();

// 执行后记录下当前的时间

CFTimeInterval endTime = CACurrentMediaTime();

return endTime - startTime;

}

// 调用

[self testTimeConsume:^{

// 放入 block 中的代码

}];

// ---- 有参数传递的 Block ---------------------------

// 实现

- (CGFloat)testTimeConsume:(void(^)(NSString * name))middleBlock {

// 执行前记录下当前的时间

CFTimeInterval startTime = CACurrentMediaTime();

NSString *name = @"有参数";

middleBlock(name);

// 执行后记录下当前的时间

CFTimeInterval endTime = CACurrentMediaTime();

return endTime - startTime;

}

// 调用

[self testTimeConsume:^(NSString *name) {

// 放入 block 中的代码,可以使用参数 name

// 参数 name 是实现代码中传入的,在调用时只能使用,不能传值

}];

4、Block回调

Block回调是关于Block最常用的内容,比如网络下载,我们可以用Block实现下载成功与失败的反馈。block使用简单,逻辑清晰,灵活。

2.2 Block 几种类型演算

{

NSLog(@"\n--------------------block调用 基本数据类型---------------------\n");

int a = 10;

NSLog(@"block定义前a地址=%p", &a);

void (^aBlock)() = ^(){

NSLog(@"block定义内部a地址=%p", &a);

};

NSLog(@"block定义后a地址=%p", &a);

aBlock();

}

/*

结果:

block定义前a地址=0x7fff5bdcea8c

block定义后a地址=0x7fff5bdcea8c

block定义内部a地址=0x7fa87150b850

*/

/*

流程:

1. block定义前:a在栈区

2. block定义内部:里面的a是根据外面的a拷贝到堆中的,不是一个a

3. block定义后:a在栈区

*/

{

NSLog(@"\n--------------------block调用 __block修饰的基本数据类型---------------------\n");

__block int b = 10;

NSLog(@"block定义前b地址=%p", &b);

void (^bBlock)() = ^(){

b = 20;

NSLog(@"block定义内部b地址=%p", &b);

};

NSLog(@"block定义后b地址=%p", &b);

NSLog(@"调用block前 b=%d", b);

bBlock();

NSLog(@"调用block后 b=%d", b);

}

/*

结果:

block定义前b地址=0x7fff5bdcea50

block定义后b地址=0x7fa873b016d8

调用block前 b=10

block定义内部b地址=0x7fa873b016d8

调用block后 b=20

*/

/*

流程:

1. 声明 b 为 __block (__block 所起到的作用就是只要观察到该变量被 block 所持有,就将“外部变量”在栈中的内存地址放到了堆中。)

2. block定义前:b在栈中。

3. block定义内部: 将外面的b拷贝到堆中,并且使外面的b和里面的b是一个。

4. block定义后:外面的b和里面的b是一个。

5. block调用前:b的值还未被修改。

6. block调用后:b的值在block内部被修改。

*/

{

NSLog(@"\n--------------------block调用 指针---------------------\n");

NSString *c = @"ccc";

NSLog(@"block定义前:c=%@, c指向的地址=%p, c本身的地址=%p", c, c, &c);

void (^cBlock)() = ^{

NSLog(@"block定义内部:c=%@, c指向的地址=%p, c本身的地址=%p", c, c, &c);

};

NSLog(@"block定义后:c=%@, c指向的地址=%p, c本身的地址=%p", c, c, &c);

cBlock();

NSLog(@"block调用后:c=%@, c指向的地址=%p, c本身的地址=%p", c, c, &c);

}

/* 输出结果

block定义前:c=ccc, c指向的地址=0x1068aac68, c本身的地址=0x7ffee93a7ab8

block定义后:c=ccc, c指向的地址=0x1068aac68, c本身的地址=0x7ffee93a7ab8

block定义内部:c=ccc, c指向的地址=0x1068aac68, c本身的地址=0x6000002542a0

block调用后:c=ccc, c指向的地址=0x1068aac68, c本身的地址=0x7ffee93a7ab8

c指针本身在block定义中和外面不是一个,但是c指向的地址一直保持不变。

1. block定义前:c指向的地址在堆中, c指针本身的地址在栈中。

2. block定义内部:c指向的地址在堆中, c指针本身的地址在堆中(c指针本身和外面的不是一个,但是指向的地址和外面指向的地址是一样的)。

3. block定义后:c不变,c指向的地址在堆中, c指针本身的地址在栈中。

4. block调用后:c不变,c指向的地址在堆中, c指针本身的地址在栈中。

*/

{

NSLog(@"\n--------------------block调用 指针并修改值---------------------\n");

NSMutableString *d = [NSMutableString stringWithFormat:@"ddd"];

NSLog(@"block定义前:d=%@, d指向的地址=%p, d本身的地址=%p", d, d, &d);

void (^dBlock)() = ^{

NSLog(@"block定义内部:d=%@, d指向的地址=%p, d本身的地址=%p", d, d, &d);

d.string = @"dddddd";

};

NSLog(@"block定义后:d=%@, d指向的地址=%p, d本身的地址=%p", d, d, &d);

dBlock();

NSLog(@"block调用后:d=%@, d指向的地址=%p, d本身的地址=%p", d, d, &d);

}

/*输出结果

block定义前:d=ddd, d指向的地址=0x600000440300, d本身的地址=0x7ffee9b2dab8

block定义后:d=ddd, d指向的地址=0x600000440300, d本身的地址=0x7ffee9b2dab8

block定义内部:d=ddd, d指向的地址=0x600000440300, d本身的地址=0x604000253940

block调用后:d=dddddd, d指向的地址=0x600000440300, d本身的地址=0x7ffee9b2dab8

d指针本身在block定义中和外面不是一个,但是d指向的地址一直保持不变。

在block调用后,d指向的堆中存储的值发生了变化。

*/

{

NSLog(@"\n--------------------block调用 __block修饰的指针---------------------\n");

__block NSMutableString *e = [NSMutableString stringWithFormat:@"eee"];

NSLog(@"block定义前:e=%@, e指向的地址=%p, e本身的地址=%p", e, e, &e);

void (^eBlock)() = ^{

NSLog(@"block定义内部:e=%@, e指向的地址=%p, e本身的地址=%p", e, e, &e);

e = [NSMutableString stringWithFormat:@"new-eeeeee"];

};

NSLog(@"block定义后:e=%@, e指向的地址=%p, e本身的地址=%p", e, e, &e);

eBlock();

NSLog(@"block调用后:e=%@, e指向的地址=%p, e本身的地址=%p", e, e, &e);

}

/*

从block定义内部使用__block修饰的e指针开始,e指针本身的地址由栈中改变到堆中,即使出了block,也在堆中。

在block调用后,e在block内部重新指向一个新对象,e指向的堆中的地址发生了变化。

*/

{

NSLog(@"\n--------------------block调用 retain cycle---------------------\n");

View *v = [[View alloc] init];

v.tag = 1;

v.frame = CGRectMake(100, 100, 100, 100);

[self.view addSubview:v]; //self->view->v

void (^block)() = ^{

v.backgroundColor = [UIColor orangeColor]; //定义内部:block->v

};

v.block = block; //v->block

block();

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

//预计3秒后释放v对象。

[v removeFromSuperview];

});

}

/*

结果:

不会输出 dealloc.

*/

/*

流程:

1. self->view->v

2. block定义内部:block->v 因为block定义里面调用了v

3. v->block

结论:

引起循环引用的是block->v->block,切断其中一个线即可解决循环引用,跟self->view->v这根线无关

*/

{

NSLog(@"\n--------------------block调用self---------------------\n");

View *v = [[View alloc] init];

v.tag = 2;

v.frame = CGRectMake(100, 220, 100, 100);

[self.view addSubview:v]; //self->view->v

void (^block)() = ^{

self.view.backgroundColor = [UIColor redColor]; //定义内部:block->self

_count ++; //调用self的实例变量,也会让block强引用self。

};

v.block = block; //v->block

block();

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

//预计3秒后释放self这个对象。

AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;

appDelegate.window.rootViewController = nil;

});

}

/*

结果:

不会输出 dealloc.

*/

/*

流程:

1. self->view->v

2. v->block

3. block->self 因为block定义里面调用了self

结论:

在block内引用实例变量,该实例变量会被block强引用。

引起循环引用的是self->view->v->block->self,切断一个线即可解决循环引用。

*/

2.3 Block 存储域

1. 在全局数据区的Block对象

NSGlobalBlock 静态block,释放有两种不同的时机:

1、如果这个block引用了外部变量后是栈block,则在定义此block的函数出栈时,block释放。

2、如果这个blcok引用了外部变量之后是堆block,则其宿主target释放的时候此block才释放。

{

NSLog(@"\n--------------------block的存储域 全局块---------------------\n");

void (^blk)(void) = ^{

NSLog(@"Global Block");

};

blk();

NSLog(@"%@", [blk class]);

}

/*

结果:输出 __NSGlobalBlock__

*/

/*

结论:

全局块:这种块不会捕捉任何状态(外部的变量),运行时也无须有状态来参与。块所使用的整个内存区域,在编译期就已经确定。

全局块一般声明在全局作用域中。但注意有种特殊情况,在函数栈上创建的block,如果没有捕捉外部变量,block的实例还是会被设置在程序的全局数据区,而非栈上。

*/

2.在堆上创建的Block对象

NSMallocBlock 堆区block

堆区是内存的常驻区域,也叫永久存储区,block一般在函数中定义,最多是个栈block。在MRC时代你需要使用Block_copy()方法,才可以将blcok复制到堆中。

复制到堆中有何用处呢?

首先,作为一个对象,把它复制到堆中,想要使用它肯定要有一个指针指向它,而指向它的指针是作为property或静态变量出现的(如果不被引用也就没有了常驻于堆区的意义),而实际开发中多使用poperty引用。

在MRC中,如果一个类的block属性是使用copy修饰的,则不需要手动调用Block_copy将其复制到堆中。如果是用strong修饰的,则必须使用Block_copy()将其复制到堆中,并在释放时调用Block_release()方法将其释放,否则会因野指针导致程序崩溃。

{

NSLog(@"\n--------------------block的存储域 堆块---------------------\n");

int i = 1;

void (^blk)(void) = ^{

NSLog(@"Malloc Block, %d", i);

};

blk();

NSLog(@"%@", [blk class]);

}

/*

结果:输出 __NSMallocBlock__

*/

/*

结论:

堆块:解决块在栈上会被覆写的问题,可以给块对象发送copy消息将它拷贝到堆上。复制到堆上后,块就成了带引用计数的对象了。

在ARC中,以下几种情况栈上的Block会自动复制到堆上:

- 调用Block的copy方法

- 将Block作为函数返回值时(MRC时此条无效,需手动调用copy)

- 将Block赋值给__strong修饰的变量时(MRC时此条无效)

- 向Cocoa框架含有usingBlock的方法或者GCD的API传递Block参数时

上述代码就是在ARC中,block赋值给__strong修饰的变量,并且捕获了外部变量,block就会自动复制到堆上。

*/

3.在栈上创建的Block对象

NSStackBlock 栈区block

函数只有入栈后才能执行,出栈后就释放了。

栈block一般在函数内部定义,并在函数内部调用;或者在函数外部定义,作为函数的一个参数在函数内部调用。函数出栈时和其他变量或参数一起释放。

{

NSLog(@"\n--------------------block的存储域 栈块---------------------\n");

int i = 1;

__weak void (^blk)(void) = ^{

NSLog(@"Stack Block, %d", i);

};

blk();

NSLog(@"%@", [blk class]);

}

/*

结果:输出 __NSStackBlock__

*/

/*

结论:

栈块:块所占内存区域分配在栈中,编译器有可能把分配给块的内存覆写掉。

在ARC中,除了上面四种情况,并且不在global上,block是在栈中。

*/

三、Block 原理

Block 优缺点

优点:

捕获外部变量

降低代码分散程度

缺点:

循环引用引起内存泄露

Block 总结

在block内部使用的是将外部变量的拷贝到堆中的(基本数据类型直接拷贝一份到堆中,对象类型只将在栈中的指针拷贝到堆中并且指针所指向的地址不变)。

__block修饰符的作用:是将block中用到的变量,拷贝到堆中,并且外部的变量本身地址也改变到堆中。

__block不能解决循环引用,需要在block执行尾部将变量设置成nil

__weak可以解决循环引用,block在捕获weakObj时,会对weakObj指向的对象进行弱引用。

使用__weak时,可在block开始用局部__strong变量持有,以免block执行期间对象被释放。

全局块不引用外部变量,所以不用考虑。

堆块引用的外部变量,不是原始的外部变量,是拷贝到堆中的副本。

栈块本身就在栈中,引用外部变量不会拷贝到堆中。

__weak 本身是可以避免循环引用的问题的,但是其会导致外部对象释放了之后,block 内部也访问不到这个对象的问题,我们可以通过在 block 内部声明一个 __strong 的变量来指向 weakObj,使外部对象既能在 block 内部保持住,又能避免循环引用的问题。

__block 本身无法避免循环引用的问题,但是我们可以通过在 block 内部手动把 blockObj 赋值为 nil 的方式来避免循环引用的问题。另外一点就是 __block 修饰的变量在 block 内外都是唯一的,要注意这个特性可能带来的隐患。

block的实现原理是C语言的函数指针。函数指针即函数在内存中的地址,通过这个地址可以达到调用函数的目的。

Q:什么是Block?

A:Block是将函数及其执行上下文封装起来的对象

struct __block_impl{

void *isa;//Block 是对象的标志

int Flags;

int Reserved;

void *FuncPtr;//函数指针

};

Q:什么是Block调用?

A:Block调用是函数调用

Q:Block 如何截获变量?

A:1.基本数据类型的局部变量截获其值

2.对象类型的局部变量连同所有权修饰符一起截获

3.局部静态变量以指针形式截获

4.不截获全局变量、静态全局变量

Q:什么情况使用__block修饰符?

A:一般情况下,对被截获变量进行赋值操作需要添加__block修饰符

赋值 != 使用

赋值:

赋值操作需要使用 __block修饰

__block NSMutableArray *arrM = nil;

void (^testBlock)(void) = ^{

arrM = [NSMutableArray array];

};

testBlock();

使用:如下代码不需要使用__block,因为是对数组的操作而不是数组的赋值。

NSMutableArray *arrM = [NSMutableArray array];

void (^testBlock)(void) = ^{

[arrM addObject:@"addObj"];

};

testBlock();

需要修饰符

不需要修饰符

Q:__block做了什么

A:__block修饰变量变成了对象

__block修饰符

Q:Block的Copy操作效果

A:如图

Block的Copy操作效果

Q:栈上Block的销毁

A:如图

栈上Block的销毁

Q:栈上Block的Copy操作

A:如图

Q:栈上Block的Copy,MRC是否会引起内存泄漏

A:会的

i栈上Block的Copy

Q:栈上__block变量的Copy操作

A:如图,修改__block变量值,修改的都是堆上的值

栈上__block变量的Copy操作

Q:__forwarding存在的意义

A:无论在栈还是堆上,__forwarding都可以顺利访问到同一个__block变量

Q:Block外部定义__weak修饰变量可以解决循环引用?

A:Block截获对象是连同所有权修饰符一起截获的,如在外部对变量进行__weak修饰,结构体里持有的对象类型也是weak

Q:Block的引用循环,如图代码出现什么问题?

A:MRC下,不会产生循环引用;ARC下回产生循环引用,引起内存泄漏

ARC下的引用循环

ARC下的引用循环

ARC下的引用循环解决方案

解决方案

上述代码:ARC下解决方案

弊端:如果该Block长时间不被引用,该断环处一直存在,循环引用无法解除

解决方案

Question1:__weak修饰对象,当外部对象释放了之后,block 内部也访问不到这个对象,怎么办?

Answer:通过在 block 内部声明一个 __strong 的变量来指向 weakObj,使外部对象既能在 block 内部保持住,又能避免循环引用的问题。

__weak typeof(self) weakSelf = self;

self.block = ^{

__strong typeof(weakSelf) strongSelf = weakSelf;

[strongSelf print];

};

我们以 AFNetworking 中 AFNetworkReachabilityManager.m 的一段代码举例:

__weak __typeof(self)weakSelf = self;

AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {

__strong __typeof(weakSelf)strongSelf = weakSelf;

strongSelf.networkReachabilityStatus = status;

if (strongSelf.networkReachabilityStatusBlock) {

strongSelf.networkReachabilityStatusBlock(status);

}

};

Question2:__strong修饰对象,会不会引起循环引用?

Answer:不会!

详解:

__weak修饰的对象被Block引用,不会影响对象的释放,而__strong在Block内部修饰的对象,会保证,在使用这个对象在scope内,这个对象都不会被释放,出了scope,引用计数就会-1。

self是一个指向实例对象的指针,它的生命周期至少是伴随着当前的实例对象的,一旦它和对象之间有循环引用是无法被自动打破的;strongSelf是block内部的一个局部变量,变量的作用域仅限于局部代码,而程序一旦跳出作用域,strongSelf就会被释放,这个临时产生的“循环引用”就会被自动打破,代码的执行事实上也是这样子的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值