美文网首页
Block 对象探索

Block 对象探索

作者: 醉看红尘这场梦 | 来源:发表于2018-06-13 21:38 被阅读10次
  • Block对象是一段代码,先给出一个Block对象的示例:
^{
    NSLog("This is an instruction with in a block.");
}
  • 看上去和C函数类型,都是在一个花括号内的一套指令,但是它没有函数名,相应的位置只有一个^符号,^表示这段代码是一个Block对象。
  • 和函数一样,Block对象也可以有实参和返回值,再给出一个Block对象示例:
^(double dividend, double divisor){
  double quotient = dividend / divisor;
  return quotient;
}
  • 这段代码中的Block对象有两个参数,类型都是double,返回一个double类型的值。
  • Block对象可以被当做一个实参传递给可以接受block的方法,很多苹果的类型都有可以接收block为实参的方法。
代码实例
  • 创建三个数组对象,一个用于保存最初的字符串;一个用于保存去除了元音的字符串;最后一个用于保存需要从字符串中移除的字符。
#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        //创建两个数组对象,分别用于保存最初的字符串对象和去除元音字母后的版本
        NSArray *originalString = @[@"SauerKraut",@"Raygun",@"Big Nerd Ranch",@"Mississippi"];
        NSLog(@"original string: %@",originalString);
        NSMutableArray *devowelizedStrings = [NSMutableArray array];
        //创建数组对象,保存需要从字符串移除的字符
        NSArray *vowels = @[@"a",@"e",@"i",@"o",@"u"];
        
    }
    return 0;
}
声明Block变量
#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        //创建两个数组对象,分别用于保存最初的字符串对象和去除元音字母后的版本
        NSArray *oldStirngs = [NSArray arrayWithObjects:@"SauerKraut",@"Raygun",@"Big Nerd Ranch",@"Mississippi",nil];
        NSLog(@"original string: %@",oldStirngs);
        NSMutableArray *newStrings = [NSMutableArray array];
        //创建数组对象,保存需要从字符串移除的字符
        NSArray *vowels = [NSArray arrayWithObjects:@"a",@"e",@"i",@"o",@"u"];
        //声明Block变量
        void(^devowelizer)(id,NSInteger,BOOL *);
    }
    return 0;
}
  • 代码解析。Block变量声明做一个详细的介绍。Block变量的名字(如devowelizer)是写在括号中,跟在^字符后面的。Block的声明需要包括Block的返回类型(void)以及它的参数类型(id,NSInteger,BOOL *),这点类似函数的声明。
编写Block对象
  • 编写一个方法赋复制原始字符串,并将移除原始字符串的元音字母,然后将去除元音字母的字符串
