Block的好处,我总结了下主要有2点:1.用于回调特别方便,2.可以延长对象的作用区域。但是,Block的内存管理这个模块一直不是很清楚,这个周末好好的看了下Block的原理,有些许心得。

为了性能,默认Block都是分配在stack上面的,所以它的作用区域就是当前函数。

#include <stdio.h>

int main()
{
int i = ;
void (^blk)(void) = ^ {
printf("%d\n", i);
};
blk();
return ;
}

在blk这个block里面是不能修改i的。Why?我们可以通过clang看看编译器处理后的这块代码

struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int i;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _i, int flags=) : i(_i) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int i = __cself->i; // bound by copy printf("%d\n", i);
} static struct __main_block_desc_0 {
unsigned long reserved;
unsigned long Block_size;
} __main_block_desc_0_DATA = { , sizeof(struct __main_block_impl_0)};
int main()
{
int i = ;
void (*blk)(void) = (void (*)(void))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, i);
((void (*)(struct __block_impl *))((struct __block_impl *)blk)->FuncPtr)((struct __block_impl *)blk);
return ;
}
struct __block_impl是Block的一个内部结构体,原型是
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};

每个block都有个默认的构造函数

__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _i, int flags=0) : i(_i) 所以只能读取i,而不能修改i,当你试图修改它时,编译器就在预处理阶段直接报错。

只要在i前加__Block变量就可以在Block里面修改i值了,此时由值类型变为引用类型

struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int *i;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_i, int flags=) : i(_i) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int *i = __cself->i; // bound by copy printf("%d\n", (*i));
} static struct __main_block_desc_0 {
unsigned long reserved;
unsigned long Block_size;
} __main_block_desc_0_DATA = { , sizeof(struct __main_block_impl_0)};
int main()
{
static int i = ;
void (*blk)(void) = (void (*)(void))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &i);
((void (*)(struct __block_impl *))((struct __block_impl *)blk)->FuncPtr)((struct __block_impl *)blk);
return ;
}

上面的代码块是将int i的类型修改为__Block int i = 1024;后编译器生成代码块,可以看到__main_block_impl_0中的 i类型已经改变为int *,所以我们可以修改它的值。

所以只要没对Block进行copy操作,它一直存在stack里面。不管是否有__block修饰符

要想延长Block的作于域,我们可以对它进行copy操作,apple提供的接口是Block_Copy()方法

/* Copy, or bump refcount, of a block.  If really copying, call the copy helper if present. */
static void *_Block_copy_internal(const void *arg, const int flags) {
struct Block_layout *aBlock;
const bool wantsOne = (WANTS_ONE & flags) == WANTS_ONE; //printf("_Block_copy_internal(%p, %x)\n", arg, flags);
if (!arg) return NULL; // The following would be better done as a switch statement
aBlock = (struct Block_layout *)arg;
if (aBlock->flags & BLOCK_NEEDS_FREE) {
// latches on high
latching_incr_int(&aBlock->flags);
return aBlock;
}
else if (aBlock->flags & BLOCK_IS_GC) {
// GC refcounting is expensive so do most refcounting here.
if (wantsOne && ((latching_incr_int(&aBlock->flags) & BLOCK_REFCOUNT_MASK) == )) {
// Tell collector to hang on this - it will bump the GC refcount version
_Block_setHasRefcount(aBlock, true);
}
return aBlock;
}
else if (aBlock->flags & BLOCK_IS_GLOBAL) {
return aBlock;
} // Its a stack block. Make a copy.
if (!isGC) {
struct Block_layout *result = malloc(aBlock->descriptor->size);
if (!result) return (void *);
memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first
// reset refcount
result->flags &= ~(BLOCK_REFCOUNT_MASK); // XXX not needed
result->flags |= BLOCK_NEEDS_FREE | ;
result->isa = _NSConcreteMallocBlock;
if (result->flags & BLOCK_HAS_COPY_DISPOSE) {
//printf("calling block copy helper %p(%p, %p)...\n", aBlock->descriptor->copy, result, aBlock);
(*aBlock->descriptor->copy)(result, aBlock); // do fixup
}
return result;
}
else {
// Under GC want allocation with refcount 1 so we ask for "true" if wantsOne
// This allows the copy helper routines to make non-refcounted block copies under GC
unsigned long int flags = aBlock->flags;
bool hasCTOR = (flags & BLOCK_HAS_CTOR) != ;
struct Block_layout *result = _Block_allocator(aBlock->descriptor->size, wantsOne, hasCTOR);
if (!result) return (void *);
memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first
// reset refcount
// if we copy a malloc block to a GC block then we need to clear NEEDS_FREE.
flags &= ~(BLOCK_NEEDS_FREE|BLOCK_REFCOUNT_MASK); // XXX not needed
if (wantsOne)
flags |= BLOCK_IS_GC | ;
else
flags |= BLOCK_IS_GC;
result->flags = flags;
if (flags & BLOCK_HAS_COPY_DISPOSE) {
//printf("calling block copy helper...\n");
(*aBlock->descriptor->copy)(result, aBlock); // do fixup
}
if (hasCTOR) {
result->isa = _NSConcreteFinalizingBlock;
}
else {
result->isa = _NSConcreteAutoBlock;
}
return result;
}
}

