Block详解

block的本质

先看block的简单实现

 int age = 20;
 void (^block)(int) =  ^(int a){
      NSLog(@"this is a block! -- %d", age);
 };
 block(10);

转为C++代码

struct __main_block_desc_0 {
    size_t reserved;
*********************************
block结构体占用的内存大小
*********************************
    size_t Block_size; 
};

struct __block_impl {
    void *isa; isa指针
    int Flags;
    int Reserved;
*********************************
block内执行的代码会封装到一个函数中,FuncPtr存放函数的内存地址
*********************************
    void *FuncPtr; 
};

struct __main_block_impl_0 {
    struct __block_impl impl;
*********************************
block的描述
*********************************
  struct __main_block_desc_0* Desc; 
*********************************
持有外部变量 age
*********************************
    int age; 
};


struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int age;
*********************************
构造函数(类似OC的init)返回结构体对象
*********************************
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age, int flags=0) : age(_age) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

*********************************
封装了block执行逻辑的函数
*********************************
static void __main_block_func_0(struct __main_block_impl_0 *__cself, int a) {
  int age = __cself->age; // bound by copy

            NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r_gswfk35n5938fbdhf6s4xw_c0000gp_T_main_22e326_mi_0, age);

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)};

查看Block的继承关系

*********************************
 __NSGlobalBlock__ : __NSGlobalBlock : NSBlock : NSObject
*********************************
 void (^block)(void) = ^{
              NSLog(@"Hello");
          };
 NSLog(@"%@", [block class]);
 NSLog(@"%@", [[block class] superclass]);
 NSLog(@"%@", [[[block class] superclass] superclass]);
 NSLog(@"%@", [[[[block class] superclass] superclass] superclass]);
__NSGlobalBlock__
__NSGlobalBlock
NSBlock
NSObject

结论:

block本质上也是一个OC对象,它内部也有个isa指针
block是封装了函数调用以及函数调用环境的OC对象

block的变量捕获(capture)

为了保证block内部能够正常访问外部的变量,block有个变量捕获机制


验证:

void (^block)(void);

int weight_ = 10;
static int staticWeight_ = 10;

void test()
{
*********************************
    自动变量,离开作用域就销毁, auto默认自带,可以不写
*********************************
    auto int age = 10;
    static int height = 10;
    
    block = ^{
        // age的值捕获进来(capture)
        NSLog(@"age is %d, height is %d, weight_ is %d,staticWeight_ is %d", age, height,weight_,staticWeight_);
    };
    age = 20;
    height = 20;
    weight_ = 20;
    staticWeight_ = 20;
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        test();
        block();
        return 0;
    }
}

打印结果

age is 10, height is 20, weight_ is 20,staticWeight_ is 20

转为C++源码如下

void (*block)(void);

int weight_ = 10;
static int staticWeight_ = 10;
*********************************
__test_block_impl_0结构体中并没有捕获全局变量
*********************************
struct __test_block_impl_0 {
  struct __block_impl impl;
  struct __test_block_desc_0* Desc;
  int age;
  int *height;
  __test_block_impl_0(void *fp, struct __test_block_desc_0 *desc, int _age, int *_height, int flags=0) : age(_age), height(_height) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __test_block_func_0(struct __test_block_impl_0 *__cself) {
  int age = __cself->age; // bound by copy
  int *height = __cself->height; // bound by copy

*********************************
age是值传递
*height 是指针传递,存储的是外部变量的地址
weight_和staticWeight_参数直接调用全局变量,并没有捕获
*********************************
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r_gswfk35n5938fbdhf6s4xw_c0000gp_T_main_a117a3_mi_0, age, (*height),weight_,staticWeight_);
    }

static struct __test_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __test_block_desc_0_DATA = { 0, sizeof(struct __test_block_impl_0)};


int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        test();
        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
        return 0;
    }
}

思考:在block里面访问self呢?

@interface Person : NSObject
@property (copy, nonatomic) NSString *name;

- (void)test;

@end

#import "Person.h"

@implementation Person

- (void)test
{
    void (^block)(void) = ^{
        NSLog(@"%@---%@----%@",self, self->_name, [self name]);
    };
    block();
}

@end

转为C++代码如下

