ios block

来源:互联网 发布:数控车床编程教学ppt 编辑:程序博客网 时间:2024/06/07 15:52

1.block的特点:

block是c语言;

block是一种数据类型,可当做参数使用,也可以用作返回值;总之,对比int的用法用即可【定义的时候,最好跟函数对比】;

block是预先准备好的代码块,在需要的时候调用;

2、定义block

有返回值、有参数:返回类型^(blockName)(参数)=^返回类型(参数列表){//代码实现}

无返回值、有参数:void^(blockName)(参数)=^(参数列表){//代码实现}

无返回值、无参数void^(blockName)()=^{//代码实现}

速记代码块:InlineBlock,编译器会提示:

returnType(^blockName)(parameterTypes)=^(parameters){

statementes

 };

3、block引用外部变量

定义block时,如果使用了外部变量,block内部会默认对外部变量做一次copy;

默认情况下,不允许在block内部修改外部变量的值

在外部变量声明时,使用__block修饰符,则可以在block内部修改外部变量的值;

4、数组的遍历和排序;

遍历:enmuerateObjectsUsingBlock:  方法,该方法的所有参数都应准备到位,可以直接使用,效率比for高;

  举例:懒加载
               enumerateObjectsUsingBlock遍历:
               [tempArray enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL*_Nonnull stop) {
           NSDictionary *dict = (NSDictionary*)obj;
           
 Heros *hero = [HerosherosWithDict:dict];
            [ArrMaddObject:hero]; 
        }];
        for—IN遍历: 
       for (NSDictionary*dict in tempArray) {
           
 Heros *heros = [HerosherosWithDict:dict];
            [ArrM
 addObject:heros];
        }

      排序:sortedArrayUsingComparator:

5、block的数据的逆向传值

被调用方:准备块代码;

调用方:定义代码属性,在适当的时候调用block;

举例说明:
调用方:定义块代码属性
[objc] view plain copy
  1. #import <Foundation/Foundation.h>  
  2.   #import <UIKit/UIKit.h>  
  3.   
  4.   @class YSCNSOperationOP;  
  5.   
  6.   typedef void(^setUpUIImage)(YSCNSOperationOP *);  
  7.   
  8.   @interface YSCNSOperationOP : NSOperation  
  9.   
  10.   @property (nonatomiccopyNSString *urlString;  
  11.   @property (nonatomicstrongUIImage *image;  
  12.   
  13.   @property (nonatomiccopy) setUpUIImage myBlock;  
  14.   - (void)setUpUIImage:(setUpUIImage )block;  
  15.   
  16.   @end  

在适当的时候执行:

  1. #import "YSCNSOperationOP.h"  
  2.    @implementation YSCNSOperationOP  
  3.    - (void)main {  
  4.        @autoreleasepool {  
  5.            UIImage *image  = [self downLoadImage:self.urlString];  
  6.            self.image = image;  
  7.            dispatch_async(dispatch_get_main_queue(), ^{  
  8.                self.myBlock(self);  
  9.            });  
  10.        }  
  11.    }  
  12.    - (UIImage *)downLoadImage:(NSString *)urlString{  
  13.   
  14.        NSURL *url = [NSURL URLWithString:urlString];  
  15.        NSData *data = [NSData dataWithContentsOfURL:url];  
  16.        UIImage *image = [UIImage imageWithData:data];  
  17.        return image;  
  18.    }  
  19.    - (void)setUpUIImage:(setUpUIImage )block {  
  20.        if (block) {  
  21.            self.myBlock = block;  
  22.        }  
  23.    }  
  24.    @end  
被调用方:准备代码块

  1. #import "ViewController.h"  
  2. #import "YSCNSOperationOP.h"  
  3. @interface ViewController ()  
  4. @property (weak, nonatomic) IBOutlet UIImageView *iamgeView;  
  5. @end  
  6. @implementation ViewController  
  7. - (void)viewDidLoad {  
  8.     [super viewDidLoad];  
  9.     // Do any additional setup after loading the view, typically from a nib.  
  10. }  
  11. - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {  
  12.     YSCNSOperationOP *yscOp = [[YSCNSOperationOP alloc] init];  
  13.     yscOp.urlString = @"http://h.hiphotos.baidu.com/image/pic/item/9825bc315c6034a8094ace24c9134954082376ee.jpg";  
  14.     [yscOp setUpUIImage:^(YSCNSOperationOP *op) {  
  15.         self.iamgeView.image = op.image ;  
  16.     }];  
  17.     NSOperationQueue *queue = [[NSOperationQueue alloc] init];  
  18.     [queue addOperation:yscOp];  
  19. }  
  20. @end  
6、block的底层实现原理

block是一个指针结构体,在终端下通过clang-rewrite-objc指令看看c++代码;

创建一个OSX工程,写一个最简单的block;

#import <Foundation/Foundation.h>int main(int argc, const char * argv[]) {    @autoreleasepool {        void (^myblock)() = ^() {            NSLog(@"hello block");        };        myblock();    }    return 0;}
利用终端编译成c++代码:clang-rewrite-objc  main.m

几个重要的结构体和函数简介:

  • __block_impl:这是一个结构体,也是C面向对象的体现,可以理解为block的基类;
  • __main_block_impl_0: 可以理解为block变量;
  • __main_block_func_0: 可以理解为匿名函数;
  • __main_block_desc_0:block的描述, Block_size;
1、__block_implstruct __block_impl {  void *isa;  int Flags;  int Reserved;  void *FuncPtr;};2、__main_block_impl_0struct __main_block_impl_0 {  struct __block_impl impl;  struct __main_block_desc_0* Desc;  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {    impl.isa = &_NSConcreteStackBlock;    impl.Flags = flags;    impl.FuncPtr = fp;    Desc = desc;  }};3、__main_block_func_0static void __main_block_func_0(struct __main_block_impl_0 *__cself) {            NSLog((NSString *)&__NSConstantStringImpl__var_folders_gc_5fkhcz0n6px48vzc744hmp6c0000gn_T_main_eef954_mi_0);        }4、 __main_block_desc_0staticstruct __main_block_desc_0 {  size_t reserved;  size_t Block_size;} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};int main(int argc, const char * argv[]) {    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;        void (*myblock)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));        ((void (*)(__block_impl *))((__block_impl *)myblock)->FuncPtr)((__block_impl *)myblock);    }    return 0;}
注意事项:block容易造成循环引用,在block里面如果使用self,然后形成强引用时,需要打断循环引用;在MRC下用_block,在ARC下使用_weak;