通过观察apple提供的block源码,我们可以看到copy方法将block从statck拷贝到heap里面,所以它的作用区域延长了

待完成:1.block与oc的混合

    2.__block修饰符与oc的混合

总结

1.block默认都是分配在stack,当copy后,它移到heap里

2.block中的变量默认是不能修改的,只有添加__Block修饰符后才能修改

3.block中有oc对象时,会_Block_retain_object(object),直到block销毁后才会_Block_release_object(object);

4.对block进行copy时

  • If you access an instance variable by reference, a strong reference is made to self;

  • If you access an instance variable by value, a strong reference is made to the variable.

它会将self进行copy,此时改对象的dealloc方法不会执行(因为它的引用计数归0),解决此问题有2种方法:在block执行完成后面立即Block_Release(),或者将改变量声明为__Block类型(Why?)

继续补充block的oc的混合

//
// main.m
// block
//
// Created by lijian on 13-8-9.
// Copyright (c) 2013年 YOUKU. All rights reserved.
// #import <Foundation/Foundation.h> int main (int argc, const char * argv[]) { NSMutableString *str = [NSMutableString stringWithFormat:@"lijian"]; void (^blk)(void) = ^ {
NSLog(@"%@", str);
};
blk(); return ;
}

编译器生成代码为

//
// main.m
// block
//
// Created by lijian on 13-8-9.
// Copyright (c) 2013年 lijian. All rights reserved.
// #include <Foundation/Foundation.h> int main(int, const char **); struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
NSMutableString *str;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSMutableString *_str, int flags=) : str(_str) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
NSMutableString *str = __cself->str; // bound by copy NSLog((NSString *)&__NSConstantStringImpl_main_m_1, str);
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->str, (void*)src->str, /*BLOCK_FIELD_IS_OBJECT*/);} static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->str, /*BLOCK_FIELD_IS_OBJECT*/);} static struct __main_block_desc_0 {
unsigned long reserved;
unsigned long Block_size;
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { , sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
int main (int argc, const char * argv[]) { NSMutableString *str = ((id (*)(id, SEL, NSString *, ...))(void *)objc_msgSend)(objc_getClass("NSMutableString"), sel_registerName("stringWithFormat:"), (NSString *)&__NSConstantStringImpl_main_m_0); void (*blk)(void) = (void (*)(void))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, str, );
((void (*)(struct __block_impl *))((struct __block_impl *)blk)->FuncPtr)((struct __block_impl *)blk); return ;
}

blk的原型为

void (*blk)(void) = (void (*)(void))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, str, 570425344);

其中570425344 = BLOCK_HAS_COPY_DISPOSE | BLOCK_HAS_DESCRIPTOR;

通过构造函数,我们看到仍然是值传递,所以blk中是能不修改str的。

至于上面的__main_block_copy_0和__main_block_dispose_0 就是用于Block_Copy()和Block_Release();

当我将str的类型修改为__block NSMutableString时,生成如下代码