*********************************
block捕获了self
*********************************
struct __Person__test_block_impl_0 {
  struct __block_impl impl;
  struct __Person__test_block_desc_0* Desc;
  Person *self;
  __Person__test_block_impl_0(void *fp, struct __Person__test_block_desc_0 *desc, Person *_self, int flags=0) : self(_self) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __Person__test_block_func_0(struct __Person__test_block_impl_0 *__cself) {
  Person *self = __cself->self; // bound by copy
*********************************
block通过捕获的self调用self的成员变量和方法
*********************************
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r_gswfk35n5938fbdhf6s4xw_c0000gp_T_Person_606d56_mi_0,self, (*(NSString **)((char *)self + OBJC_IVAR_$_Person$_name)), ((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("name")));
    }
static void __Person__test_block_copy_0(struct __Person__test_block_impl_0*dst, struct __Person__test_block_impl_0*src) {_Block_object_assign((void*)&dst->self, (void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static void __Person__test_block_dispose_0(struct __Person__test_block_impl_0*src) {_Block_object_dispose((void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static struct __Person__test_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __Person__test_block_impl_0*, struct __Person__test_block_impl_0*);
  void (*dispose)(struct __Person__test_block_impl_0*);
} __Person__test_block_desc_0_DATA = { 0, sizeof(struct __Person__test_block_impl_0), __Person__test_block_copy_0, __Person__test_block_dispose_0};
*********************************
OC转C默认传递两个参数self 和 SEL _cmd
参数就是局部变量,所以block会捕获self
*********************************
static void _I_Person_test(Person * self, SEL _cmd) {
    void (*block)(void) = ((void (*)())&__Person__test_block_impl_0((void *)__Person__test_block_func_0, &__Person__test_block_desc_0_DATA, self, 570425344));
    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}

block的类型

block有3种类型,可以通过调用class方法或者isa指针查看具体类型,最终都是继承自NSBlock类型
NSGlobalBlock ( _NSConcreteGlobalBlock )
NSStackBlock ( _NSConcreteStackBlock )
NSMallocBlock ( _NSConcreteMallocBlock )



每一种类型的block调用copy后的结果如下所示

验证:

将工程置于MRC下(在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上)


void test()
{
    // Global:没有访问auto变量
    void (^block1)(void) = ^{
    };
    NSLog(@"没有访问auto变量block类型---------%@",block1);
    NSLog(@"copy后---------%@",[block1 copy]);
    // Stack:访问了auto变量
    int age = 10;
    void (^block2)(void) = ^{
        NSLog(@"age---------%d", age);
    };
    NSLog(@"访问auto变量block类型---------%@", block2);
    NSLog(@"copy后-%@", [block2 copy]);
}

调用

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        test();
    }
    return 0;
}

结果如下:

没有访问auto变量block类型---------<__NSGlobalBlock__: 0x1000020a8>
copy后---------<__NSGlobalBlock__: 0x1000020a8>

访问auto变量block类型---------<__NSStackBlock__: 0x7ffeefbff3e0>
copy后-<__NSMallocBlock__: 0x10051b360>

block的copy

在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上,比如以下情况

  • block作为函数返回值时
typedef void (^Block)(void);

Block myblock()
{
    int age = 10;
    return ^{
        NSLog(@"---------%d",age);
    };
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Block block = myblock();
        block();
        NSLog(@"%@", [block class]);
    }
    return 0;
}
---------10
__NSMallocBlock__
  • 将block赋值给__strong指针时
typedef void (^Block)(void);

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        int age = 10;
        Block block = ^{
            NSLog(@"---------%d", age);
        };
        NSLog(@"%@", [block class]);
    }
    return 0;
}
__NSMallocBlock__
  • block作为Cocoa API中方法名含有usingBlock的方法参数时
NSArray *arr = @[];
[arr sortedArrayUsingComparator:^NSComparisonResult(id  _Nonnull obj1, id  _Nonnull obj2) {
        }];
  • block作为GCD API的方法参数时
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        });

block作为属性变量的写法

MRC下block属性的建议写法

@property (copy, nonatomic) void (^block)(void);

ARC下block属性的建议写法

@property (strong, nonatomic) void (^block)(void);
@property (copy, nonatomic) void (^block)(void);

对象类型的auto变量

  • 当block内部访问了对象类型的auto变量时
    如果block是在栈上,将不会对auto变量产生强引用

验证:(MRC环境下)

#import <Foundation/Foundation.h>

@interface Person : NSObject
@property (assign, nonatomic) int age;
@end

#import "Person.h"

@implementation Person

- (void)dealloc
{
    [super dealloc];
    NSLog(@"Person - dealloc");
}

@end

调用auto修辞的person对象,并设置断点

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

typedef void (^Block)(void);

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Block block;
        {
            Person *person = [[Person alloc] init];
            person.age = 10;
            block = ^{
                NSLog(@"---------%d", person.age);
            };
            NSLog(@"%@",[block class]);
            [person release];
        }
      *******************************
        断点处
      *******************************
        NSLog(@"------");
    }
    return 0;
}

打印如下

__NSStackBlock__
Person - dealloc

可以看到block未释放时,person对象已经被提前释放,说明block并没有对person产生强引用

  • 如果block被拷贝到堆上
  1. 会调用block内部的copy函数
  2. copy函数内部会调用_Block_object_assign函数
  3. _Block_object_assign函数会根据auto变量的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用

验证:(ARC环境下)

