Block詳解

block的本質(zhì)

先看block的簡單實現(xiàn)

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

轉(zhuǎn)為C++代碼

struct __main_block_desc_0 {
    size_t reserved;
*********************************
block結(jié)構(gòu)體占用的內(nèi)存大小
*********************************
    size_t Block_size; 
};

struct __block_impl {
    void *isa; isa指針
    int Flags;
    int Reserved;
*********************************
block內(nèi)執(zhí)行的代碼會封裝到一個函數(shù)中沃呢,F(xiàn)uncPtr存放函數(shù)的內(nèi)存地址
*********************************
    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;
*********************************
構(gòu)造函數(shù)(類似OC的init)返回結(jié)構(gòu)體對象
*********************************
  __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執(zhí)行邏輯的函數(shù)
*********************************
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的繼承關(guān)系

*********************************
 __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

結(jié)論:

block本質(zhì)上也是一個OC對象,它內(nèi)部也有個isa指針
block是封裝了函數(shù)調(diào)用以及函數(shù)調(diào)用環(huán)境的OC對象

block的變量捕獲(capture)

為了保證block內(nèi)部能夠正常訪問外部的變量,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;
    }
}

打印結(jié)果

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

轉(zhuǎn)為C++源碼如下

void (*block)(void);

int weight_ = 10;
static int staticWeight_ = 10;
*********************************
__test_block_impl_0結(jié)構(gòu)體中并沒有捕獲全局變量
*********************************
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_參數(shù)直接調(diào)用全局變量,并沒有捕獲
*********************************
        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

轉(zhuǎn)為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調(diào)用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轉(zhuǎn)C默認傳遞兩個參數(shù)self 和 SEL _cmd
參數(shù)就是局部變量,所以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種類型焊傅,可以通過調(diào)用class方法或者isa指針查看具體類型,最終都是繼承自NSBlock類型
NSGlobalBlock ( _NSConcreteGlobalBlock )
NSStackBlock ( _NSConcreteStackBlock )
NSMallocBlock ( _NSConcreteMallocBlock )



每一種類型的block調(diào)用copy后的結(jié)果如下所示

驗證:

將工程置于MRC下(在ARC環(huán)境下狈涮,編譯器會根據(jù)情況自動將棧上的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]);
}

調(diào)用

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

結(jié)果如下:

沒有訪問auto變量block類型---------<__NSGlobalBlock__: 0x1000020a8>
copy后---------<__NSGlobalBlock__: 0x1000020a8>

訪問auto變量block類型---------<__NSStackBlock__: 0x7ffeefbff3e0>
copy后-<__NSMallocBlock__: 0x10051b360>

block的copy

在ARC環(huán)境下狐胎,編譯器會根據(jù)情況自動將棧上的block復制到堆上,比如以下情況

  • block作為函數(shù)返回值時
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的方法參數(shù)時
NSArray *arr = @[];
[arr sortedArrayUsingComparator:^NSComparisonResult(id  _Nonnull obj1, id  _Nonnull obj2) {
        }];
  • block作為GCD API的方法參數(shù)時
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內(nèi)部訪問了對象類型的auto變量時
    如果block是在棧上歌馍,將不會對auto變量產(chǎn)生強引用

驗證:(MRC環(huán)境下)

#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

調(diào)用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對象已經(jīng)被提前釋放松却,說明block并沒有對person產(chǎn)生強引用

  • 如果block被拷貝到堆上
  1. 會調(diào)用block內(nèi)部的copy函數(shù)
  2. copy函數(shù)內(nèi)部會調(diào)用_Block_object_assign函數(shù)
  3. _Block_object_assign函數(shù)會根據(jù)auto變量的修飾符(__strong暴浦、__weak溅话、__unsafe_unretained)做出相應的操作,形成強引用(retain)或者弱引用

驗證:(ARC環(huán)境下)

#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執(zhí)行邏輯的函數(shù)
*********************************
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")));
            }
*********************************
根據(jù)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函數(shù),對對象類型變量的引用和釋放
*********************************
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;

呢关炼?
結(jié)果就是

__NSMallocBlock__
Person - dealloc
  • 如果block從堆上移除
  1. 會調(diào)用block內(nèi)部的dispose函數(shù)
  2. dispose函數(shù)內(nèi)部會調(diào)用_Block_object_dispose函數(shù)
  3. _Block_object_dispose函數(shù)會自動釋放引用的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;
}