int main(int, const char **);
struct __Block_byref_str_0 {
void *__isa;
__Block_byref_str_0 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
NSMutableString *str;
};
static void __Block_byref_id_object_copy_131(void *dst, void *src) {
_Block_object_assign((char*)dst + , *(void * *) ((char*)src + ), );
}
static void __Block_byref_id_object_dispose_131(void *src) {
_Block_object_dispose(*(void * *) ((char*)src + ), );
} struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_str_0 *str; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_str_0 *_str, int flags=) : str(_str->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_str_0 *str = __cself->str; // bound by ref (str->__forwarding->str) = ((id (*)(id, SEL, NSString *, ...))(void *)objc_msgSend)(objc_getClass("NSMutableString"), sel_registerName("stringWithFormat:"), (NSString *)&__NSConstantStringImpl_main2_m_1);
NSLog((NSString *)&__NSConstantStringImpl_main2_m_2, (str->__forwarding->str));
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->str, (void*)src->str, /*BLOCK_FIELD_IS_BYREF*/);} static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->str, /*BLOCK_FIELD_IS_BYREF*/);} static struct __main_block_desc_0 {
unsigned long reserved;
unsigned long Block_size;
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { , sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
int main (int argc, const char * argv[]) { __block __Block_byref_str_0 str = {(void*),(__Block_byref_str_0 *)&str, , sizeof(__Block_byref_str_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, ((id (*)(id, SEL, NSString *, ...))(void *)objc_msgSend)(objc_getClass("NSMutableString"), sel_registerName("stringWithFormat:"), (NSString *)&__NSConstantStringImpl_main2_m_0)}; void (*blk)(void) = (void (*)(void))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (struct __Block_byref_str_0 *)&str, );
((void (*)(struct __block_impl *))((struct __block_impl *)blk)->FuncPtr)((struct __block_impl *)blk); return ;
}

可以看到str的类型实际上是__Block_byref_str_0,其中33554432 = BLOCK_HAS_COPY_DISPOSE = (1 << 25)

而blk的构造函数中传递的是__Block_byref_str_0类型的指针,所以我们能在blk中修改str

ios之Block研究的更多相关文章

  1. iOS中block的用法 以及和函数用法的区别

    ios中block的用法和函数的用法大致相同 但是block的用法的灵活性更高: 不带参数的block: void ^(MyBlock)() = ^{}; 调用的时候  MyBlock(); 带参数的 ...

  2. iOS开发--Block

    iOS开发--Block 1.什么是Block,block 的作用 ui开发和网络常见功能实现回调,按钮的事件处理方法是回调方法以及网络下载后的回调处理 (1)按钮 target-action   一 ...

  3. iOS之block

    1. Block的声明和线程安全Block属性的声明,首先需要用copy修饰符,因为只有copy后的Block才会在堆中,栈中的Block的生命周期是和栈绑定的,可以参考之前的文章(iOS: 非ARC ...

  4. iOS开发——Block详解

    iOS开发--Block详解 1. Block是什么 代码块 匿名函数 闭包--能够读取其他函数内部变量的函数 函数变量 实现基于指针和函数指针 实现回调的机制 Block是一个非常有特色的语法,它可 ...

  5. ios开发 block语句块

    ios开发 block语句块 1.block 理解为匿名函数 2.block变量的定义 //定义block变量,^表示定义block //技巧:函数名左右加括号,在函数名前面在加^ void (^bl ...

  6. iOS中Block介绍(一)基础

    ios开发block的使用指南,以及深入理解block的内存管理,也适用于osx开发.讨论范围:block的使用,内存管理,内部实现.不包含的内容:gc arc下的block内存,block在c++中 ...

  7. iOS中Block介绍 基础

    ios开发block的使用指南,以及深入理解block的内存管理,也适用于osx开发.讨论范围:block的使用,内存管理,内部实现.不包含的内容:gc arc下的block内存,block在c++中 ...

  8. iOS 中Block以及Blocks的使用,闭包方法调用

    OC: -(void)dataWithUrl:(NSString*)string AndId:(NSInteger)id returnName:(void(^)(NSString*name))back ...

  9. iOS中block类型大全

    iOS中block类型大全 typedef的block 作为属性的block 作为变量的block 作为方法变量入参的block 作为方法参数的block 无名block 内联函数的block 递归调 ...

随机推荐

  1. EasyPlayerPro Windows播放器全屏模式下GDI显示出现黑屏问题解决

    问题来源 2017.12.21 前天有杭州某教育领域客户反馈有部分视频源在全屏模式下显示黑屏: 问题复现 EasyPlayerPro由于没有实现单个窗口完全全屏,故没有暴露该问题,晚上加班,加上单个窗 ...

  2. 创建laravel项目

    下载项目到本地 git clone https://github.com/251068550/LaraBlog.git compoer安装 cd LaraBlog composer install 如 ...

  3. nvl()与regexp_replace()

    NVL(字段,0):将空值转化为0 regexp_replace(字段, ‘[1-9]‘ ,'') is not null; 将数字转化为0

  4. me12里更改信息记录的净价和有效价格,以及信息记录的条件价格

    转自 http://blog.csdn.net/zeewjj/article/details/7941525REPORT ztest. DATA:l_kbetr LIKE konp-kbetr.l_k ...

  5. Spring-data-redis:特性与实例(转载)

    原文地址:http://www.cnblogs.com/davidwang456/p/4915109.html Spring-data-redis为spring-data模块中对redis的支持部分, ...

  6. js的内部类

    JavaScript中本身提供一些,可以直接使用的类,这种类就是内部类.主要有: Object/Array/Math/Boolean/String/RegExp/Date/Number共8个内部类. ...

  7. javascript(5)

    (1)数组的细节: 基本用法 var 数组名=[元素值,元素值...]; 元素的值可以是任意类型. 数组是引用类型. js里的引用. 在函数参数列表中,如果传入的是基本类型,那 按值传递.如果传入的是 ...

  8. Win7 下安装MongoDB

    1).下载MongoDBhttp://downloads.mongodb.org/win32/mongodb-win32-i386-2.4.5.zip 下载Windows 32-bit版本并解压缩,程 ...

  9. html5--1.7超链接上

    html5--1.7超链接上 一.超链接的5种形式 <!DOCTYPE html> <html lang="en"> <head> <me ...

  10. DBA日记:一次reboot导致的严重失误

    昨天下午,一现场要添加RAC节点,db1节点正常运行,添加db2节点:在db2上做了安装的一些配置后,需要reboot, 于是直接就reboot:糟糕,这条命令错误地执行在db1上了,导致现场数据库直 ...