【引】objective-c,3:关于block

原文参考博文:

http://blog.devtang.com/2013/07/28/a-look-inside-blocks/

http://www.cnblogs.com/kesalin/archive/2013/04/30/ios_block.html

 

block 实际上可以理解成是Objective-C 语言对于闭包的实现。配合上 dispatch_queue,可以方便地实现简单的多线程编程和异步编程。

关于闭包,可以理解成是一个函数(或指向函数的指针),再加上该函数执行的外部的上下文变量。

 

这里介绍关于block的:

1,内部实现,数据结构介绍

2,block 的三种类型及其相关的内存管理方式

3,block 如何通过 capture 变量来达到访问函数外的变量

 

一个 block 实际是一个对象,它主要由一个 isa 和 一个 impl 和 一个 descriptor 组成。更具体的说来,结构如下:

对应的结构体定义如下,

struct Block_layout {
void *isa;
int flags;
int reserved;
void (*invoke)(void *, ...);
struct Block_descriptor *descriptor;
/* Imported variables. */
};
struct Block_descriptor {
unsigned long int reserved;
unsigned long int size;
void (*copy)(void *dst, void *src);
void (*dispose)(void *);
};

可以看出,主要包含6部分, 

1,isa指针,所有对象都有这个指针。指向对象的类结构。

2,invoke指针,函数指针,指向具体的block实现的函数调用地址

3,variables,block 能够访问它外部的局部变量,就是因为将这些变量(或变量的地址)复制到了结构体中。

4,reserved,保留字段。5,flags,descriptions,一些附加描述信息,比如size等。

 

OC中,一共三种类型的block,

1,_NSConcreteGlobalBlock 全局的静态 block,不会访问任何外部变量。

2,_NSConcreteStackBlock 保存在栈中的 block,当函数返回时会被销毁。

3,_NSConcreteMallocBlock 保存在堆中的 block,当引用计数为 0 时会被销毁。

 

block对外部变量的引用:

主要包含2类,

1,对于 block 外的变量引用,block 默认是将其复制到其数据结构中来实现访问的。

2,对于用 __block 修饰的外部变量引用,block 是复制其引用地址来实现访问的。

 

=======

block使用时的几个注意点:

 

1,block中的变量拷贝,是在实现时,对引用到的它所在方法中定义的栈变量进行一次只读拷贝

这个变量是只读的,比如

id result = nil;

[context performBlockAndWait:^{

result = block();

}];

会报错,variable is not assignable。

 

需要对其有写操作时,可以加__block。

 

 

 

2,非内联(inline)block 不能直接访问 self。

  这种情况,可以将self 当作参数传递到 block 中才能使用。而且这个时候self的属性,只能通过setter 或 getter方法访问,不能用点语法。

 

 

typedef NSString* (^IntToStringInlineConverter)(NSInteger paramInteger);

// 非内联block

IntToStringConverter independentBlockObject = ^(id self, NSInteger paramInteger) {
    KSLog(@" >> self %@, memberVariable %d", self, [self memberVariable]);
    NSString *result = [NSString stringWithFormat:@"%d", paramInteger];
    KSLog(@" >> independentBlockObject %@", result);
    return result;
};

// 内联block,用到self则不需要传self,也可以用点语法。 - (void)testAccessSelf { // Independent [self convertIntToString:20 usingBlockObject:independentBlockObject]; // Inline IntToStringInlineConverter inlineBlockObject = ^(NSInteger paramInteger) { KSLog(@" >> self %@, memberVariable %d", self, self.memberVariable); NSString *result = [NSString stringWithFormat:@"%d", paramInteger]; KSLog(@" >> inlineBlockObject %@", result); return result; }; [self convertIntToStringInline:20 usingBlockObject:inlineBlockObject]; }

 

3,使用 weak–strong避免循环引用

 

  内联block直接使用self时,需要小心,避免循环引用。

  block的本质其实还是一个对象。当self强持有这个block,而这个block又强持有self时,就会导致循环引用。

  换句话也就是说,只有释放了self才可以释放被self强持有的block。但self又被block强持有,无法释放。

 

  

  __weak KSViewController * wself = self;
    _observer = [[NSNotificationCenter defaultCenter]
                 addObserverForName:@"TestNotificationKey"
                 object:nil queue:nil usingBlock:^(NSNotification *n) {
                     KSViewController * sself = wself;
                     if (sself) {
                         NSLog(@"%@", sself);
                     }
                     else {
                         NSLog(@"<self> dealloc before we could run this code.");
                     }
                 }];

  weak-strong的作用是,

  外部传入一个self的弱引用。这样block对象并没有强持有self,当self被释放的时候,wself 变成 nil。这样已经可以解决循环引用的问题。

  但是为了在block运行过程中不至于出现wself被释放,所以一般会声明一个__strong的变量,持有wself。这样,在self被释放后,block可以本正常释放,sself也就被释放。

转载于:https://www.cnblogs.com/Pegasus-xy/p/6171121.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值