Block底层实现

《objective-c高级编程》这本书是一本非常好的书,但是时间比较早,书中有一些理论不适合最新的ios版本,并且书中论述ARC和MRC的情况比较混乱,穿插讲,但丝毫不会掩饰这本书的好。瑕不掩瑜

iOS代码块Block

概述

代码块Block是苹果在iOS4开始引入的对C语言的扩展,用来实现匿名函数的特性,Block是一种特殊的数据类型,其可以正常定义变量、作为参数、作为返回值,特殊地,Block还可以保存一段代码,在需要的时候调用,目前Block已经广泛应用于iOS开发中,常用于GCD、动画、排序及各类回调

Block变量的声明、赋值与调用

Block变量的声明

block和普通的变量一样可以作为属性,参数,返回值

作为属性时的声明如下:

形式为: 返回值类型 (^block名称) (参数声明);

int (^blockName) (int);
void (^blockName) (void);
void (^blockName) ();

typedef形式如下:

typedef int (^blk_t)();

blk_t blockName;

作为函数参数时的声明如下:

objective-c形式

- (id) fun: (int (^)())block
{
    int var = 10;
    NSArray *arry = [[NSArray alloc] initWithObjects:^{NSLog(@"%d",var);}, nil];
    return arry;
}

typedef形式如下:

typedef int (^blk_t)();

- (id) fun: (blk_t)block
{
    int var = 10;
    NSArray *arry = [[NSArray alloc] initWithObjects:^{NSLog(@"%d",var);}, nil];
    return arry;
}

c语言形式如下:

void func (int (^block)())
{
    
}

typedef int (^blk_t)();

void func (blk_t  block)
{
    
}
作为返回值类型时

objective-c形式

- (int (^)()) funoc
{
    return ^(int count){return count;};
}

typedef形式如下:

typedef int (^blk_t)();

- (blk_t) funoc
{
    return ^(int count){return count;};
}

c语言形式如下:

int (^fun())()
{
    return ^(int count){return count;};
}

typedef形式如下:

typedef int (^blk_t)();

blk_t fun()
{
    return ^(int count){return count;};
}

Block变量的定义

^ 返回值类型 (参数) {};
说明:返回值类型可以省略,(参数)可以省略,最简形式为^{};

//完整形式
    ^ int (int count){return count;};//完整形式
    
    //省略返回值
    ^  (int count){return count;};//返回值类型可以省略,默认与返回值类型相同
    
    //省略参数
    ^ {
    //方法体
    };//如果没有参数,可以省略参数
  
 //没有省略参数的形式
   ^ (void){
      //方法体
    };//参数为空可以省略参数

Block变量的调用

block的调用很简单,直接在block名称加括号,与函数调用一致

    int (^blockName)(int) = ^ int (int count){return count;};//完整形式
    NSLog(@"%d",blockName(10));
MRC

mrc下捕获自动变量的block会被存储在栈区,调用copy可以被复制到堆上

但是以下不需要主动复制

  • 调用copy
  • 传递给usingBlock方法
  • GCD函数的参数
ARC

arc下捕获自动变量的block会从栈区复制到堆区,这是编译器默认做的
但并不是说,ARC下不存在栈区的block;如果栈上的block没有赋值给

  • strong类型属性,
  • copy类型属性,
  • __strong类型的实例变量,
  • 作为函数返回值,
  • 传递给函数作为参数,
    那么block不会自动复制到堆上

截获自动变量

首先我们看下变量所有类型

  • 自动变量(局部变量)
  • 函数参数
  • 静态变量
  • 静态全局变量
  • 全局变量

block对自动变量的截获如下:

int var = 10;
void (^blockVar)() = ^{
    NSLog(@"%d",var);
};
blockVar();

但是不能对截获的自动变量修改

NSMutableArray *array = [NSMutableArray array];
void (^blockVar)() = ^{
    [array addObject:[[NSObject alloc] init]];//正确,因为没有对array赋值,只是向array添加数据
     array = [NSMutableArray array];//错误,改变了array的数值
};

以上代码中,没有对array的值修改,是可以的
但是不能访问c语言数组类型,即不能捕获c语言数组

    char c[10] = {'q','q'};
    void (^blockVar)() = ^{
        c[0];//编译报错
    };

但是c语言数组,我们可以通过指针的方式访问

char *q = @"ustb";
void (^blockVar)() = ^{
    q[0];//合法
};

