Objc Block 对于变量的访问

引子

作为ios初学者,看了许多关于介绍Block的文章,但是终究都是纸上谈兵,这次我们来实践以下,只为加强理解和记忆。

对于Block的理解可以参考以下文章:

Block编程值得注意的那些事儿(使用)

ios之Block研究 (Block内部数据结构)

谈Objective-C Block的实现 (Block内部数据结构)

对Objective-C中Block的追探 (Block内部数据结构,讲得很好)


例子

这里,定义一个类TestBlock,包含一个成员变量m_age,以及一个成员方法AccessVarInBlock

TestBlock.h与TestBlock.m的实现

//
//  TestBlock.m
//  BlockDemo
//
//  Created by fenglh on 14-8-18.
//  Copyright (c) 2014年 yons. All rights reserved.
//

#import "TestBlock.h"

@implementation TestBlock
@synthesize m_age;

//初始化
- (id) init:(int)age
{
    self = [super init];
    if (nil != self) {
        m_age = age;
    }
    return self;
}

- (void)AccessVarInBlock
{
    typedef void ( ^ MyBlock )(void);
    
    int outside_age=20;         //Block 外部的整形变量
    int *p_age = &outside_age;  //Block 外部的指针变量
    
    
    MyBlock aBlock = ^(){       //start Block
        NSLog(@" class member variable:\tm_age = %d", self.m_age);
        NSLog(@" outside variable: \t\toutside_age = %d", outside_age);
        NSLog(@" outside point variable:p_age = %d", *p_age);
    }; //end Block
    
    
    self.m_age = 100; //语句1
    *p_age = 100;     //语句2
    outside_age = 100;//语句3
    
    aBlock();
    
}

@end

main函数:

//
//  main.m
//  BlockDemo
//
//  Created by fenglh on 14-8-18.
//  Copyright (c) 2014年 yons. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "TestBlock.h"

int main(int argc, const char * argv[])
{
    TestBlock *test = [[TestBlock alloc ] init:10];
    [test AccessVarInBlock];
    
}

输出结果:

2014-08-18 23:40:35.334 BlockDemo[2136:303]  class member variable:	m_age = 100
2014-08-18 23:40:35.336 BlockDemo[2136:303]  outside variable: 		outside_age = 20
2014-08-18 23:40:35.337 BlockDemo[2136:303]  outside point variable:p_age = 100


先看,AccessVarInBlock方法,如果把代码1、2、3(即aBlock(); 上面三句)都注释掉的话,其输出结果为:

2014-08-18 23:35:06.513 BlockDemo[2111:303]  class member variable:	m_age = 10
2014-08-18 23:35:06.516 BlockDemo[2111:303]  outside variable: 		outside_age = 20
2014-08-18 23:35:06.517 BlockDemo[2111:303]  outside point variable:p_age = 20
可得出以下结论:

1. 指针变量和类成员变量,在Block的内部实现中,是一种引用而非拷贝。

2. 其他基本类型的变量,在Block的内部实现中,是一种拷贝。


简单探讨Block内部实现

1.先来看一段简单的代码

文件:block.c

include <stdio.h>

int main()
{
 int i = 10;
 int *p =&i;
 typedef void (^ typeBlock )(void);
    typeBlock aBlock = ^{
        printf("i = %d\n", i);
        printf("*p = %d", *p);
     } ;
    aBlock();
    return 0;
}


2.利用clang命令来编译一下:

你可以在终端下找到该工具所在的目录,命令“ sudo find / -name "clang" ”

编译使用clang -rewrite-objc block.c  命令,有时候需要设置包含文件



3.编译后会产生一个block.cpp文件,查看以下关键的代码:

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int i;// 定义一个整形变量(并非引用)
  int *p; //定义了一个指针变量,也就是一个引用
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _i, int *_p, int flags=0) : i(_i), p(_p) {//构造函数,赋值
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
可以发现:

1)原本的变量i,在block的内部重新定义了一个i并且在该结构体的构造函数进行赋值,也就是对i进行了一份拷贝。

2)指针变量p ,在block内部也有一个变量p,但是在构造函数的时候,它是指针地址的赋值,也就是说是一份引用。

3)可以猜测,类成员变量,在block的内部数据结构中也是类似一个指针的这样的实现!


4.其他知识点(暂做简单记录)

1)__block 修饰符

2)block的类型有三种:_NSConcreteStackBlock、_NSConcreteMallocBlock、_NSConcreteGlobalBlock

3)block的copy, copy了block后一定要release,防止内存泄露!

参考文章:对Objective-C中Block的追探,文章描述如下:

我在这里想探讨的另外一个问题是设计原则,对于一个对象,当外传的时候我们都会想着把对象autorelease掉,比如:

- (NSArray *) myTestArray {
    NSArray *array = [[NSArray alloc] initWithObjects: @"a", @"b", @"c", nil];
    return [array autorelease];
}

同样,我们在向外传递block的时候一定也要做到,传给外面一个在堆上的,autorelease的对象。

- (blk) myTestBlock {
    __blockint val = 10;
    blk stackBlock = ^{NSLog(@"val = %d", ++val);};
    return [[stackBlock copy] autorelease];
}

第一步,copy将block上从栈放到堆上,第二步,autorelease防止内存泄露。

同样,有时我们会去将block放到别的类中做回调,如放到AFNetworking中的回调。
这时根据统一的设计原则,我们也应该给调用对象一个堆上的autorelease的对象。

总之,在把block对象外传的时候,我们要传出一个经过copy,再autorelease的block在堆上的__NSMallocBlock__对象。(个人观点,block是模仿NSObject对象发明的,就不要让调用方做与其他对象不一样的事)



注:其他关于block内部各种数据结构的说明可以查看文章开始说的 参考文章!!如有错误,请指正,共同进步!







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值