设为首页 加入收藏

TOP

Objective-C中的Block(一)
2015-02-02 14:15:09 来源: 作者: 【 】 浏览:6
Tags:Objective-C Block

1.相关概念


在这篇笔记开始之前,我们需要对以下概念有所了解。


1.1 操作系统中的栈和堆


注:这里所说的堆和栈与数据结构中的堆和栈不是一回事。


我们先来看看一个由C/C++/OBJC编译的程序占用内存分布的结构:?


memory_structure.jpg


栈区(stack):由系统自动分配,一般存放函数参数值、局部变量的值等。由编译器自动创建与释放。其操作方式类似于数据结构中的栈,即后进先出、先进后出的原则。


例如:在函数中申明一个局部变量int b;系统自动在栈中为b开辟空间。


堆区(heap):一般由程序员申请并指明大小,最终也由程序员释放。如果程序员不释放,程序结束时可能会由OS回收。对于堆区的管理是采用链表式管理的,操作系统有一个记录空闲内存地址的链表,当接收到程序分配内存的申请时,操作系统就会遍历该链表,遍历到一个记录的内存地址大于申请内存的链表节点,并将该节点从该链表中删除,然后将该节点记录的内存地址分配给程序。


例如:在C中malloc函数?


但是p1本身是在栈中的。


链表:是一种常见的基础数据结构,一般分为单向链表、双向链表、循环链表。以下为单向链表的结构图:


linked_list.jpg


单向链表是链表中最简单的一种,它包含两个区域,一个信息域和一个指针域。信息域保存或显示关于节点的信息,指针域储存下一个节点的地址。


上述的空闲内存地址链表的信息域保存的就是空闲内存的地址。


全局区/静态区:顾名思义,全局变量和静态变量存储在这个区域。只不过初始化的全局变量和静态变量存储在一块,未初始化的全局变量和静态变量存储在一块。程序结束后由系统释放。


文字常量区:这个区域主要存储字符串常量。程序结束后由系统释放。


程序代码区:这个区域主要存放函数体的二进制代码。


下面举一个前辈写的例子:


原型声明:extern char *strcpy(char* dest, const char *src);


功能:把从src地址开始且含有NULL结束符的字符串复制到以dest开始的地址空间。


1.2 结构体(Struct)


C语言中,结构体(struct)指的是一种数据结构。结构体可以被声明为变量、指针或数组等,用以实现较复杂的数据结构。结构体同时也是一些元素的集合,这些元素称为结构体的成员(member),且这些成员可以为不同的类型,成员一般用名字访问。


我们来看看结构体的定义:


struct:结构体关键字。


tag:结构体标签。


member-list:结构体成员列表。


variable-list:为结构体声明的变量列表。


在一般情况下,tag,member-list,variable-list这三部分至少要出现两个。以下为示例:


以上就是简单结构体的代码示例。结构体的成员可以包含其他结构体,也可以包含指向自己结构体类型的指针。结构体的变量也可以是指针。


下面我们来看看结构体成员的访问。结构体成员依据结构体变量类型的不同,一般有2种访问方式,一种为直接访问,一种为间接访问。直接访问应用于普通的结构体变量,间接访问应用于指向结构体变量的指针。直接访问使用结构体变量名.成员名,间接访问使用(*结构体指针名).成员名或者使用结构体指针名->成员名。相同的成员名称依靠不同的变量前缀区分。


最后我们来看看结构体成员存储。在内存中,编译器按照成员列表顺序分别为每个结构体成员分配内存。如果想确认结构体占多少存储空间,则使用关键字sizeof,如果想得知结构体的某个特定成员在结构体的位置,则使用offsetof宏(定义于stddef.h)。


1.3 闭包(Closure)


闭包就是一个函数,或者一个指向函数的指针,加上这个函数执行的非局部变量。


说的通俗一点,就是闭包允许一个函数访问声明该函数运行上下文中的变量,甚至可以访问不同运行上文中的变量。


我们用脚本语言来看一下:


通过上面的代码我们可以看出,按常规思维来说,变量str是函数funB的局部变量,作用域只在函数funB中,函数funA是无法访问到str的。但是上述代码示例中函数funA中的callback可以访问到str,这是为什么呢,因为闭包性。


2.blcok基础知识


block实际上就是Objective-C语言对闭包的实现。


2.1 block的原型及定义


我们来看看block的原型:


上面的代码声明了一个block(^)原型,名字叫做myBlock,包含一个int型的参数,返回值为NSString类型的指针。


下面来看看block的定义:


上面的代码中,将一个函数体赋值给了myBlock变量,其接收一个名为paramA的参数,返回一个NSString对象。


注意:一定不要忘记block后面的分号。


定义好block后,就可以像使用标准函数一样使用它了:


由于block数据类型的语法会降低整个代码的阅读性,所以常使用typedef来定义block类型。例如,下面的代码创建了GetPersonEducationInfo和GetPersonFamilyInfo两个新类型,这样我们就可以在下面的方法中使用更加有语义的数据类型。


我们用一张大师文章里的图来总结一下block的结构:


block.png


2.2 将block作为参数传递


由于Objective-C是强制类型语言,所以作为函数参数的block也必须要指定返回值的类型,以及相关参数类型。


2.3 闭包性


上文说过,block实际是Objc对闭包的实现。


我们来看看下面代码:


上面的代码在main函数中声明了一个整型,并赋值42,另外还声明了一个block,该block会将42返回。然后将block传递给logBlock函数,该函数会显示出返回的值42??即使是在函数logBlock中执行block,而block又声明在main函数中,但是block仍然可以访问到x变量,并将这个值返回。


注意:block同样可以访问全局变量,即使是static。


2.4 block中变量的复制与修改


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


block-capture-1.jpg


通过block进行闭包的变量是const的。也就是说不能在block中直接修改这些变量。来看看当block试着增加x的值时,会发生什么:


编译器会报错,表明在block中变量x是只读的。


有时候确实需要在block中处理变量,怎么办?别着急,我们可以用__block关键字来声明变量,这样就可以在block中修改变量了。


基于之前的代码,给x变量添加__block关键字,如下:


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


block-capture-2.jpg


3.编译器中的block


3.1 block的数据结构定义


我们通过大师文章中的一张图来说明:


block-struct.jpg


上图这个结构是在栈中的结构,我们来看看对应的结构体定义:


从上面代码看出,Block_layout就是对block结构体的定义:


isa指针:指向表明该block类型的类。


flags:按bit位表示一些block的附加信息,比如判断block类型、判断block引用计数、判断block是否需要执行辅助函数等。


reserved:保留变量,我的理解是表示block内部的变量数。


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


descriptor:block的附加描述信息,比如保留变量数、block的大小、进行copy或dispose的辅助函数指针。


variables:因为block有闭包性,所以可

首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇UNIX内核用于所有I/O的数据结构 下一篇PHP代码优化24条真经

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: