【ios block】iOS开发中Block深入使用探索

时间:2020-03-25  来源:ios  阅读:

Block在ios 4.0之后加入,并大量使用在新的ios api中。block是一个匿名的代码块,可以作为传递给其他对象的参数,并得到返回值。从本质上讲,block同其他普通的变量类似,只是其储存的数据是一个函数体。Block不只是针对Objective-C的专利,而是一种可以应用于C、C++和OBjective-C的语言层面的新特性。通过使用block,开发者可以将一段代码段像某一个数值一样当做参数传递给函数。同时,blocks也是Objective-C的一种对象,可以像其他对象一样添加到NSArray或者NSDictionary等集合中。

块语法,本质上是匿名函数。与函数指针很相似
Block封装了一段代码,可以在任何时候执行
Block可以作为函数参数或者函数的返回值,而其本身又可以带输入参数或返回值。

Block基本使用

1> 如何定义block变量

int (^sumBlock)(int, int);

 void (^myBlock)();

 int (^myBlock)(int) = ^(int num){ return num *multiplier };

 
 

2> 如何利用block封装代码


 ^(int a, int b) {

    return a - b;

 };

 

 ^() {

    NSLog(@"----------");

 };

 

 ^ {

    NSLog(@"----------");

 };



 3> block访问外面变量

   * block内部可以访问外面的变量

   * 默认情况下,block内部不能修改外面的局部变量

   * 给局部变量加上__block关键字,这个局部变量就可以在block内部修改

  * 给局部变量加上static关键字,这个局部变量就可以在block内部修改

 4> 利用typedef定义block类型


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

 // 以后就可以利用MyBlock这种类型来定义block变量

 MyBlock block;

 MyBlock b1, b2;

 

 b1 = ^(int a, int b) {

    return a - b;

 };

 

 MyBlock b3 = ^(int a, int b) {

    return a - b;

 };

 

Block使用的细节和本质

1.block实际上是指向结构体的指针

2.编译器会将block的内部代码生成对应的函数

Block的内存管理 

 1.默认情况下, block的内存是在栈中

    * 它不会对所引用的对象进行任何操作

 2.如果对block做一次copy操作, block的内存就会在堆中

    * 它会对所引用的对象做一次retain操作

    * 非ARC : 如果所引用的对象用了__block修饰, 就不会做retain操作

    * ARC : 如果所引用的对象用了__unsafe_unretained\__weak修饰, 就不会做retain操作

 

  这里有一篇个人比较喜欢的关于Block内存管理文章,有兴趣研究的可以看看:Block 的内存管理

Block使用注意

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

 

  不判空直接使用,一旦指针为空直接产生崩溃。

 


if (!self.isOnlyNet) {

    if (succBlock == NULL) { //后面使用block之前要先做判空处理

        return;

    }

    id data = [NSKeyedUnarchiver unarchiveObjectWithFile:[self favoriteFile]];

    if ([data isKindOfClass:[NSMutableArray class]]) {

        succBlock(data,YES);

    }else{

        succBlock(nil,YES);

    }

}



 

2、使用方将self或成员变量加入block之前要先将self变为__weak

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

 


__weak __typeof(self)weakSelf = self;

AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {

    __strong __typeof(weakSelf)strongSelf = weakSelf;

    strongSelf.networkReachabilityStatus = status;

    if (strongSelf.networkReachabilityStatusBlock) {

        strongSelf.networkReachabilityStatusBlock(status);

    }

};



Block使用中的一些疑问解答


主要是阐述一下Block中如何的使用外部变量以及block本身的内存管理。
 
先定义一个block变量,作为后续的例子中使用:
 

    typedef void(^BlockCC)(void);
    BlockCC _block;

 
1、block中引用外部变量


block中可以直接使用外部的变量,比如
 

    int number = 1;
    _block = ^(){
        NSLog(@"number %d", number);
    };

 
那么实际上,在block生成的时候,是会把number当做是常量变量编码到block当中。可以看到,以下的代码,block中的number值是不会发生变化的:
 

    int number = 1;
    _block = ^(){
        NSLog(@"number %d", number);
    };
    number = 2;
    _block();

则输出的值为 1,而不是2。原因就是如上所说。
 
如果要在block中尝试改变外部变量的值,则会报错的。对于这个问题的解决办法是引入__block标识符。将需要在block内部修改的变量标识为__block scope。更改后的代码如下:
 

    __block int number = 1;
    _block = ^(){
        number++;
        NSLog(@"number %d", number);
    };

而这个时候,其实block外部的number和block内部的number指向了同一个值,回到刚才的在外部改变block的例子,它的输出结果将是2,而不是1。有兴趣的可以自己写一个例子试试。
 
2、block自身的内存管理


block本身是像对象一样可以retain,和release。但是,block在创建的时候,它的内存是分配在栈(stack)上,而不是在堆(heap)上。他本身的作于域是属于创建时候的作用域,一旦在创建时候的作用域外面调用block将导致程序崩溃。比如下面的例子。
我在view did load中创建了一个block:
 

    - (void)viewDidLoad
    {
        [superviewDidLoad];
      
        int number = 1;
        _block = ^(){
      
        NSLog(@"number %d", number);
    };
    }

并且在一个按钮的事件中调用了这个block:
 

    - (IBAction)testDidClick:(id)sender {
        _block();
    }

此时我按了按钮之后就会导致程序崩溃,解决这个问题的方法就是在创建完block的时候需要调用copy的方法。copy会把block从栈上移动到堆上,那么就可以在其他地方使用这个block了~
修改代码如下:
 

    _block = ^(){
        NSLog(@"number %d", number);
    };
      
    _block = [_blockcopy];

同理,特别需要注意的地方就是在把block放到集合类当中去的时候,如果直接把生成的block放入到集合类中,是无法在其他地方使用block,必须要对block进行copy。不过代码看上去相对奇怪一些:
 

    [array addObject:[[^{
        NSLog(@"hello!");
    } copy] autorelease]];


3、循环引用


这一点其实是在第一点的一个小的衍生。当在block内部使用成员变量的时候,比如
 

    @interface ViewController : UIViewController
    {
        NSString *_string;
    }
    @end

在block创建中:
 

    _block = ^(){
        NSLog(@"string %@", _string);
    };

这里的_string相当于是self->_string;那么block是会对内部的对象进行一次retain。也就是说,self会被retain一次。当self释放的时候,需要block释放后才会对self进行释放,但是block的释放又需要等self的dealloc中才会释放。如此一来变形成了循环引用,导致内存泄露。
 
修改方案是新建一个__block scope的局部变量,并把self赋值给它,而在block内部则使用这个局部变量来进行取值。因为__block标记的变量是不会被自动retain的。
 

    __block ViewController *controller = self;
    _block = ^(){
        NSLog(@"string %@", controller->_string);
    };

【ios block】iOS开发中Block深入使用探索

http://m.bbyears.com/shoujikaifa/89572.html

推荐访问:
相关阅读 猜你喜欢
本类排行 本类最新