結(jié)果如下:

__NSMallocBlock__
------
Person - dealloc

源碼解讀在上面第2步

__weak問題解決

在使用clang轉(zhuǎn)換OC為C++代碼時,可能會遇到以下問題

cannot create __weak reference in file using manual reference

解決方案:支持ARC匣吊、指定運行時系統(tǒng)版本儒拂,比如

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

__block修飾符

__block可以用于解決block內(nèi)部無法修改auto變量值的問題
__block不能修飾全局變量、靜態(tài)變量(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;
}

轉(zhuǎn)為底層C++代碼

***************************
編譯器把__block變量age包裝成一個對象色鸳,轉(zhuǎn)變C++ 就是__Block_byref_age_0結(jié)構(gòu)體
__forwarding指針指向__Block_byref_age_0結(jié)構(gòu)體
***************************
struct __Block_byref_age_0 {
  void *__isa;
__Block_byref_age_0 *__forwarding;
 int __flags;
 int __size;
***************************
捕獲的age的內(nèi)存地址和外部變量age的內(nèi)存地址是一樣的
***************************
 int age;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
***************************
block內(nèi)部持有的 __Block_byref_age_0 類型的age指針指向上面的結(jié)構(gòu)體
這里是強引用關(guān)系
***************************
  __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內(nèi)部修改age變量的值會先找到__Block_byref_age_0結(jié)構(gòu)體
->__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的內(nèi)存地址
                                                          ***************************
                                                          (__Block_byref_age_0 *)&age, 
                                                          0, 
                                                          sizeof(__Block_byref_age_0), 
                                                          10
                                                          };
       ...
    }
    return 0;
}

編譯器會將__block變量包裝成一個對象

__block的內(nèi)存管理

  • 當block在棧上時社痛,并不會對__block變量產(chǎn)生強引用

  • 當block被copy到堆時

  1. 會調(diào)用block內(nèi)部的copy函數(shù)
  2. copy函數(shù)內(nèi)部會調(diào)用_Block_object_assign函數(shù)
  3. _Block_object_assign函數(shù)會對__block變量形成強引用(retain)
  • 當block從堆中移除時
  1. 會調(diào)用block內(nèi)部的dispose函數(shù)
  2. dispose函數(shù)內(nèi)部會調(diào)用_Block_object_dispose函數(shù)
  3. _Block_object_dispose函數(shù)會自動釋放引用的__block變量(release)

__block的__forwarding指針

對象類型的auto變量、__block變量

  • 當block在棧上時命雀,對它們都不會產(chǎn)生強引用

  • 當block拷貝到堆上時蒜哀,都會通過copy函數(shù)來處理它們
    __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函數(shù)來釋放它們
    __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;
}

轉(zhuǎn)為C++底層源碼

*************************
__Block修辭的person對象轉(zhuǎn)成C++變成了__Block_byref_person_0結(jié)構(gòu)體
*************************
struct __Block_byref_person_0 {
  void *__isa; 指針占用8個字節(jié)內(nèi)存空間
__Block_byref_person_0 *__forwarding; 8
 int __flags; 4
 int __size; 4
*************************
 __Block_byref_person_0結(jié)構(gòu)體中持有person對象
 copy和dispose函數(shù)是對person對象的引用和釋放
*************************
 void (*__Block_byref_id_object_copy)(void*, void*); 8
 void (*__Block_byref_id_object_dispose)(void*); 8
*************************
__Block_byref_person_0結(jié)構(gòu)體內(nèi)存地址+40就是person的內(nèi)存地址
在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的內(nèi)存管理操作
*************************
  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};

