iOS - block详解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/coderMy/article/details/53418201

最近赶项目 , 自己时间比较少 , 加上也没找到什么可写的东西 , 就写一点关于block的东西吧 . 一些细节还是值得思考的 , 基本上也把所有的情况和大致原理都罗列了一些 . 这里我们创建Person类 和ViewController类来做一些试验和比较.


Person类 .h 文件

#import <Foundation/Foundation.h>
/**
 *  无返回值 可以接收参数的block
 *
 *  @param number block接收参数类型
 */
typedef void(^MYOneBlock)(NSInteger number);
/**
 *  无返回值,不能接收参数的block
 */
typedef void(^MYTwoBlock)();
/**
 *  有返回值,可以接收参数的block
 *
 *  @param number 接收参数类型
 *
 *  @return 返回值类型
 */
typedef BOOL(^MYThreeBlock)(NSInteger number);
/**
 *  有返回值,不能接收参数的block
 *
 *  @return 返回值类型
 */
typedef BOOL(^MYFourBlock)();



@interface Person : NSObject

/**
 *
 *
 *  @param MYOneBlock 为此方法参数, block参数可以接收参数,无返回值
 */
+ (void)testWithOneBlock:(MYOneBlock)MYOneBlock;
/**
 *
 *
 *  @param MYTwoBlock 为此方法参数, block参数不能接收参数,无返回值
 */
+ (void)testWithTwoBlock:(MYTwoBlock)MYTwoBlock;
/**
 *  <#Description#>
 *
 *  @param MYThreeBlock 为此方法参数, block参数可以接收参数,且有返回值
 */
+ (void)testWithThreeBlock:(MYThreeBlock)MYThreeBlock;
/**
 *  <#Description#>
 *
 *  @param MYFourBlock 为此方法参数, block参数不能接收参数,有返回值
 */
+ (void)testWithFourBlock:(MYFourBlock)MYFourBlock;


Person类 . m 文件

@property (strong, nonatomic) UIView *testView;

@property (copy, nonatomic) void(^testBlock)();

@end

@implementation ViewController

#import "Person.h"

@implementation Person


+ (void)testWithOneBlock:(MYOneBlock)MYOneBlock
{
    //此处回调
    //向MYOneBlock参数传入5,执行block块,block块中打印参数
    MYOneBlock(5);
    NSLog(@"MYOneBlock当前类为:%@",[self class]);

}

/**
 *  MYTwoBlock();内 打印类为viewController
 *  当前类为Person
 *  结论: 当block块作为传输时,block块中的self是为地址传递
 */
+ (void)testWithTwoBlock:(MYTwoBlock)MYTwoBlock
{
    //观察此处打印值 ,block中打印参数
    MYTwoBlock();
    NSLog(@"MYTwoBlock当前类为:%@",[self class]);

}

+ (void)testWithThreeBlock:(MYThreeBlock)MYThreeBlock
{
    //向MYThreeBlock参数传入 10,并接收block块的返回值,当前方法中打印返回值
    NSInteger number = MYThreeBlock(10);
    NSLog(@"%li",number);
    NSLog(@"MYThreeBlock当前类为:%@",[self class]);

}

+ (void)testWithFourBlock:(MYFourBlock)MYFourBlock
{
    //执行MYFourBlock block参数,接收返回值,当前方法中打印返回值
    NSInteger number = MYFourBlock();
    NSLog(@"%li",number);
    NSLog(@"MYFourBlock当前类为:%@",[self class]);
}

ViewController .m文件

例1: block调用机制

- (void)test0
{
    /**
     *  调用过程: 调用testWithOneBlock:方法 ,参数为可以接收参数的block块
     *
     *  @param number <#number description#>
     *
     *  @return <#return value description#>
     */

    [Person testWithOneBlock:^(NSInteger number) {

        NSLog(@"%li",number);

    }];


    /**
     *  调用过程: 调用testWithTwoBlock:方法,传入block块
     */
    [Person testWithTwoBlock:^{

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

    }];


    /**
     *  调用过程:调用testWithThreeBlock: 方法,传入一个有返回值,且可以接收参数的block块
     *
     *  @param number <#number description#>
     *
     *  @return <#return value description#>
     */
    [Person testWithThreeBlock:^BOOL(NSInteger number) {

        return number + 10;

    }];


    /**
     *  调用testWithFourBlock:方法,传入一个有返回值的block块
     */
    [Person testWithFourBlock:^BOOL{

        return 88;

    }];
}

例 2 : block的循环引用 实验方法: 给当前类viewController增添一个block作为属性

- (void)test
{
    /**
     * 如此写法,会报一个警告: lead to a retain cycle 因为造成了循环引用
     */
    //循环引用过程: testBlock作为当前类viewController实例对象(self)的属性,self.testBlock 指针指向testBlock块的内存空间.此处,访问self.testView,testBlock又指向了self.testView .因为循环引用,解决方式如下:
    /*
    self.testBlock = ^{

        self.testView = [[UIView alloc]initWithFrame:(CGRect){100,100,100,100}];

    };
     */



    //解决方案原理一致,选择各按喜好

    //解决方案1:
//    typeof(self) weakself = self;
//    self.testBlock = ^{
//        
//        weakself.testView = [[UIView alloc]initWithFrame:(CGRect){100,100,100,100}];
//        
//    };

    //解决方案2:
     __weak ViewController * weakself = self;

    self.testBlock = ^{

        weakself.testView = [[UIView alloc]initWithFrame:(CGRect){100,100,100,100}];

    };
}

例3: block块中修改外界值

- (void)test2
{
    //情况1: 访问外界对象
    UIView *myView = [[UIView alloc]initWithFrame:(CGRect){100,100,100,100}];

    self.testBlock = ^{
//        a = 10;
        myView.frame = CGRectMake(10, 10, 10, 10);

        NSLog(@"%@",NSStringFromCGRect(myView.frame));
    };

    self.testBlock();

    //情况2 : 访问外界变量

    //经c++反编译可以得出 , block内部中访问外部变量 , 外部变量只是值的传递到block内部 ,所以如果不用__block修饰 , 直接在block内部 写 a = 10 , 是会报错的 .   如果用__block修饰之后 , block内部则可以访问到 a 的地址 对其值进行修改
    __block int a =5;

    self.testBlock = ^{

        a = 10;

    };
    NSLog(@"%d",a);


    //情况3: 访问外界变量
    int b = 666;

    self.testBlock = ^{

        NSLog(@"%d",b);
    };

    b = 1000000000;

    self.testBlock();

    //此类情况 , NSLog会打印 666 而不是1000000000   原因是程序从上往下运行 ,NSLog(@"%d",b);这一步时已经记录了 b的值 , 所以后面 更改b =1000000000 并不能改变打印值

猜你喜欢

转载自blog.csdn.net/coderMy/article/details/53418201