截获自动变量是值拷贝,在由block编译生成的结构体中,声明与自动变量相对应的成员变量来存放截获的自动变量值,用于存放捕获的自动变量的值
MRC下编译器不会默认地把block拷贝到堆区,需要手动调用copy;ARC下,如果赋值给强指针会默认把block拷贝到堆区

截获对象

#import <UIKit/UIKit.h>
#import "AppDelegate.h"
typedef void (^blk_t)(id);
int main(int argc, char * argv[]) {
    @autoreleasepool {
        
        blk_t blk;
        {
            id arrayM = [NSMutableArray array];
            blk = ^(id obj){
                [arrayM addObject:obj];
                NSLog(@"%ld",[arrayM count]);
            };//arc环境下默认会拷贝到堆
        }
        
        blk([[NSObject alloc] init]);
        
        
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}
80A72DAA-A3EB-4939-98F8-7D2363273643.png

我们可以看到在堆上的block对截获的对象会有retain操作,并不会在对象作用域结束时被释放
我们再来看下在栈上的block会怎么样

#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import "TestObject.h"
extern int  _objc_rootRetainCount(id);
typedef void (^blk_t)(id);
int main(int argc, char * argv[]) {
    @autoreleasepool {

        __unsafe_unretained void (^block)(id);
        
        id array = [NSMutableArray array];
        block = ^(id obj){
            [array addObject:obj];//不会retain array
            NSLog(@"%@",array);
        };
        [array release];
        
        block([[NSObject alloc] init]);//EXC_BAD_ACCESS
        
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

我们看一下示例代码

#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import "TestObject.h"
extern int  _objc_rootRetainCount(id);
typedef void (^blk_t)(id);
int main(int argc, char * argv[]) {
    @autoreleasepool {

        __unsafe_unretained void (^block)(id);
        
        {
            id array = [NSMutableArray array];
            block = ^(id obj){
                [array addObject:obj];//MRC下EXC_BAD_ACCESS,ARC下array为nil
                NSLog(@"%@",array);
            };//无论MRC,ARC,block都在栈上
//            [array release];
        }
        
        block([[NSObject alloc] init]);//不管MRC,ARC都没有来的急释放,按理说超出作用域,栈上的对象会被回收的
        
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

可以看出栈上的block捕获的对象,相应的成员变量不会强引用被捕获的对象,但是当对象被释放的时候,如果是ARC环境,那么相应的成员变量会被负值nil;如果是MRC,不会负值nil,这时如果访问,会报EXC_BAD_ACCESS错误

截获对象且用__block修饰

__block修饰对象在mrc和ARC下不同,在ARC下会增加对象的引用计数,mrc不会增加引用计数,因此在mrc下可以用来防止引用计数

IMG_0262.JPG

ARC下防止引用循环有两种方式,__block,__weak

#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) void (^t)();
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
     __block  ViewController *strongSelf = self;
    _t = [^{
        NSLog(@"%@",Sself);
        strongSelf = nil;//付空,打破引用循环
    } copy];
}
- (void)dealloc
{
    NSLog(@"view dealloc!!!!!");
}
@end
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) void (^t)();
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    __weak  ViewController *weakSelf = self;
    _t = [^{
        NSLog(@"%@",weakSelf);
    } copy];
}
- (void)dealloc
{
    NSLog(@"view dealloc!!!!!");
}
@end

MRC下使用 __block 防止引用循环

#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, copy) void (^t)();
@end
@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
     __block  ViewController *WeakSelf = self;
    _t = [^{
        NSLog(@"%@",WeakSelf);//__block在MRC下的作用是防止循环引用,不会增加引用计数,这和ARC不同(这在官方文档Transitioning to ARC Release Notes有详细解释)
    } copy];
}
- (void)dealloc
{
    [super dealloc];
    [_t release];
    NSLog(@"view dealloc!!!!!");

}
@end

截获自动变量且用__block 修饰

__block修饰变量,会把变量作为成员变量放到结构体里,如果使用__block变量的block被拷贝到堆上,那么__block变量也会被拷贝到堆上

循环引用

对于循环引用的问题,mrc下通过__block解决,arc下通过__weak解决,或者在block执行完时手动置相应指针nil打破循环引用,__weak和__block不能同时使用

Block的实现

block的实质

进入项目目录,然后我们可以通过 clang -x objective-c -rewrite-objc -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk xxx.m指令将xxx.m文件编译成.cpp文件

//objective-c
#import <UIKit/UIKit.h>
#import "AppDelegate.h"

int main(int argc, char * argv[]) {
    @autoreleasepool {
        
        void (^blk)() = ^{printf("Block\n");};
        blk();
        
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

转换C++代码如下:

struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteStackBlock;//在栈上,但是打印出来是__NSGlobalBlock__,按理说应该就是应该在全局区的,这个地方编译的时候可能有误差
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
printf("Block\n");}

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main(int argc, char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 

        void (*blk)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
        ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);

        return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
    }
}