#import <Foundation/Foundation.h>

@interface Person : NSObject
@property (assign, nonatomic) int age;
@end

#import "Person.h"

@implementation Person

- (void)dealloc
{
//    [super dealloc];
    NSLog(@"Person - dealloc");
}

@end

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

typedef void (^Block)(void);

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Block block;
        {
            Person *person = [[Person alloc] init];
            person.age = 10;
//            __weak Person *weakPerson = person;
            block = ^{
                NSLog(@"---------%d", person.age);
            };
            NSLog(@"%@",[block class]);
        }
      *******************************
        断点处
      *******************************
        NSLog(@"------");
    }
    return 0;
}

打印如下

__NSMallocBlock__

可以看到block未释放时,person对象也没有释放

源码解读:

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
*********************************
如果auto对象没有用_weak修辞,默认就是__strong
*********************************
  Person *__strong person;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, Person *__strong _person, int flags=0) : person(_person) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
*********************************
封装了block执行逻辑的函数
*********************************
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  Person *__strong person = __cself->person; // bound by copy

                NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r_gswfk35n5938fbdhf6s4xw_c0000gp_T_main_07fbbe_mi_0, ((int (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("age")));
            }
*********************************
根据auto对象类型变量的修饰符(__strong、__weak、__unsafe_unretained)
做出相应的操作,形成强引用(retain)或者弱引用
*********************************
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->person, (void*)src->person, 3/*BLOCK_FIELD_IS_OBJECT*/);}
*********************************
释放引用的auto对象类型变量
*********************************
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->person, 3/*BLOCK_FIELD_IS_OBJECT*/);}
*********************************
block的描述,一旦访问的是对象类型,就会多了__main_block_copy_0,
和 __main_block_dispose_0函数,对对象类型变量的引用和释放
*********************************
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};

思考,如果用

 __weak Person *weakPerson = person;

呢?
结果就是

__NSMallocBlock__
Person - dealloc
  • 如果block从堆上移除
  1. 会调用block内部的dispose函数
  2. dispose函数内部会调用_Block_object_dispose函数
  3. _Block_object_dispose函数会自动释放引用的auto变量(release)
#import <Foundation/Foundation.h>
#import "Person.h"

typedef void (^Block)(void);

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Block block;
        {
            Person *person = [[Person alloc] init];
            person.age = 10;
            
           // __weak Person *weakPerson = person;
            block = ^{
                NSLog(@"---------%d", person.age);
            };
            NSLog(@"%@",[block class]);
        }
        NSLog(@"------");
    }
    *******************************
        断点处
    *******************************
    return 0;
}

结果如下:

__NSMallocBlock__
------
Person - dealloc

源码解读在上面第2步

__weak问题解决

在使用clang转换OC为C++代码时,可能会遇到以下问题

cannot create __weak reference in file using manual reference

解决方案:支持ARC、指定运行时系统版本,比如

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m

__block修饰符

__block可以用于解决block内部无法修改auto变量值的问题
__block不能修饰全局变量、静态变量(static

typedef void (^Block)(void);

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        __block int age = 10;
        Block block = ^{
            age = 20;
            NSLog(@"age is %d", age);
        };
        block();
    }
    return 0;
}

转为底层C++代码

***************************
编译器把__block变量age包装成一个对象,转变C++ 就是__Block_byref_age_0结构体
__forwarding指针指向__Block_byref_age_0结构体
***************************
struct __Block_byref_age_0 {
  void *__isa;
__Block_byref_age_0 *__forwarding;
 int __flags;
 int __size;
***************************
捕获的age的内存地址和外部变量age的内存地址是一样的
***************************
 int age;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
***************************
block内部持有的 __Block_byref_age_0 类型的age指针指向上面的结构体
这里是强引用关系
***************************
  __Block_byref_age_0 *age; // by ref
  ...
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_age_0 *age = __cself->age; // bound by ref
***************************
block内部修改age变量的值会先找到__Block_byref_age_0结构体
->__forwarding指针->age
***************************
            (age->__forwarding->age) = 20;
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r_gswfk35n5938fbdhf6s4xw_c0000gp_T_main_3520b8_mi_0, (age->__forwarding->age));
        }


int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        __attribute__((__blocks__(byref))) __Block_byref_age_0 age = {
                                                          (void*)0,
                                                          ***************************
                                                          对应的是__Block_byref_age_0 *__forwarding;
                                                          说明*_forwarding接收的是age的内存地址
                                                          ***************************
                                                          (__Block_byref_age_0 *)&age, 
                                                          0, 
                                                          sizeof(__Block_byref_age_0), 
                                                          10
                                                          };
       ...
    }
    return 0;
}

编译器会将__block变量包装成一个对象

