OC中的block块的一点备忘

先说点其他的,不知道是网站访问量过大,还是因为设置不合理或者受攻击了,从一月份开始主机就不断被关闭,上次升级了下服务器,终于平静了段时间,但是今天早上又出现了,然后就选择关闭了一点功能,比如rss全文订阅,这个估计用的人比较少,先关一段时间试试效果。

今天说下OC的block块的一点备忘,Block是iOS中一种比较特殊的数据类型,是苹果官方特别推荐使用的一种数据类型,应用场景有:动画、多线程、集合遍历、网络请求回调等

Block的作用:用来保存某一段代码,可以在恰当的时间取出来再调用。

基础的介绍使用就看参考链接里面的那两个文章就行了,这里说下几个注意地方,昨天看到的,感觉别人总结的很全面

2015102512250906.jpg

一、基本的几个使用代码示例

1.1、基础使用

block的基础使用可以参考下面的代码,当然也可以有返回值,这里没有设置返回值

#import "ViewController.h"
typedef void(^testtt2)(int);

@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];

    UIButton *button2 = [[UIButton alloc] initWithFrame:CGRectMake(10, 200, 100, 40)];
    [button2 setTitle:@"声明块使用" forState:UIControlStateNormal];
    [button2 setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
    [button2 addTarget:self action:@selector(test2) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:button2];

}
//声明块
-(void)test2{
    NSLog(@"test2");
    //声明一个块
    void (^testt)(int) = ^(int count){
        NSLog(@"%d",count);
    };
    //使用
    testt(10);

    //使用typedaf块
    testtt2 te = ^(int count){
        NSLog(@"%d",count);
    };
    te(20);

    //更改局部变量
    __block int m =0;
    testtt2 te2 = ^(int count){
        m=count;
        NSLog(@"m1: %d",m);
    };
    te2(30);
     NSLog(@"m2: %d",m);

    //加不加括号
    void (^test3)()=^{
        NSLog(@"没有变量");
    };
    test3();

    void (^test4)()=^(){
        NSLog(@"没有变量2");
    };
    test4();
}

@end

1.2、回调使用

block块也可以作为回调函数,作用类似于delegate,但是更加简洁方便,这里写一个BlockTest的类,在这个类中有一个请求函数,当函数执行之后回调

1.2.1、BlockTest.h

#import <Foundation/Foundation.h>

typedef void(^testCall)(int m,NSString*str);
@interface BlockTest : NSObject
@property(copy,nonatomic)testCall tess;
//发起一个请求,请求结束回掉block块
-(void)touchWithCall:(testCall)tes;
@end

1.2.2、BlockTest.m

#import "BlockTest.h"

@implementation BlockTest
-(void)touchWithCall:(testCall)tes
{
    self.tess = tes;
    if (self.tess) {
        self.tess(1,@"hudongdong");
    }
}
@end

1.2.3、使用的时候,比如按钮点击时发送请求

- (void)viewDidLoad {
    [super viewDidLoad];
    UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(10, 100, 100, 40)];
    ;
     forState:UIControlStateNormal];
    ;
    [self.view addSubview:button];

}

//块回调
-(void)test{
    NSLog(@"test");
    BlockTest *bb = [[BlockTest alloc] init];
    [bb touchWithCall:^(int m, NSString *str) {
        NSLog(@"回调了%d,%@",m,str);
    }];
}

这里请求完毕之后就会得到回调结果1,"hudongdong"了。

二、Block块使用的六个注意事项

这个是网友总结的,原文链接在参考链接里面

对于刚学习OC新伙伴,block块一直都是一个比较纠结、比较难懂的知识点,不过,在使用一段时间后,就会感觉很酸爽。block块的原理及使用我就不再赘述,网上有很多这方面的资料。我个人使用这么长时间以来,觉得使用block块应该注意以下几点。

2.1、在使用block前需要对block指针做判空处理

例如:XXXX为定义的block块

if (XXXX ) {                  
    if(XXXX != nil){
        //XXXX(参数);或XXXX(参数); 
    }                                
}

在使用block块,最好对block进行判空处理,不进行判空处理直接使用的话,一旦指针为空就会直接产生崩溃。

2.2、block如果作为属性变量时,要copy一下,将栈上的block拷贝到堆上

例如,作为属性时,写成@property (nonatomac,copy)XXXXXX;(注:XXXXXX为block块)

如果不进行copy,若是栈上的block被释放,此block块属性变量就为空了,程序有可能就会直接产生崩溃。

2.3、在block使用之后要对block指针做赋空值处理,如果是MRC的编译环境下,要先release掉block对象。

block作为类对象的成员变量,使用block的人有可能用类对象参与block中的运算而产生循环引用。

将block赋值为空,是解掉循环引用的重要方法。(不能只在dealloc里面做赋空值操作,这样已经产生的循环引用不会被破坏掉)

例如:

if (_sucBlock ) {
  _sucBlock(参数);
}
//MRC下:要先将[_sucBlock release];(之前copy过)
_sucBlock = nil; //在使用之后将Block赋空值,解引用 !!!

还有一种改法,在block接口设计时,将可能需要的变量作为形参传到block中,从设计上解决循环引用的问题。

2.4、使用时将self或成员变量加入block之前要先将self变为__weak弱引用

这与第三条其实是一个问题,就是解决循环引用问题。

2.5、在多线程环境下(block中的weakSelf有可能被析构的情况下),需要先将self转为strong指针,避免在运行到某个关键步骤时self对象被析构。

第四、第五条合起来有个名词叫weak–strong dance,以下代码来自AFNetworking,堪称使用weak–strong dance的经典。

__weak typeof(self) weakSelf = self;
AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
    __strong typeof(weakSelf) strongSelf = weakSelf;
    strongSelf.networkReachabilityStatus = status;
    if (strongSelf.networkReachabilityStatusBlock) {
        strongSelf.networkReachabilityStatusBlock(status);
    }
};

第一行:__weak __typeof(self)weakSelf = self;

如之前第四条所说,为防止callback内部对self强引用,weak一下。

第三行:__strong typeof(weakSelf) strongSelf = weakSelf;

按照之前第五条的说法给转回strong了,这里typeof()里面写的是weakSelf,里面写self也没有问题,因为typeof是编译时确定变量类型,所以这里写self 不会被循环引用。

第四、五、六行,如果不转成strongSelf而使用weakSelf,后面几句话中,有可能在第四句执行之后self的对象可能被析构掉,然后后面的StausBlock没有执行,导致逻辑错误。

最后第五行,使用前对block判空。

其中用到了__typeof(self),这里涉及几个知识点:

2.5.1. __typeof、__typeof__、typeof的区别

没有区别,对于老的LLVM编译器上面这句话会编译报错,所以在很早的ARC使用者中流行__typeof(&*self)这种写法

2.6、block回调不起作用,可能是调用block属性变量的类的实例对象已不是原来的对象。

这个问题只能具体情况具体分析了,程序运行可能不会错,就是block回调不起作用,有些功能实现不了,断点调试发现根本不走回调。之前我有一个同事就遇到过这个问题,另外一个同事给他解决了一个小时也没解决,我让他检查一下调用block块的类对象,果然,与原来的地址不是一个,他又新建一个对象,前后就花了1分钟解决(得意一下)。

三、demo下载

Github下载:https://github.com/DamonHu/blcokTest

Gitosc下载:http://git.oschina.net/DamonHoo/blcokTest

四、参考链接

Last modification:March 22nd, 2018 at 02:15 pm
如果看了这个文章可以让你少加会班,可以请我喝杯可乐
已打赏名单
微信公众号

Leave a Comment