我们看到原先的代码块,编译之后,变成了结构体
命名规则是根据block所属函数(main函数)和block在函数中出现(此处0)的位置命名的

我们看到结构体中含有isa指针,说明这是一个objective-c对象
所以我们说block是objective-c对象

截获自动变量值的实质

#import <UIKit/UIKit.h>
#import "AppDelegate.h"

int main(int argc, char * argv[]) {
    @autoreleasepool {
        int var = 10;
        const char *fmt = "var = %d\n";
        void (^blk)() = ^{printf(fmt,var);};
        var = 2;
        fmt = "These values changed. var = %d\n";
        blk();
        
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}
struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  const char *fmt;
  int var;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, const char *_fmt, int _var, int flags=0) : fmt(_fmt), var(_var) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  const char *fmt = __cself->fmt; // bound by copy
  int var = __cself->var; // bound by copy
printf(fmt,var);}

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main(int argc, char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        int var = 10;
        const char *fmt = "var = %d\n";
        void (*blk)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, fmt, var));
        var = 2;
        fmt = "These values changed. var = %d\n";
        ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);

        return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
    }
}

我们看到捕获的自动变量被作为成员变量添加到__main_block_impl_0中啦,结构体如下

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  const char *fmt;
  int var;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, const char *_fmt, int _var, int flags=0) : fmt(_fmt), var(_var) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

在初始化结构体时,根据传递给构造函数的参数对由自动变量添加的成员变量初始化
我们可以看到,这仅仅是值传递,所以在block的外部修改自动变量不会影响block内的值,因此block也禁止在block内对相应的成员变量修改
截获自动变量的意思指:声明block语法时,block内使用的自动变量被保存在block内的结构体中

__block说明符

如果想在block中修改截获的自动变量的值,有两种方式,第一c语言有一个变量允许在block内写值,

  • 静态变量
  • 静态全局变量
  • 全局变量
    虽然block的匿名函数部分简单的转换成c语言的函数,但从这个变换的函数中访问静态变量,全局变量没有改变
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
static int intS;
int intE;
int main(int argc, char * argv[]) {
    @autoreleasepool {
        int var = 10;
        static int inlineInt = 10;
        const char *fmt = "var = %d\n";
        static NSString *str = @"ustb";
        void (^blk)() = ^{printf(fmt,var);
            intS = 10;
            intE = 10;
            inlineInt = 100;
            str = @"value changed ustb";
        };
        var = 2;
        fmt = "These values changed. var = %d\n";
        blk();
        
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

static int intS;
int intE;

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  const char *fmt;
  int var;
  int *inlineInt;
  NSString **str;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, const char *_fmt, int _var, int *_inlineInt, NSString **_str, int flags=0) : fmt(_fmt), var(_var), inlineInt(_inlineInt), 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) {
  const char *fmt = __cself->fmt; // bound by copy
  int var = __cself->var; // bound by copy
  int *inlineInt = __cself->inlineInt; // bound by copy
  NSString **str = __cself->str; // bound by copy
            printf(fmt,var);
            intS = 10;
            intE = 10;
            (*inlineInt) = 100;
            (*str) = (NSString *)&__NSConstantStringImpl__var_folders_wh_ycjfh9y54fbg8172sbbfcsbc0000gp_T_main_a2e925_mi_1;
        }
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, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->str, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static struct __main_block_desc_0 {
  size_t reserved;
  size_t 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 = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
int main(int argc, char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        int var = 10;
        static int inlineInt = 10;
        const char *fmt = "var = %d\n";
        static NSString *str = (NSString *)&__NSConstantStringImpl__var_folders_wh_ycjfh9y54fbg8172sbbfcsbc0000gp_T_main_a2e925_mi_0;
        void (*blk)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, fmt, var, &inlineInt, &str, 570425344));
        var = 2;
        fmt = "These values changed. var = %d\n";
        ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);

        return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
    }
}