__block的内存管理

  • 当block在栈上时,并不会对__block变量产生强引用

  • 当block被copy到堆时

  1. 会调用block内部的copy函数
  2. copy函数内部会调用_Block_object_assign函数
  3. _Block_object_assign函数会对__block变量形成强引用(retain)
  • 当block从堆中移除时
  1. 会调用block内部的dispose函数
  2. dispose函数内部会调用_Block_object_dispose函数
  3. _Block_object_dispose函数会自动释放引用的__block变量(release)

__block的__forwarding指针

对象类型的auto变量、__block变量

  • 当block在栈上时,对它们都不会产生强引用

  • 当block拷贝到堆上时,都会通过copy函数来处理它们
    __block变量(假设变量名叫做a)

_Block_object_assign((void*)&dst->a, (void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);

对象类型的auto变量(假设变量名叫做p)

_Block_object_assign((void*)&dst->p, (void*)src->p, 3/*BLOCK_FIELD_IS_OBJECT*/);
  • 当block从堆上移除时,都会通过dispose函数来释放它们
    __block变量(假设变量名叫做a)
_Block_object_dispose((void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);

对象类型的auto变量(假设变量名叫做p)

_Block_object_dispose((void*)src->p, 3/*BLOCK_FIELD_IS_OBJECT*/);

__block修饰的对象类型

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

typedef void (^Block) (void);

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        __block Person *person = [[Person alloc] init];
        Block block = ^{
            NSLog(@"%p", person);
        };
        block();
    }
    return 0;
}

转为C++底层源码

*************************
__Block修辞的person对象转成C++变成了__Block_byref_person_0结构体
*************************
struct __Block_byref_person_0 {
  void *__isa; 指针占用8个字节内存空间
__Block_byref_person_0 *__forwarding; 8
 int __flags; 4
 int __size; 4
*************************
 __Block_byref_person_0结构体中持有person对象
 copy和dispose函数是对person对象的引用和释放
*************************
 void (*__Block_byref_id_object_copy)(void*, void*); 8
 void (*__Block_byref_id_object_dispose)(void*); 8
*************************
__Block_byref_person_0结构体内存地址+40就是person的内存地址
在MRC情况下不会对person强引用
*************************
 Person *person;
};

static void __Block_byref_id_object_copy_131(void *dst, void *src) {
 _Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131);
}
static void __Block_byref_id_object_dispose_131(void *src) {
 _Block_object_dispose(*(void * *) ((char*)src + 40), 131);
}

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_person_0 *person; // by ref
...
};

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

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

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
*************************
对__Block_byref_person_0 *person的内存管理操作
*************************
  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};

结论:

  • 当__block变量在栈上时,不会对指向的对象产生强引用

  • 当__block变量被copy到堆时

  1. 会调用__block变量内部的copy函数
  2. copy函数内部会调用_Block_object_assign函数
  3. _Block_object_assign函数会根据所指向对象的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用(注意:这里仅限于ARC时会retain,MRC时不会retain
  • 如果__block变量从堆上移除
  1. 会调用__block变量内部的dispose函数
  2. dispose函数内部会调用_Block_object_dispose函数
  3. _Block_object_dispose函数会自动释放指向的对象(release)

循环引用问题

#import <Foundation/Foundation.h>

typedef void (^Block) (void);

@interface Person : NSObject
@property (copy, nonatomic) Block block;

- (void)test;

@end

#import "Person.h"

@implementation Person

- (void)dealloc
{
********************
MRC打开 //[super dealloc];
********************
    NSLog(@"%s", __func__);
}

- (void)test
{
    self.block = ^{
        NSLog(@"%@", self);
    };
}
@end
#import <Foundation/Foundation.h>
#import "Person.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *person = [[Person alloc] init];
        [person test];
    }
**********************
设置断点
**********************
    NSLog(@"*****");
    return 0;
}

可以看到在block函数执行完毕后,dealloc函数并没有执行,person对象没有释放

关于block持有self的问题,上面从C++源码角度已经解读就不在说了

解决循环引用问题 - ARC

  • 用__weak、__unsafe_unretained解决
__weak typeof(self) weakSelf = self;
self.block = ^{
    NSLog(@"%@", weakSelf);
};
 __unsafe_unretained typeof(self) weakSelf = self;
self.block = ^{
    NSLog(@"%@", weakSelf);
};

__weak:不会产生强引用,指向的对象销毁时,会自动让指针置为nil

__unsafe_unretained:不会产生强引用,不安全,指向的对象销毁时,指针存储的地址值不变
  • 用__block解决(必须要调用block)
#import <Foundation/Foundation.h>
#import "Person.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        __block Person *person = [[Person alloc] init];
        person.block = ^{
            NSLog(@"%@", person);
            person = nil;
        };
        person.block();
    }
    NSLog(@"*****");
    return 0;
}

解决循环引用问题 - MRC

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

推荐阅读更多精彩内容