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被拷貝到堆上
- 會調(diào)用block內(nèi)部的copy函數(shù)
- copy函數(shù)內(nèi)部會調(diào)用_Block_object_assign函數(shù)
- _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從堆上移除
- 會調(diào)用block內(nèi)部的dispose函數(shù)
- dispose函數(shù)內(nèi)部會調(diào)用_Block_object_dispose函數(shù)
- _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到堆時
- 會調(diào)用block內(nèi)部的copy函數(shù)
- copy函數(shù)內(nèi)部會調(diào)用_Block_object_assign函數(shù)
- _Block_object_assign函數(shù)會對
__block
變量形成強引用(retain)
- 當block從堆中移除時
- 會調(diào)用block內(nèi)部的dispose函數(shù)
- dispose函數(shù)內(nèi)部會調(diào)用_Block_object_dispose函數(shù)
- _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到堆時
- 會調(diào)用
__block
變量內(nèi)部的copy函數(shù) - copy函數(shù)內(nèi)部會調(diào)用_Block_object_assign函數(shù)
- _Block_object_assign函數(shù)會根據(jù)所指向?qū)ο蟮男揎椃╛_strong撵儿、__weak、__unsafe_unretained)做出相應的操作狐血,形成強引用(retain)或者弱引用(
注意:這里僅限于ARC時會retain淀歇,MRC時不會retain
)
- 如果__block變量從堆上移除
- 會調(diào)用
__block
變量內(nèi)部的dispose函數(shù) - dispose函數(shù)內(nèi)部會調(diào)用_Block_object_dispose函數(shù)
- _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];