結(jié)論:

  • 當__block變量在棧上時吏砂,不會對指向的對象產(chǎn)生強引用

  • 當__block變量被copy到堆時

  1. 會調(diào)用__block變量內(nèi)部的copy函數(shù)
  2. copy函數(shù)內(nèi)部會調(diào)用_Block_object_assign函數(shù)
  3. _Block_object_assign函數(shù)會根據(jù)所指向?qū)ο蟮男揎椃╛_strong撵儿、__weak、__unsafe_unretained)做出相應的操作狐血,形成強引用(retain)或者弱引用(注意:這里僅限于ARC時會retain淀歇,MRC時不會retain
  • 如果__block變量從堆上移除
  1. 會調(diào)用__block變量內(nèi)部的dispose函數(shù)
  2. dispose函數(shù)內(nèi)部會調(diào)用_Block_object_dispose函數(shù)
  3. _Block_object_dispose函數(shù)會自動釋放指向的對象(release)

循環(huán)引用問題

#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函數(shù)執(zhí)行完畢后,dealloc函數(shù)并沒有執(zhí)行匈织,person對象沒有釋放

關(guān)于block持有self的問題浪默,上面從C++源碼角度已經(jīng)解讀就不在說了

解決循環(huán)引用問題 - ARC

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

__weak:不會產(chǎn)生強引用报亩,指向的對象銷毀時浴鸿,會自動讓指針置為nil

__unsafe_unretained:不會產(chǎn)生強引用,不安全弦追,指向的對象銷毀時岳链,指針存儲的地址值不變
  • 用__block解決(必須要調(diào)用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;
}

解決循環(huán)引用問題 - 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];
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市劲件,隨后出現(xiàn)的幾起案子掸哑,更是在濱河造成了極大的恐慌约急,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件苗分,死亡現(xiàn)場離奇詭異厌蔽,居然都是意外死亡,警方通過查閱死者的電腦和手機摔癣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進店門奴饮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人择浊,你說我怎么就攤上這事戴卜。” “怎么了琢岩?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵投剥,是天一觀的道長。 經(jīng)常有香客問我担孔,道長江锨,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任糕篇,我火速辦了婚禮啄育,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘娩缰。我一直安慰自己灸撰,他們只是感情好,可當我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布拼坎。 她就那樣靜靜地躺著浮毯,像睡著了一般。 火紅的嫁衣襯著肌膚如雪泰鸡。 梳的紋絲不亂的頭發(fā)上债蓝,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天,我揣著相機與錄音盛龄,去河邊找鬼饰迹。 笑死,一個胖子當著我的面吹牛余舶,可吹牛的內(nèi)容都是我干的啊鸭。 我是一名探鬼主播,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼匿值,長吁一口氣:“原來是場噩夢啊……” “哼赠制!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起挟憔,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤钟些,失蹤者是張志新(化名)和其女友劉穎烟号,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體政恍,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡汪拥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了篙耗。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片迫筑。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖鹤树,靈堂內(nèi)的尸體忽然破棺而出铣焊,到底是詐尸還是另有隱情,我是刑警寧澤罕伯,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站叽讳,受9級特大地震影響追他,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜岛蚤,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一邑狸、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧涤妒,春花似錦单雾、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至贿讹,卻和暖如春渐逃,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背民褂。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工茄菊, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人赊堪。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓面殖,卻偏偏與公主長得像,于是被迫代替她去往敵國和親哭廉。 傳聞我的和親對象是個殘疾皇子脊僚,可洞房花燭夜當晚...
    茶點故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 第一部分:Block本質(zhì) Q:什么是Block,Block的本質(zhì)是什么群叶? block本質(zhì)上也是一個OC對象吃挑,它內(nèi)部...
    sheldon_龍閱讀 555評論 0 0
  • 目錄1.Block 的基本使用2.Block 的底層數(shù)據(jù)結(jié)構(gòu)3.Block 的變量捕獲機制3.1 auto 類型的...
    師大小海騰閱讀 1,000評論 0 9
  • block對對象變量的捕獲 block一般使用過程中都是對對象變量的捕獲钝荡,那么對象變量的捕獲和基本數(shù)據(jù)類型變量相同...
    非洲小白猿閱讀 1,374評論 1 1
  • block ??Block:帶有自動變量(局部變量)的匿名函數(shù),它是C語言的擴充功能舶衬。之所以是拓展埠通,是因為C語言不...
    愛看書de圖圖閱讀 2,086評論 0 9
  • 久違的晴天,家長會逛犹。 家長大會開好到教室時端辱,離放學已經(jīng)沒多少時間了。班主任說已經(jīng)安排了三個家長分享經(jīng)驗虽画。 放學鈴聲...
    飄雪兒5閱讀 7,523評論 16 22