超棒文集iOS-blockiOS9开发技术

[iOS]Block技术中的weak-strong

2015-09-11  本文已影响6882人  肖浩呗

本篇文章的主要内容

一、看一看什么是Block

我们使用^运算符来声明一个Block变量,而且在声明完一个Block变量后要像声明普通变量一样,最后要加;

int (^block)( int ) = NULL;

我们一起来看一下声明Block变量的语法

数据返回值类型  (^变量名) (参数列表) = NULL;
block = ^(int m){
   return m * m; 
};
//通过使用block变量,计算整型常量10的平方,并且打印在控制器输出
NSLog(@"10的平方是:%d",block(10));

注意,到目前我们应该有发现block变量的使用步骤,有类似于函数的步骤

二、看一看如何直接使用block参数

    //声明数组变量
    NSMutableArray *mutableArray = [[NSMutableArray alloc] initWithObjects:@"5",@"2",@"3",@"9",@"7", nil];
    
    //直接使用block进行数组升序排序
    [mutableArray sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
        //将两个参数转换为字符串的对象
        NSString *value1 = (NSString *)obj1;
        NSString *value2 = (NSString *)obj2;
        
        //value1与value2两个对象的比较结果直接返回
        return [value1 compare:value2];
    }];
    
    //打印可变数组变量
    NSLog(@"%@",mutableArray);
    //声明网络地址对象
    NSURL *url = [NSURL URLWithString:@"http://www.qq.com"];
    
    //根据网络地址对象,声明网络请求对象
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    
    //直接使用block变量完成链接成功后的数据返回功能
    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
        //将二进制数据使用utf8编码转换成字符串对象
        NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        
        //打印链接完成后的结果
        NSLog(@"%@",result);
        
    }];

可以看出ios平台中的很多功能都已经集成了Block语法的处理方法.

三、看一看深入理解Block语法

在本节,主要要去介绍的就是使用__block修饰的变量能够完成的作用。

先来看一个例子。

    //声明一个局部整型变量
    int intValue = 3;
    
    //声明一个返回值为int,一个int参数的block变量
    int (^block)(int) = ^(int m){
        return m * intValue;
    };
    
    //调用block变量,5作为参数之后的结果
    NSLog(@"block(5) = %d",block(5));

在上面的例子中,我们将intValue变量称为block执行过程中的外部变量,在block执行过程中可以直接使用该外部变量。

再看一个例子。

    //声明一个局部整型变量
    int intValue = 3;
    
    //声明一个返回值为int,一个int参数的block变量
    int (^block)(int) = ^(int m){
        intValue++;
        return m * intValue;
    };
    
    //调用block变量,5作为参数之后的结果
    NSLog(@"block(5) = %d",block(5));

在上面的例子中,我们编译程序后发现编译器会有红色错误,错误提示为
Variable is not assignable (missing __block type specifier)

为什么会出现不能被赋值的错误提示呢?

那为了避免上述错误,就要使用__block修饰符来修饰外部变量,用来通知编译器该外部变量intValueblock中的intValue指的是同一块儿内存地址,而不需要内存拷贝。


如下例:

    //将intValue局部整型变量使用__block修饰符进行修饰
    __block int intValue = 3;
    
    //声明一个返回值为int,一个int参数的block变量
    int (^block)(int) = ^(int m){
        intValue++;
        return m * intValue;
    };
    
    //调用block变量,5作为参数之后的结果
    NSLog(@"block(5) = %d",block(5));

四、使用Block要注意的内存问题

使用weak–strong dance技术来避免循环引用

举例如下

//
//  ViewController.m
//
//  Created by lewis.
//

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController
{
    id observer;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    //添加观察者,观察主题修改消息通知,并且在收到消息通知后,打印视图控制器对象
    observer = [[NSNotificationCenter defaultCenter] addObserverForName:@"kThemeChangeNotification" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
        NSLog(@"%@",self);
    }];
}

//当视图控制器对象销毁时,移除观察者
- (void)dealloc
{
    if (observer) {
        [[NSNotificationCenter defaultCenter] removeObserver:observer];
    }
}
@end

在上面代码中,我们添加向通知中心注册了一个观察者,然后在 dealloc 时解除该注册,一切看起来正常。但这里有两个问题:
在消息通知 block 中引用到了 self,在这里 self 对象被 block 保留一次,而 observer 又 retain 该 block的一份拷贝,通知中心又持有 observer。因此只要 observer对象还没有被解除注册,block 就会一直被通知中心持有,从而 self 就不会被释放,其 dealloc 就不会被调用。而我们却又期望在 dealloc 中通过 removeObserver 来解除注册以消除通知中心对 observer/block 的 保留次数。
同时,observer 是在 self 所在类中定义赋值,因此是被 self retain 的,这样就形成了循环引用。
解决方式如下.

//
//  ViewController.m
//
//  Created by lewis.
//

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController
{
    id observer;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    //先声明一个weak弱对象
    __weak ViewController *wSelf = self;
    
    //添加观察者,观察主题修改消息通知,并且在收到消息通知后,打印视图控制器对象
    observer = [[NSNotificationCenter defaultCenter] addObserverForName:@"kThemeChangeNotification" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note){
        
        //在block的执行过程中,使用强对象对弱对象进行引用
        ViewController *bSelf = wSelf;
        if (bSelf) {
            NSLog(@"%@",bSelf);
        }
    }];
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

//当视图控制器对象销毁时,移除观察者
- (void)dealloc
{
    if (observer) {
        [[NSNotificationCenter defaultCenter] removeObserver:observer];
    }
}

@end 

通过这种weak-strong手法,block 就不会持有 self 的引用,从而打破了循环引用。

上一篇下一篇

猜你喜欢

热点阅读