#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        //创建两个数组对象,分别用于保存最初的字符串对象和去除元音字母后的版本
        NSArray *oldStirngs = [NSArray arrayWithObjects:@"SauerKraut",@"Raygun",@"Big Nerd Ranch",@"Mississippi",nil];
        NSLog(@"original string: %@",oldStirngs);
        NSMutableArray *newStrings = [NSMutableArray array];
        //创建数组对象,保存需要从字符串移除的字符
        NSArray *vowels = [NSArray arrayWithObjects:@"a",@"e",@"i",@"o",@"u",nil];
        //声明Block变量
        void(^devowelizer)(id,NSInteger,BOOL *);
        
        //将Block对象赋值给变量
        devowelizer = ^(id string,NSInteger i, BOOL *stop){
            NSMutableString *newString = [NSMutableString stringWithString:string];
            
            //枚举数组中的字符串,将所有出现的元音字母替换成空字符串
            for (NSString *s in vowels) {
                NSRange fullRange = NSMakeRange(0, [newString length]);
                [newString replaceOccurrencesOfString:s withString:@"" options:NSCaseInsensitiveSearch range:fullRange];
            }
            //[devowelizedStrings addObject:newString];
        };//Block变量赋值结束
    }
    return 0;
}

  • 此外,和其他变量一样,也可以将devowelizer的声明和赋值写在一起
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        //创建两个数组对象,分别用于保存最初的字符串对象和去除元音字母后的版本
        NSArray *oldStirngs = [NSArray arrayWithObjects:@"SauerKraut",@"Raygun",@"Big Nerd Ranch",@"Mississippi",nil];
        NSLog(@"original string: %@",oldStirngs);
        NSMutableArray *newStrings = [NSMutableArray array];
        //创建数组对象,保存需要从字符串移除的字符
        NSArray *vowels = [NSArray arrayWithObjects:@"a",@"e",@"i",@"o",@"u",nil];
        //声明Block变量
        void(^devowelizer)(id,NSInteger,BOOL *) = ^(id string, NSInteger i, BOOL *stop){
        
            NSMutableString *newString = [NSMutableString stringWithString:string];
            
            //枚举数组中的字符串,将所有出现的元音字母替换成空字符串
            for (NSString *s in vowels) {
                NSRange fullRange = NSMakeRange(0, [newString length]);
                [newString replaceOccurrencesOfString:s withString:@"" options:NSCaseInsensitiveSearch range:fullRange];
            }
            [newStrings addObject:newString];
        };
    }
    return 0;
}
传递Block对象
  • enumerateObjectsUsingBlock 要求传入的Block对象三个实参类型是固定的,第一个实参是对象指针,指向当前的对象,该指针的类型是id,所以无论数组包含的是什么类型的对象,都可以将地址赋值给指针。第二个实参的类型是NSInteger,其值是当前对象在数组中的索引。第三个实参是指向BOOL变量的指针,该变量的默认是NO。如果值为YES,那么数组对象会执行完当前的Blcok对象后终止枚举过程。
        //枚举数组对象,针对每个数组中的对象,执行Block对象devowelizer
        [oldStirngs enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            NSLog(@"new string: %@",newStrings);
        }];
  • 修改代码,检查字符串是否包含字母y,如果有,则设置指针指向YES,终止枚举
   //声明Block变量
        void(^devowelizer)(id,NSInteger,BOOL*);
        //将Block对象赋值给变量
        devowelizer = ^(id string,NSInteger i, BOOL *stop){
            NSRange yRange = [string rangeOfString:@"y" options:NSCaseInsensitiveSearch];
            //是否包含字符y?
            if (yRange.location != NSNotFound) {
                //执行完当前的Block对象后终止枚举过程
                *stop = YES;
                //结束当然正在执行的Block对象
                return;
            }
            
            NSMutableString *newString = [NSMutableString stringWithString:string];
            //枚举数组中的字符串,将所有出现的元音字母替换成空字符串
            for (NSString *s in vowels) {
                NSRange fullRange = NSMakeRange(0, [newString length]);
                [newString replaceOccurrencesOfString:s withString:@"" options:NSCaseInsensitiveSearch range:fullRange];
            }
            [newStrings addObject:newString];
typedef
  • Block对象的语法可能比较复杂,通过typedef关键字,可以将某个Block对象类型定义为一个类型。
  • 以下代码中的typedef语句看上去与Block变量声明很像,但是这里定义的是一个新的类型,而不是变量。
#import <Foundation/Foundation.h>

typedef void (^ArrayEnumerationBlock)(id,NSUInteger,BOOL *);
Block对象 vs 其他回调
  • 两种回调机制:委托机制和通告机制,通过回调机制,程序能够在特定事件发生时调用指定的方法,虽然以上两种回调机制能够很好完成任务,但是也有一个缺点,即回调的设置代码和回调方法的具体实现无法写在同一段代码中。
        [[NSNotificationCenter defaultCenter] addObserver:log selector:@selector(zoneChange:) name:NSSystemTimeZoneDidChangeNotification object:nil];

深入学习Block对象

返回值
  • 这个Block对象有两个类型为double的实参,返回同一个double类型的值,要在变量中保存这个Block,需要声明一个double类型的变量,然后再在Block赋值给这个变量
   ^(double dividend, double divisor){
       double quotient = dividend / divisor;
       return quotient;
   };
   //声明divBlock变量
   double (^divBlock)(double,double);
   
   //将Block对象赋值给变量
   divBlock = ^(double dividend, double divisor){
       double quotient = dividend / divisor;
       return quotient;
   };
诺名Block对象
  1. 三种方法可以将整数传递给方法
  2. 将Block对象传递给方法的的办法和传递整数相同。分别三行代码来声明Block对象,然后赋值,最后使用。
   //方法一:声明,赋值和使用完全分开
   int i;
   i = 5;
   NSNumber *num = [NSNumber numberWithInt:i];
   
   //方法二:声明,在一行中声明赋值使用
   int i2 = 5;
   NSNumber *num2 = [NSNumber numberWithInt:i2];
   
   //方法三:跳过变量声明步骤
   NSNumber *num3 = [NSNumber numberWithInt:5];
外部变量
  1. Block对象通常会使用外部创建的其他变量。这些外部创建的变量叫做外部变量。当执行Block对象时,为了确保其下的外部变量始终存在,相应的Block对象会捕获这些变量
  2. 对基本类型的变量,捕获意味着程序会拷贝变量的值,并用Block对象内存的局部变量保存。对指针类型的变量,Block对象会使用强引用,这意味凡是Block对象用到的对象,都会被保留。所以在相应的Block对象被释放前,这些对象一定不会被释放
在Block对象中使用self
  • 如果需要写一个使用self的Block对象,就必须要多做几步工作来避免造成强引用循环。以下实例,BNREmployee实例创建一个Block对象,每次执行的时候就会打印这个BNREmployee实例
   myBlock = ^ {
       NSLog(@"Employee: %@",self);
   };

  • BNREmployee有一个指向Block对象的指针,这个Block对象会捕获self,所以它有一个指回BNREmployee实例的指针。
  • 为了打破这个强引用循环,可以先在Block对象外声明一个__weak指针;然后将这个指针指向Block对象使用的self;最后在Block对象中使用新的指针
   //一个弱引用
   __weak BNREmployee *weakSelf = self;
   myBlock = ^ {
       NSLog(@"Employee: %@",self);
   };
  • 现在这个Block对象BNREmployee实例是弱引用,强引用循环打破了。
  • 然而,由于是弱引用,所以self指向的对象在Block执行的时候可能会被释放。
  • 为了避免这种情况的发生,可以再Block对象中创建一个self的局部强引用
   //一个弱引用
   __weak BNREmployee *weakSelf = self;
   myBlock = ^ {
       //局部w强引用
       BNREmployee *innerSelf = weakSelf;
       NSLog(@"Employee: %@",self);
   };
  • 通过创建innerSelf强引用,就可以在Block和BNREmployee实例中再次创建一个强引用循环。但是,由于innerSelf引用是针对Block内部的,所以只有在Block执行的时候它才会执行,而Block结束之后就会自动消失。
在Block对象中无意使用self
  • 如果直接在Block对象中使用实例变量,那么block会捕获self,而不是捕获实例变量。这是实例变量的一个鲜为人知的特点。例如,一下代码直接存取一个实例变量:
   __weak BNREmployee *weakSelf = self;
   myBlock = ^ {
       //局部w强引用
       BNREmployee *innerself = weakSelf;
       NSLog(@"Employee: %@",self);
       NSLog(@"Employee ID: %d",_employID);
   };
   //编辑器是这么解读代码
   __weak BNREmployee *weakSelf = self;
   myBlock = ^ {
       //局部w强引用
       BNREmployee *innerself = weakSelf;
       NSLog(@"Employee: %@",self);
       NSLog(@"Employee ID: %d", self->_employID);
   };
  • ->语法看上去是不是很熟悉?这个语法实际是用来后去堆上的成员结构的。从最底层来说,对象实际是结构。
  • 由于编辑器将_employID看成self->_employID,self就被Block对象无意地捕获了。这样又会造成之前使用weakSelf和innerSelf避免的强引用循环。
   __weak BNREmployee *weakSelf = self;
   myBlock = ^ {
       
       BNREmployee *innerself = weakSelf;
       NSLog(@"Employee: %@",self);
       NSLog(@"Employee ID: %d",innerSelf.employID);
   };

  • 现在没有直接地使用self了,就不会造成无意地强引用循环。
  • 在这种情况下,重要的是理解编辑器是如何思考的,这样才能避免隐藏地强引用循环。然而,绝不要使用->语法来存取对象的实例变量,这样做非常危险,可能会超越这个Block对象的范围。
修改外部变量
  1. 在Block对象中,被捕获的变量是常数,程序无法修改变量所保存的值。
  2. 如果需要在Block对象内修改某个外部变量,则可以在声明相应的外部变量时,在前面加上__block关键字。
  3. 列如以下代码在Block对象外部变量counter的值增加1.
   __block int counter = 0;
   void(^conterBlock)() = ^{
       counter++;
   };
   //counter增加1,数值为1
   counterBlock();
   //counter增加1,数值为2
   counterBlock();

相关文章

  • Block 对象探索

    Block对象是一段代码,先给出一个Block对象的示例: 看上去和C函数类型,都是在一个花括号内的一套指令,但是...

  • block分析(下)

    block通过clang分析 带着下面的疑问,我们去探索block原理 探索block底层源码 block在底层是...

  • objective-C Block对象

    block对象简介及语法 什么是block? block对象是一组指令,可以像调用函数指令那样调用block对象。...

  • iOS __block和__weak的区别

    __block 1.__block对象在block中是可以被修改、重新赋值的。2.__block对象在block中...

  • iOS __block和__weak区别

    __block对象在block中可以被修改、重新赋值 __block对象在block中不会被block强引用一次,...

  • Block探索

    随着苹果越来越多的API开始提供block的写法,另外加上block在很多时候写起来确实很方便,因此block越来...

  • Block探索

    Block内存关系Block经典问题循环引用&解决Block底层分析Block底层HooK 程序占用内存分类 栈区...

  • Block探索

    一、什么是Block? Block是将函数及其执行上下文封装起来的对象。 例如: eg:clang -rewrit...

  • Block探索

    Block的分类 Block一共有6种类,常见的有三种。 上面这种block的打印结果为<__NSGlobalBl...

  • Block探索

    Block block 类型**1、全局block - **NSGlobalBlock**** **2、栈bloc...

网友评论

      本文标题:Block 对象探索

      本文链接:https://www.haomeiwen.com/subject/uimseftx.html