[关闭]
@universal 2019-01-24T07:24:53.000000Z 字数 2429 阅读 225

iOS入门——Block学习

iOS


简介

block是在iOS 4.0之后才出现的,它可以将一段代码封装起来作为变量使用。
block声明

返回值 (^block名称) (参数列表...)
如: void (^blockName)(int arg1, int arg2)

block定义(实现)

blockname = ^(int arg1, int arg2){
//do something
}

如下:

  1. int (^addTwoNumber)(int a, int b) = ^(int a, int b) {
  2. return a + b;
  3. };

使用

block的作用类似java中接口回调,但仅仅是作用类似,原理上完全是两个东西。
举例:作为参数回调使用:

  1. //block无参数
  2. dispatch_async(dispatch_get_main_queue(), ^{
  3. NSLog(@"ui refresh");
  4. });
  5. //block带参数
  6. AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
  7. [manager GET:@"url" parameters:parameters progress:^(NSProgress *_Nonnull downloadProgress) {
  8. NSLog(@"downloadProgress-->%@", downloadProgress);
  9. } success:^(NSURLSessionDataTask *_Nonnull task, id _Nullable responseObject) {
  10. NSLog(@"success %@", responseObject);
  11. }
  12. } failure:^(NSURLSessionDataTask *_Nullable task, NSError *_Nonnull error) {
  13. NSLog(@"error %@", error);
  14. }];

block还能实现链式调用,返回一个block对象,并在block中返回自身对象。

  1. int main(int argc, const char * argv[])
  2. {
  3. @autoreleasepool {
  4. Person *p = [[Person alloc] init];
  5. p.study(@"xx宝典").run().study(@"xx技能");
  6. }
  7. return 0;
  8. }
  9. //Person.h
  10. @interface Person : NSObject
  11. - (Person *(^)(NSString *name))study;
  12. - (Person *(^)())run;
  13. @end
  14. //Person.m
  15. @implementation Person
  16. - (Person *(^)(NSString *))study
  17. {
  18. return ^(NSString *name){
  19. NSLog(@"study----%@", name);
  20. return self;
  21. };
  22. }
  23. - (Person *(^)())run
  24. {
  25. return ^{
  26. NSLog(@"run----");
  27. return self;
  28. };
  29. }
  30. @end

注意:
1、block的内存中有一块是专门来捕获变量的,在声明的block的范围内,所有变量都可以被其捕获。但是对外部变量只能读,如果想要修改,需要给变量加上__block关键字修饰。
2、避免循环引用, 使用__weak关键字来修饰需要引用的外部对象,防止导致内存泄漏。
3、可以利用typedef对block进行别称定义,减少代码量,增加可读性,将“对象抽象成类”。比如:typedef void(^SayHello)();

block深究

1、block是一个函数对象,是在运行时产生的,在一个作用域中生成的block对象分配在栈上,离开作用域,就不存在了(可以利用copy将block对象拷贝到堆上)。
每个对象都会有一个isa指针,那么根据isa指针类型,block有三种:
全局静态(globalBlock)/保存在程序的数据区域中(.data区)
出作用域销毁(stackBlock)/保存在栈中,
retainCount=0销毁(mallocBlock)/保存在堆中

2、block访问普通外部变量时,会将变量的值以“const方式”copy一份到block所在内存空间,所以block内部访问的并不是真正的外部变量,而且因为是const方式,所以编译器不允许修改copy的变量。

  1. typedef void (^CustomBlock)(void);
  2. // 外部变量
  3. int value = 0;
  4. NSLog(@"block外部访问:value = %d", value);
  5. NSLog(@"block外部访问:value的地址是:%p", &value);
  6. CustomBlock block = ^{
  7. NSLog(@"block内部访问:value = %d", value);
  8. NSLog(@"block内部访问:value的地址是:%p", &value);
  9. };
  10. block();
  11. NSLog(@"block回到外部访问:value的地址是:%p", &value);

输出:

block外部访问:value = 0
block外部访问:value的地址是:0x7ffeea11a0c4
block内部访问:value = 0
block内部访问:value的地址是:0x600003cc10a0
block回到外部访问:value的地址是:0x7ffeea11a0c4

还有,要注意一点:若将变量定义为__block形式,那么变量的地址将会在block结束后改为block中copy生成的新地址。
3、其实由上可以看出,在block中引用对象,会导致对象的生命周期被延长,特别是当某些大文件被block访问时,有几率会导致内存访问不足。
4、利用block做回调时,在避免循环引用的同时,还要防止引用的对象被提前释放,从而可能会因为操作nil对象导致crash,或者执行一些无意义的逻辑。所以需要我们在block内部操作时加上保护代码。(这种场景在网络访问的情景下较为常见)。

未完待续。。。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注