7、关于block在内存中的位置:

block块的存储位置(block入口的地址)可能存放在3个地方:代码区(全局区)、堆区、栈区(ARC情况下会自动拷贝到堆区,因此ARC下只有两个地方:代码区和堆区);ARC下也会有栈块的存在,只有把栈块赋值给strong对象或者block类型变量的时候才会触发_Block_copy函数,即[block copy],此时的栈块才会变成堆块。

代码区:不访问除去栈区的变量(如局部变量),且不访问堆区的变量(如用alloc创建对象)时,此时block存放在代码区;

堆区:如果访问了处于栈区的变量(如局部变量)或堆区的变量(如用alloc创建的对象),此时block存放在堆区;  ***需要注意

补充:

1)block内容实际是放在栈区,在ARC情况下,会自动拷贝到堆区;如果不是ARC,则存放在栈区。所以,在函数执行完毕就会释放,想在外面调用需要用copy指向他,这样就拷贝到了堆区;strong属性不会拷贝,会造成野指针错误。(需要理解ARC是一种编译器特性,即编译器在编译时在合适的地方retain、release、autorelease,不是iOS的运行时特性);

2)此外代码存放在堆区时,需要注意!!因为堆区不像代码区不变化,堆区是动态的(不断的创建销毁)。当没有强指针指向的时候就会被销毁,如果再去访问这段代码时,程序就会奔溃!所以此种情况在定义block属性时2需要制定为strong or  copy。block是一段代码,是不可变的,所以使用copy也不会深拷贝;


简单记忆:

  • Block如果没有引用外部变量
    保存在全局区(MRC/ARC一样)
  • Block如果引用外部变量
    ARC保存在 堆区; MRC保存在 栈区必须用copy修饰block;
补充:

1.被block捕获的外界变量不能被随意修改,需要使用__block修饰变量才能达到修改的目的。
2.__NSGlobalBlock__ 从程序启动一直存在于全局区,且并不像__NSMallocBlock__会强引用捕获的变量
3.__MallocBlock造成的强引用循环可以通过 置nil 破解