我们可以看到全局变量和静态全局变量并没有加入到block结构体中,静态局部变量,会以指针的形式访问
个人理解:自动变量在超出作用域后会被释放,所以block内存放的是值拷贝,不可修改,但是全局变量和静态变量,并不会被销毁所以我们可以在block内公用一份复本,可以修改,特别的,静态全局变量超出作用域后无法直接访问,所以用block内添加指针成员变量,指向静态局部变量
第二使用__block修饰符
__block说明符像static,auto,register一样,用于说明变量存放在哪个地方

#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char * argv[]) {
    @autoreleasepool {
        __block int var = 10;
        void (^blk)() = ^{
            var = 1;
        };
        blk();
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}
struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};




struct __Block_byref_var_0 {
  void *__isa;
__Block_byref_var_0 *__forwarding;
 int __flags;
 int __size;
 int var;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_var_0 *var; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_var_0 *_var, int flags=0) : var(_var->__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_var_0 *var = __cself->var; // bound by ref

            (var->__forwarding->var) = 1;
        }
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->var, (void*)src->var, 8/*BLOCK_FIELD_IS_BYREF*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->var, 8/*BLOCK_FIELD_IS_BYREF*/);}

static struct __main_block_desc_0 {
  size_t reserved;
  size_t 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 = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
int main(int argc, char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        __attribute__((__blocks__(byref))) __Block_byref_var_0 var = {(void*)0,(__Block_byref_var_0 *)&var, 0, sizeof(__Block_byref_var_0), 10};
        void (*blk)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_var_0 *)&var, 570425344));
        ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
        return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
    }
}

我们可以看到多了结构

struct __Block_byref_var_0 {
  void *__isa;
__Block_byref_var_0 *__forwarding;
 int __flags;
 int __size;
 int var;
};

static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->var, (void*)src->var, 8/*BLOCK_FIELD_IS_BYREF*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->var, 8/*BLOCK_FIELD_IS_BYREF*/);}

这就是说__block修饰的变量作为成员变量被放在结构体里,我们称被__block修饰的变量叫__block变量

BLOCK存储域

block分三个存储区:

  • 全局区(_NSConcreteGlobalBlock)
  • 栈区(_NSConcreteStackBlock)
  • 堆区(_NSConcreteMallocBlock)

除了全局变量,静态变量,静态全局变量外没有捕获其他自动变量的block会被存放在全局区,结构体isa = &_NSConcreteGlobalBlock;

对栈上的对象retain是不管用的

#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import "TestObject.h"
extern int  _objc_rootRetainCount(id);
typedef void (^blk_t)(id);
int main(int argc, char * argv[]) {
   @autoreleasepool {

       int var = 10;
       void (^b)() = ^{
           printf("%d\n",var);
       };
       NSLog(@"%@",b);
       NSLog(@"%d",_objc_rootRetainCount(b));
       [b retain];
       NSLog(@"%@",b);
       NSLog(@"%d",_objc_rootRetainCount(b));
       [b release];
       NSLog(@"%@",b);
       NSLog(@"%d",_objc_rootRetainCount(b));
       b();
       
       return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
   }
}
D70F2667-B6D0-4855-8313-4C5A85173409.png

习题:

#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import "TestObject.h"
extern int  _objc_rootRetainCount(id);
typedef void (^blk_t)(id);
int main(int argc, char * argv[]) {
    @autoreleasepool {

        void (^block)(id);
        
        TestObject *obj = [[TestObject alloc] init];
        block = [^(id obj){
            
            NSLog(@"%@",obj);
        } copy];//TestObject类型的obj对象没有retain操作
        block(obj);
        [obj release];//调用完,obj被释放
        [block release];
        
        
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

对比:

#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import "TestObject.h"
extern int  _objc_rootRetainCount(id);
typedef void (^blk_t)(id);
int main(int argc, char * argv[]) {
    @autoreleasepool {

        void (^block)();
        
        TestObject *obj = [[TestObject alloc] init];
        block = [^{
            
            NSLog(@"%@",obj);
        } copy];//TestObject类型的obj对象有retain操作
        [obj release];//调用完,obj不会被释放
        [block release];//调用完,obj会被释放
        
        
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

修饰符问题:
ARC下:
void (^blk)();//默认强引用
__weak void (^blk)(); //blk编译为指向结构体的指针,类型为weak类型,修饰的是blk,block不会复制到堆上
__block void (^blk)();//blk会作为结构体成员变量
截获__block变量,其实截获blk所在结构体的指针,blk是strong类型

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,045评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,114评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,120评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,902评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,828评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,132评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,590评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,258评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,408评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,335评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,385评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,068评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,660评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,747评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,967评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,406评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,970评论 2 341

推荐阅读更多精彩内容