1:捕獲
2:block類型
2.1:問題 :mrc環境下 下面講的都是mrc環境下,會真實很多
2.2:在arc下,block 自動加上copy的情況---:返回block。
2.3:在arc下,block 自動加上copy的情況---:強指針__block;
2.4:arc環境下:方法名中有usingBlock的方法參數時,也會進行copy操作。
2.5:在arc下,block 自動加上copy的情況---:block作爲GCD的方法參數。
3:對象類型的auto變量
3.1:假設在mrc環境下
3.2:arc:對上面做一個原因解釋
4:如何修改block內部的變量:__block、static、全局、指針
4.1:__block的內存管理
4.2:__forwarding指針原理
4.3:問題:blok內部會對下面兩個對象有什麼不同麼
4.4:被__block修飾的對象類型:arc環境下
5:循環引用
下面以一個簡單的例子開始講起
int main(int argc, char * argv[]) {
@autoreleasepool {
int age = 10;
void (^ block)(void) = ^{
// age的值捕獲進來,(capture)
NSLog(@"block:%d",age);
};
age = 20;
block();
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
通過 (這裏解釋一下,編譯如果xcode路徑出問題可能導致編譯失敗,結果辦法點擊)
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m
把main.m -> main.cpp文件。
查看上面block的源碼
int main(int argc, char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
int age = 10;
void (* block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age));
age = 20;
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
}
}
整理一下就是
int main(int argc, char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
int age = 10; // 自動變量auto,離開作用域便自動銷燬 c語言特性
// 定義block變量
void (* block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age));
// 上面相當於是
void (* block)(void) = &__main_block_impl_0(
__main_block_func_0,
&__main_block_desc_0_DATA
, age);
age = 20;
// 執行block內部的代碼
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
// 上面相當於是:
block->FuncPtr(block);
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;
int age;
// 構造函數(類似oc的init方法),返回結構體對象 這個也就是block指向的 c++的特性 傳進來的_age會自動賦值給age
__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執行邏輯的函數:block裏面的函數
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int age = __cself->age; // bound by copy 取出block裏面的age
NSLog((NSString *)&__NSConstantStringImpl__var_folders_27_tr3mg74j3nq_zxgdyg1rj6480000gn_T_main_ff1ca7_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)};
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
1:捕獲
捕獲:block內部會專門新增一個成員來外面變量的值,這個操作稱之爲捕獲。
auto只存在於局部變量裏,不能存在於全局變量中,因爲它出了作用域就會被銷燬。值傳遞,相當於把10傳遞進去。(auto是自動銷燬的,所以必須要傳值。)
static,址傳遞。看下面
int main(int argc, char * argv[]) {
@autoreleasepool {
int age = 10;
static int height = 10;
void (^ block)(void) = ^{
NSLog(@"block:%d height:%d",age,height);
};
age = 20;
height = 20;
block();
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
所以上面的age捕獲的是值,所以block內部的age跟外面的int age並不是一個,block裏面的age是重新創建的,只是這裏是把int age的值賦值給了block裏面的age。只是因爲這種情況下age無法在block裏面進行賦值操作,所以age是不是改變的就不重要了但是也要注意,這種在block裏面操作取值,長時間的操作,會有不準確性。(這種情況是局部變量age情況下)
上面的static height 跟block裏面的*height是一個,因爲__main_block_impl_0()這個函數傳進去的是height指針,所以,這裏block裏面的*height修改了,同樣外面的值也就修改了。
如果是全局變量
int age = 10;
static int height = 10;
int main(int argc, char * argv[]) {
@autoreleasepool {
void (^ block)(void) = ^{
NSLog(@"block:%d height:%d",age,height);
};
age = 20;
height = 20;
block();
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
這個打印出來
2018-08-16 22:44:17.640 newxc[16609:5312874] block:20 height:20
源碼
因爲是全局變量,所以不論在哪裏都能夠訪問,所以沒有必要捕獲。
(這種情況下,block內部的height和age,就是拿的全局變量的指針進行操作的,這本就是一個數據進行的操作。)
局部變量需要捕獲,是因爲局部變量的作用域只僅限在當前函數作用域,如果block函數和調用不在一個函數作用域,那麼就會跨函數訪問局部變量,所以需要捕獲。
總結如下。
問:如果block內部調用self,會捕獲麼
#import "Person.h"
@implementation Person
- (void)test
{
void (^ block)(void) = ^{
NSLog(@"self:%p",self);
};
block();
}
@end
答案會捕獲。
因爲test在c語言中的寫法是
參數就是局部變量,所以會被捕獲。
person的test的c語言函數就是下面這樣,Person * self, SEL _cmd這兩個參數是默認必傳的參數。
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);
}
// 看這裏還有load方法
static void _C_Person_load(Class self, SEL _cmd) {
}
看一下上面的源碼
// 看這裏,裏面有一個Person *self 參數裏的self就會自動賦值給*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;
}
};
// 這裏是block內部函數,在訪問self。
static void __Person__test_block_func_0(struct __Person__test_block_impl_0 *__cself) {
Person *self = __cself->self; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_27_tr3mg74j3nq_zxgdyg1rj6480000gn_T_Person_4d5a32_mi_0,self);
}
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};
// 這裏是test函數 __Person__test_block_impl_0這個函數傳的值是self,上面是__Person__test_block_impl_0函數可以看到函數內部創建了一個person,來保存這個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));
// 調用block
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
問題:如果是訪問_name呢?
- (void)test
{
void (^ block)(void) = ^{
NSLog(@"self:%p",_name);
};
block();
}
答案是會捕獲,這個相當於是self->_age; 這個self是在block的局部的,這個捕獲的是self,然後訪問的self裏面的_age成員變量。並不是單獨對_age進行捕獲,而是對self進行捕獲。看源碼
問題:如果是[self name];
這個也是捕獲的,因爲先要拿到self,才能發消息
這裏需要知道一個問題:self 是局部變量,不是全局變量!!!!
2:block類型
從下面代碼的isa可以看出block是有類型的
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; // block的類型
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
block就是一個oc對象。我們可以通過[block class] 來查看一下它的類型。
我們來打印一下 這裏是arc環境下的,跟mrc還是不一樣的
int main(int argc, char * argv[]) {
@autoreleasepool {
void (^ block)(void) = ^{
};
block();
NSLog(@"%@", [block class]);
NSLog(@"%@", [[block class] superclass]);
NSLog(@"%@", [[[block class] superclass] superclass]);
NSLog(@"%@", [[[[block class] superclass] superclass] superclass]);
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
結果 :印證了 block是一個oc對象,isa本質上來說是從NSObject對象中來的。
// __NSGlobalBlock__ : __NSGlobalBlock : NSBlock : NSObject
2018-08-17 20:04:39.088 newxc[17439:5526253] __NSGlobalBlock__
2018-08-17 20:04:39.089 newxc[17439:5526253] __NSGlobalBlock
2018-08-17 20:04:39.090 newxc[17439:5526253] NSBlock
2018-08-17 20:04:39.090 newxc[17439:5526253] NSObject
block到底有哪幾種呢
// Global:沒有訪問auto變量。包括static 都是global
void (^ block)(void) = ^{
NSLog(@"self:");
};
// Stack:訪問了auto變量。 :這個需要把arc關掉。
int ne = 10;
void (^ block1)(void) = ^{
NSLog(@"self:%d",ne);
};
NSLog(@"%@ %@ %@",[block class], [block1 class],[^{
NSLog(@"%d",ne);
} class]);
block();
2018-08-17 20:12:12.046 newxc[17492:5539840] __NSGlobalBlock__ __NSMallocBlock__ __NSStackBlock__
但是通過編譯到.cpp文件中查看跟上面的類型不一致。isa都是_NSConcreteStackBlock類型
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;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
struct __main_block_impl_1 {
struct __block_impl impl;
struct __main_block_desc_1* Desc;
int ne;
__main_block_impl_1(void *fp, struct __main_block_desc_1 *desc, int _ne, int flags=0) : ne(_ne) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
struct __main_block_impl_2 {
struct __block_impl impl;
struct __main_block_desc_2* Desc;
int ne;
__main_block_impl_2(void *fp, struct __main_block_desc_2 *desc, int _ne, int flags=0) : ne(_ne) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
不一致原因:
一切以運行時結果爲準。
有時候通過clang 轉成的c++代碼,有時候並不是真正轉成的c++代碼。(蘋果的llvm1.0不再是生成c++文件,而是生成中間代碼,但是大致上相同,只有少數不同。llvm編譯器的一部分就是clang,就是clang屬於llvm編譯器裏面的一部分)從上到下是高地址到低地址。
2.1:問題 :mrc環境下 下面講的都是mrc環境下,會真實很多
先看一下block的類型
看下面的問題
void (^block)(void);
void test() {
// stack
int age = 10;
block = ^ {
NSLog(@"block------%d",age);
};
}
int main(int argc, char * argv[]) {
@autoreleasepool {
test();
block();
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
顯然:這個block的結果會有問題
2018-08-18 20:50:15.110 newxc[18572:6009580] block------272632489
那爲什麼會這樣呢? 是因爲這個block函數在棧上,block在全局變量區,但是這個block代碼塊內的所有包括這個age都是在棧上,棧有一個特點就是隨時可能就會被釋放,所以,這個打印的結果是不確定的。那如何解決呢?把這個block放在堆裏。也即是把這個block變成malloc類型。調用copy。下面這個就是在堆上的就是block。
void test() {
// stack
int age = 10;
block = [^{
NSLog(@"block------%d",age);
} copy];
}
那如果是global類型調用copy呢 是什麼類型呢
block = [^{
//NSLog(@"block------%d",age);
} copy];
block();
NSLog(@"block:%@",[block class]);
答案還是global類型
2018-08-18 21:04:59.322 newxc[18618:6028439] block:__NSGlobalBlock__
那上面stack調用兩次copy呢?(調用一次在堆上,再調用一次呢)
block = [[^{
NSLog(@"block------%d",age);
} copy] copy];
答案還是malloc:也即是malloc調用copy還是在堆區,但是並不會再開闢內存了,而是引用計數器+1,但是這種的記住要自己管理內存。
2018-08-18 21:07:08.597 newxc[18641:6032580] block:__NSMallocBlock__
2.2:在arc下,block 自動加上copy的情況---:返回block。
typedef void (^block)(void);
block test() {
// stack
int age = 19;
return ^{
NSLog(@"block------%d",age);
};
}
int main(int argc, char * argv[]) {
@autoreleasepool {
block bloc = test();
bloc();
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
因爲test函數內return的block是棧上的block,一旦函數作用域消失,那這個block就消失了,所以不是很妥當。如果是arc環境下,會自動給返回的這個block加了一個copy操作。在你函數作用域消失的時候,會自動給你加上release操作。
2.3:在arc下,block 自動加上copy的情況---:強指針__block;
在arc環境下,只要被強指針持有,就是malloc。就會自動給你這個block加copy。
int age = 10;
block bloccc = ^{
NSLog(@"---%d",age);
};
NSLog(@"%@",[bloccc class]);
2018-08-19 17:10:03.248 newxc[19403:6283487] __NSMallocBlock__
但是在mrc環境下 就是
2018-08-19 17:12:09.420 newxc[19450:6287484] __NSStackBlock__
2.4:arc環境下:方法名中有usingBlock的方法參數時,也會進行copy操作。
NSArray *array = @[];
[array enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
}];
有usingBlock 這個“^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { }”block也會搬到堆上面去。
2.5:在arc下,block 自動加上copy的情況---:block作爲GCD的方法參數。
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
});
只要是CGD裏面的block,“ ^{ }”這個block就會自動添加copy操作。
so:小總結
在mrc下一定要用copy,在arc下可以用strong也可以用copy,用strong是因爲有強指針,就會被自動添加copy,但是爲了項目在哪裏都能用,所以建議用copy。
3:對象類型的auto變量
arc環境
int main(int argc, char * argv[]) {
@autoreleasepool {
Person *person = [[Person alloc] init];
person.age = 10;
block bloccc = ^{
NSLog(@"---%d",person.age);
};
NSLog(@"-----=====");
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
源碼是
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
Person *person;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, Person *_person, int flags=0) : person(_person) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
這裏是auto變量是什麼捕捉的就是什麼。如果是 static Person *person; 那麼源碼中捕捉到的就是Person **person.
因爲整個block在堆上,而內部是對person的指針指引,所以只要block不釋放,對person的指針就不會釋放,person也就不會被釋放。
3.1:假設在mrc環境下
int main(int argc, char * argv[]) {
@autoreleasepool {
block blo;
{
Person *person = [[Person alloc] init];
person.age = 10;
blo = ^{
NSLog(@"---%d",person.age);
};
[person release];
}
NSLog(@"========");
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
#import "Person.h"
@implementation Person
- (void)dealloc
{
[super dealloc];
NSLog(@"person-dealloc----");
}
@end
2018-08-19 17:58:05.680 newxc[19773:6348592] person-dealloc----
2018-08-19 17:58:05.681 newxc[19773:6348592] ========
爲啥呢:因爲這個block是在棧上,而且棧空間上的是不會持有對象的。如果是堆空間,是有能力保住這個對象的。所以person就dealloc了。
如果對這個block copy 那麼person就不會dealloc了,因爲block相當於對person做了retain操作。
假設在arc環境下----------這裏2.1.1會做詳細解釋,從這裏做案例開始.
nt main(int argc, char * argv[]) {
@autoreleasepool {
block blo;
{
Person *person = [[Person alloc] init];
person.age = 10;
blo = ^{
NSLog(@"---%d",person.age);
};
}
NSLog(@"========");
}
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
2018-08-19 18:12:54.016 newxc[19963:6373020] ========
2018-08-19 18:12:54.017 newxc[19963:6373020] person-dealloc----
會先出了作用域,block釋放,然後block對其person的指針也沒了,所以person釋放。
假設用__weak來修飾person實例
int main(int argc, char * argv[]) {
@autoreleasepool {
block blo;
{
Person *person = [[Person alloc] init];
person.age = 10;
__weak Person *wekaPerson = person;
blo = ^{
NSLog(@"---%d",wekaPerson.age);
};
}
NSLog(@"========");
}
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
2018-08-19 18:15:29.656 newxc[19986:6377817] person-dealloc----
2018-08-19 18:15:29.660 newxc[19986:6377817] ========
看結果不一樣了,爲啥呢,
3.2:arc:對上面做一個原因解釋
弱引用:需要運行時的支持的。
Person *person = [[Person alloc] init];
person.age = 10;
__weak Person *wekaPerson = person;
blo = ^{
NSLog(@"---%d",wekaPerson.age);
};
你會發現,當你寫了__weak,在去調用下面的命令時會報錯
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m
/var/folders/27/tr3mg74j3nq_zxgdyg1rj6480000gn/T/main-6a8820.mi:47464:28: error:
cannot create __weak reference because the current deployment target does
not support weak references
__attribute__((objc_ownership(weak))) Person *wekaPerson = person;
^
1 error generated.
你會發現上述報錯,只是靜態的編譯會報錯,所以需要我們指定是runtime並且指定arc環境。
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m
發現,不一樣的地方,就是person弱引用了
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
Person *__weak wekaPerson;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, Person *__weak _wekaPerson, int flags=0) : wekaPerson(_wekaPerson) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
那如果是不加__weak呢 ,這裏注意要接着用上面的指令編譯,發現結果,是strong.
typedef void (*Block)(void);
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
Person *__strong per; //注意這裏,是strong類型,這是沒有加__weak的情況,同樣也沒有加__strong
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, Person *__strong _per, int flags=0) : per(_per) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
那如果是加了__strong的情況呢?接着使用上指令 結果可以看到依然是__strong修飾
Block blo;
{
__strong Person *per = [[Person alloc] init];
per.age = 100;
blo = ^{
NSLog(@"---%d",per.age);
};
};
typedef void (*Block)(void);
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
Person *__strong per; // 主要看這裏,是用__strong來修飾的
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, Person *__strong _per, int flags=0) : per(_per) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
Person *__strong per = __cself->per; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_27_tr3mg74j3nq_zxgdyg1rj6480000gn_T_main_a9de8d_mi_0,((int (*)(id, SEL))(void *)objc_msgSend)((id)per, sel_registerName("age")));
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->per, (void*)src->per, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->per, 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};
好總結一下,
如果block在棧上(不論是arc還是mrc),都不會對block內部的產生強引用。(因爲自己都是棧上的,隨時會被銷燬,所以不會對裏面的對象產生強引用。)
可以看到這個有對象的block跟以前的block不太一樣。不太理解不一樣的,可以看1.1捕獲上面的源碼跟這個的區別,是多了兩個函數,請看下面。
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->per, (void*)src->per, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->per, 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};
__main_block_copy_0賦值給copy函數,__main_block_dispose_0賦值給dispose函數。
也就是說,上面的blo一旦進行copy操作,要copy到堆上,就會自動調用copy函數,這個函數會調用裏面的_Block_object_assign函數,這個函數做的事情是:會根據外部auto變量傳進來的是strong還是weak,來對__main_block_impl_0裏面的Person指針進行對外部的person對象是strong還是weak。如果是傳進來的是strong,則__main_block_impl_0裏面的person指針會強引用着外部的person對象,如果是weak也是一樣。
那dispose什麼時候調用呢? 當堆上的blo 進行銷燬時,就會自動調用block內部的dispose,就會釋放引用的那些函數
看總結圖
看題
蘋果會認爲block內部有強引用的時候,不會釋放,等到強引用釋放完了纔會釋放。弱引用沒有影響。
額外提示一下,什麼情況下才會出現copy和dispose這兩個函數呢?
一旦block內部訪問的是對象類型的,就會出現copy和dispose函數,因爲訪問的是對象,想擁有這個對象,所以就會有內存對象管理。
4:如何修改block內部的變量:__block、static、全局、指針
如果我們想對block裏面的變量進行修改,怎麼做?
a:static
b:弄成全局變量
c:用__block.
d:數據類型用指針指向(參考static使用的block內部原理)
1:先看d也就是static使用的block的內部原理
typedef void(^Block)(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
static int height = 10;
Block bl = ^{
height = 199;
};
bl();
}
return 0;
}
typedef void(*Block)(void);
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int *height;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_height, int *__newhei, int flags=0) : height(_height){
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int *height = __cself->height; // bound by copy
(*height) = 199;
}
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, const char * argv[]) {
static int height = 10;
Block bl = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &height));
((void (*)(__block_impl *))((__block_impl *)bl)->FuncPtr)((__block_impl *)bl);
}
return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };
從上面可以看出來 傳進去的是height的指針,從__main_block_impl_0對象結構體中也可以知道,是拿int *height;來接收的,在__main_block_func_0函數中可以看到賦值時候用的是int *_newhei = __cself->_newhei; *_newhei = 20; .所以我們可以這麼做
typedef void(^Block)(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
int age = 10;
int *_hei = &age;
int *_newhei = _hei;
*_newhei = 304;
Block bl = ^{
*_newhei = 20;
};
bl();
NSLog(@"在這裏打印的話會發現age已經變爲20 了 並且*_newhei 也是20")
}
return 0;
}
當然 這種方式是麻煩了點,而且只針對基本數據類型,對於對象類型不適用。那爲什麼不適用呢,請看下面
Person *per = [[Person alloc] init];
NSInteger df = 19;
static int lage = 100;
Block bl = ^{
lage = 300;
NSLog(@"%@",per.name);
};
bl();
這個底層源碼是 我們只看這一段
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int *lage = __cself->lage; // bound by copy
Person *per = __cself->per; // bound by copy
(*lage) = 300;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_70_0rj_h7dx3zv20xx9ln3x0d8h0000gn_T_main_3efd6b_mi_0,((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)per, sel_registerName("name")));
}
注意到,對於基本數據類型,這裏取值都用的是*lage 而不是lage,也就是這是取值,並不是取的指針。
但是對於對象來說,取的是per,也就是(id)per,而不是*per,(這個*per在oc中也不代表什麼)。所以對於oc對象來說,同樣跟c語言一樣有指針,但是方式卻不一樣。 所以oc對象來說 應該方式都是這樣的下面包裝一層的方式
2:看一個例子 用__block
typedef void (^Block)(void);
int main(int argc, char * argv[]) {
@autoreleasepool {
__block int age = 10;
Block blo = ^{
NSLog(@"---%d",age);
};
}
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
typedef void (*Block)(void);
struct __Block_byref_age_0 {
void *__isa;
__Block_byref_age_0 *__forwarding; // 看下面的引用知道,這個指向自己
int __flags;
int __size;
int age;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_age_0 *age; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_age_0 *_age, int flags=0) : age(_age->__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_age_0 *age = __cself->age; // bound by ref
(age->__forwarding->age) = 20;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_27_tr3mg74j3nq_zxgdyg1rj6480000gn_T_main_0dc06e_mi_0,(age->__forwarding->age));
}
// 要對__main_block_impl_0對象進行內存管理,所有有copy和dispose
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->age, (void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->age, 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;
// 這裏是 __block int age = 10; 是下面的這個
__attribute__((__blocks__(byref))) __Block_byref_age_0 age = {
(void*)0,
(__Block_byref_age_0 *)&age,
0,
sizeof(__Block_byref_age_0),
10
};
Block blo = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_age_0 *)&age, 570425344));
}
return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };
// 要對__Block_byref_age_0對象進行內存管理,所有有copy和dispose
看這個結構體 這個是調用age,能看出來第二個(__Block_byref_age_0 *)&age賦值給了__Block_byref_age_0結構體的forwarding指針,也就是把自己的地址賦值給了自己結構體中的forwarding。
__attribute__((__blocks__(byref))) __Block_byref_age_0 age = {
(void*)0,
(__Block_byref_age_0 *)&age,
0,
sizeof(__Block_byref_age_0),
10};
struct __Block_byref_age_0 {
void *__isa;
__Block_byref_age_0 *__forwarding;
int __flags;
int __size;
int age;
};
接着看這個,看blo這個結構體把age的地址:&age 傳進去了,賦值給了第三個 __Block_byref_age_0 *age; // by ref
Block blo = (
(void (*)())&__main_block_impl_0((void *)__main_block_func_0,
&__main_block_desc_0_DATA,
(__Block_byref_age_0 *)&age,
570425344));
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_age_0 *age; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_age_0 *_age, int flags=0) : age(_age->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
所以,block內部會有一個指針(這裏是*age)指向__Block_byref_age_0結構體,這個結構體內部又有age這個值,存儲這個值,那是如何修改這個值的呢?
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_age_0 *age = __cself->age; // bound by ref
(age->__forwarding->age) = 20;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_27_tr3mg74j3nq_zxgdyg1rj6480000gn_T_main_acce32_mi_0,(age->__forwarding->age));
}
看這個__cself從__main_block_impl_0這個結構體中拿到age也就是__Block_byref_age_0結構體,然後用這個結構體先訪問自己內部的forwarding,因爲forwarding指向的就是自己,所以再拿age,再賦值,就賦值成功啦。那到此肯定有疑問,這個改變的不就是block內部的age這個值麼,而且這個age是在內部創建的,驗證。打印一下
__block int age = 10;
Block blo = ^{
age = 20;
NSLog(@"---%d",age);
};
blo();
NSLog(@"外面:%d",age);
2018-09-07 22:01:44.772 newxc[31713:10543714] ---20
2018-09-07 22:02:16.602 newxc[31713:10543714] 外面:20
那是爲啥呢?看這個,不加block,只用__block修飾,再看源碼
int main(int argc, char * argv[]) {
@autoreleasepool {
__block int age = 10;
NSLog(@"外面:%d",age);
}
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
struct __Block_byref_age_0 {
void *__isa;
__Block_byref_age_0 *__forwarding;
int __flags;
int __size;
int age;
};
int main(int argc, char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
__attribute__((__blocks__(byref))) __Block_byref_age_0 age = {(void*)0,(__Block_byref_age_0 *)&age, 0, sizeof(__Block_byref_age_0), 10};
NSLog((NSString *)&__NSConstantStringImpl__var_folders_27_tr3mg74j3nq_zxgdyg1rj6480000gn_T_main_80e809_mi_0,(age.__forwarding->age));
}
return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
}
請看,有__Block_byref_age_0結構體,就是說只要加上__block,就會被包裝秤這個對象結構體.在nslog中訪問的就是__Block_byref_age_0結構體中的age,所以是能修改的,這個原來船進入的是10,並不是10的地址,而是這個結構體中的age是新生成的age變量,這只是造成了一個假象,顯示這個age被修改了,其實是修改的這個__Block_byref_age_0結構體對象裏面的age變量,驗證如下 (就如同,外面是一個person變量,有一個age的屬性,在block內部給age賦值,是修改成功的)。
當使用了__block之後,age的地址就發生了變化,這裏因爲arc環境下,block已經copy到堆上,但是原來__block修飾的age的那個對象還在棧上,而最後age= 20,這個age,是訪問的堆上的age,(因爲已經從棧到堆了。)
總結
那如果再加上一個person對象呢用__block修飾?
typedef void(^Block)(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
__block Person *per = [[Person alloc] init];
__block int age = 10;
Block bl = ^{
NSString *a = per.name;
per.name = @"34";
age = 300;
};
bl();
}
return 0;
}
typedef void(*Block)(void);
struct __Block_byref_per_0 {
void *__isa;
__Block_byref_per_0 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
Person *per;
};
struct __Block_byref_age_1 {
void *__isa;
__Block_byref_age_1 *__forwarding;
int __flags;
int __size;
int age;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_per_0 *per; // by ref
__Block_byref_age_1 *age; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_per_0 *_per, __Block_byref_age_1 *_age, int flags=0) : per(_per->__forwarding), age(_age->__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_per_0 *per = __cself->per; // bound by ref
__Block_byref_age_1 *age = __cself->age; // bound by ref
NSString *a = ((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)(per->__forwarding->per), sel_registerName("name"));
((void (*)(id, SEL, NSString *))(void *)objc_msgSend)((id)(per->__forwarding->per), sel_registerName("setName:"), (NSString *)&__NSConstantStringImpl__var_folders_70_0rj_h7dx3zv20xx9ln3x0d8h0000gn_T_main_b0ec3f_mi_0);
(age->__forwarding->age) = 300;
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->per, (void*)src->per, 8/*BLOCK_FIELD_IS_BYREF*/);_Block_object_assign((void*)&dst->age, (void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->per, 8/*BLOCK_FIELD_IS_BYREF*/);_Block_object_dispose((void*)src->age, 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, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
__attribute__((__blocks__(byref))) __Block_byref_per_0 per = {(void*)0,(__Block_byref_per_0 *)&per, 33554432, sizeof(__Block_byref_per_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc")), sel_registerName("init"))};
__attribute__((__blocks__(byref))) __Block_byref_age_1 age = {(void*)0,(__Block_byref_age_1 *)&age, 0, sizeof(__Block_byref_age_1), 10};
Block bl = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_per_0 *)&per, (__Block_byref_age_1 *)&age, 570425344));
((void (*)(__block_impl *))((__block_impl *)bl)->FuncPtr)((__block_impl *)bl);
}
return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };
看上面就會生成兩個__main_block_impl_結構體,並且最後nslog裏,訪問的也是__main_block_impl_結構體裏面的forwarding裏的per或者age。
4.1:__block的內存管理
(如果是對象修飾用__block的話,記住兩個copy是不同的,__main_block_copy_0和__Block_byref_id_object_copy。作用對象也不同)
強引用__Block_byref_age_0 *age; 對象,對這個強引用(copy)
銷燬對__Block_byref_age_0 *age;對象進行release。
(這個內存管理對沒有修飾的對象類型的copy是一樣的,如果加上了__block, 那麼就是再加上一層內存管理,也就是__block變量內部對對象類型的內存管理。)
4.2:__forwarding指針原理
那上面間接的可以解釋爲什麼有__forwarding指針,而不是age直接指向age
(age->__forwarding->age) = 20;
因爲這個__Block_byref_age_0對象剛開始都是在棧上,當block被copy到堆上的時候,自動也就copy了一份__Block_byref_age_0對象,這個時候,第一個age是棧上的,而它的__forwarding指針是指向堆上的它自己,所以需要用__forwarding的age來取值或者賦值,但是如果block是在棧上,那麼這些都不會被copy到堆上,所以__forwarding還是指向自己,所以,這不論是在棧還是再堆都實現了指向自己,所以可以賦值或者取值。(copy到堆上了,地址肯定就發生變化了。)
4.3:問題:blok內部會對下面兩個對象有什麼不同麼
Person *per = [[Person alloc] init];
__block int age = 10;
不同點在這裏
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
_Block_object_assign((void*)&dst->age, (void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);
_Block_object_assign((void*)&dst->per, (void*)src->per, 3/*BLOCK_FIELD_IS_OBJECT*/);}
一旦用__block,這個assign函數就會對這個對象產生強引用,而沒有用__block的對象,會根據是strong還是weak來修飾的,再對它產生強或者弱引用。
那相同點是什麼呢?
4.4:被__block修飾的對象類型:arc環境下
這裏着重看一下下面兩個
__block Person *weakPerson = per;
__block __weak Person *weakPerson = per;
這兩個在代碼上有需要注意的地方,先看下__block Person *weakPerson = per;的源碼
typedef void (^Block)(void);
int main(int argc, char * argv[]) {
@autoreleasepool {
Person *per = [[Person alloc] init];
__block Person *weakPerson = per;
Block blok = ^{
weakPerson.age = 20;
};
blok();
}
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
#define __OFFSETOFIVAR__(TYPE, MEMBER) ((long long) &((TYPE *)0)->MEMBER)
static void __Block_byref_id_object_copy_131(void *dst, void *src) {
_Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131);
} // 看這裏assign 對 person這個對象進行copy,也就是進行131,根據外面傳進來的操作。
static void __Block_byref_id_object_dispose_131(void *src) {
_Block_object_dispose(*(void * *) ((char*)src + 40), 131);
}
typedef void (*Block)(void);
struct __Block_byref_weakPerson_0 {
void *__isa;
__Block_byref_weakPerson_0 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
Person *__strong weakPerson;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_weakPerson_0 *weakPerson; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_weakPerson_0 *_weakPerson, int flags=0) : weakPerson(_weakPerson->__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_weakPerson_0 *weakPerson = __cself->weakPerson; // bound by ref
((void (*)(id, SEL, int))(void *)objc_msgSend)((id)(weakPerson->__forwarding->weakPerson), sel_registerName("setAge:"), 20);
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->weakPerson, (void*)src->weakPerson, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->weakPerson, 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;
Person *per = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc")), sel_registerName("init"));
__attribute__((__blocks__(byref))) __Block_byref_weakPerson_0 weakPerson = {
(void*)0,
(__Block_byref_weakPerson_0 *)&weakPerson,
33554432,
sizeof(__Block_byref_weakPerson_0),
__Block_byref_id_object_copy_131,
__Block_byref_id_object_dispose_131,
per}; // 這裏的封裝好的weakPerson放在下面裏面.
Block blok = (
(void (*)())&__main_block_impl_0((void *)__main_block_func_0,
&__main_block_desc_0_DATA,
(__Block_byref_weakPerson_0 *)&weakPerson,
570425344));
((void (*)(__block_impl *))((__block_impl *)blok)->FuncPtr)((__block_impl *)blok);
}
return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
}
看這個用__block修飾的對象跟__block修飾的普通數據類型不一樣的地方,在__Block_byref_weakPerson_0對象裏多了兩個參數, void (*__Block_byref_id_object_copy)(void*, void*);和 void (*__Block_byref_id_object_dispose)(void*);那這兩個的作用是什麼呢?就是管理__Block_byref_per_0中_Person *__strong weakPerson;的內存的,也就是是對象就需要的內存管理。
接着分析這個過程,剛開始把weakPerson封裝好給blok賦值,從__Block_byref_weakPerson_0結構中可以看到,賦值的內容:isa是指針,
__forwarding是賦值的地址&weakPerson,
__flags賦值的是33554432,
__size賦值的是sizeof(__Block_byref_weakPerson_0),
__Block_byref_id_object_copy賦值的是 __Block_byref_id_object_copy_131,
__Block_byref_id_object_dispose賦值的是__Block_byref_id_object_dispose_131,
Person *__strong weakPerson;賦值的是per。
所以__forwarding指向的就是自己。而__Block_byref_id_object_copy_131看這個函數內是這個_Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131); 這個是這個結構體指針的地址加40,也就是__Block_byref_weakPerson_0的地址加40 就是 Person *__strong weakPerson;,對它進行的copy和dispose操作。
所以 過程是:
當block被copy到堆上的時候,(arc環境下),block會調用__main_block_copy_0函數,來對__main_block_impl_0對象內部的 __Block_byref_weakPerson_0 *weakPerson;進行copy,進行強指針引用。同時__Block_byref_weakPerson_0對象也被拷貝到堆上,在這個copy的時候,__Block_byref_weakPerson_0對象就會調用自己內部的__Block_byref_id_object_copy對自己內部的對象 Person *__strong weakPerson進行copy;根據前面的修飾符進行強引用還是弱引用,這裏因爲是strong,所以是強引用。同時注意一下,如果是mrc環境下,那麼這個對person的引用就永遠不會是強引用。
好下面看
typedef void (^Block)(void);
int main(int argc, char * argv[]) {
@autoreleasepool {
Person *per = [[Person alloc] init];
__block __weak Person *weakPerson = per;
Block blok = ^{
weakPerson.age = 20;
};
blok();
}
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
typedef void (*Block)(void);
struct __Block_byref_weakPerson_0 {
void *__isa;
__Block_byref_weakPerson_0 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
Person *__weak weakPerson;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_weakPerson_0 *weakPerson; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_weakPerson_0 *_weakPerson, int flags=0) : weakPerson(_weakPerson->__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_weakPerson_0 *weakPerson = __cself->weakPerson; // bound by ref
((void (*)(id, SEL, int))(void *)objc_msgSend)((id)(weakPerson->__forwarding->weakPerson), sel_registerName("setAge:"), 20);
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->weakPerson, (void*)src->weakPerson, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->weakPerson, 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;
Person *per = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc")), sel_registerName("init"));
__attribute__((__blocks__(byref))) __attribute__((objc_ownership(weak))) __Block_byref_weakPerson_0 weakPerson = {(void*)0,(__Block_byref_weakPerson_0 *)&weakPerson, 33554432, sizeof(__Block_byref_weakPerson_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, per};
Block blok = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_weakPerson_0 *)&weakPerson, 570425344));
((void (*)(__block_impl *))((__block_impl *)blok)->FuncPtr)((__block_impl *)blok);
}
return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
}
跟上面不同的點就在於__Block_byref_weakPerson_0裏面的 Person *__weak weakPerson;這裏是weak修飾,同樣形象化的以下圖解釋
證明是強指針:
證明有弱指針
在mrc環境下,證明copy也不會產生強指針。
arc下不論block裏經過幾層對person對象,都會有強弱指針。
block的內存管理,調用_Block_object_assign的時候傳進去的值要麼是3要麼是8,但是__block修飾的 傳進去的是__Block_byref_id_object_copy_131 這個 是131,這個管理的還是不同的。
總結:
5:循環引用
typedef void (^Block)(void);
int main(int argc, char * argv[]) {
@autoreleasepool {
Person *per = [[Person alloc] init];
per.age = 10;
per.block = ^{
NSLog(@"-----%d",per.age);
};
}
NSLog(@"-----000000000");
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
// 我們只看這一節
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
Person *__strong per;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, Person *__strong _per, int flags=0) : per(_per) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
看到上面是__strong,也就是下面這個圖
導致最後person對Person這個類的指針釋放了,上面兩個卻一直相互引用,無法釋放。
5.1:解決循環引用問題
arc:
__weak __unsafe_unretained解決 和__block
Person *per = [[Person alloc] init];
__weak typeof(per) weakPer = per;
per.age = 10;
per.block = ^{
NSLog(@"-----%d",weakPer.age);
};
Person *per = [[Person alloc] init];
// __weak 不會產生強引用
// __unsafe_unretained 不會產生強引用 不安全
__unsafe_unretained typeof(per) weakPer = per;
per.age = 10;
per.block = ^{
NSLog(@"-----%d",weakPer.age);
};
weak會把指向的對象銷燬時會自動置讓指針爲nil,所以weak是安全的。
__block
int main(int argc, char * argv[]) {
@autoreleasepool {
__block Person *per = [[Person alloc] init];
// __weak 不會產生強引用
// __unsafe_unretained 不會產生強引用 不安全
per.age = 10;
per.block = ^{
NSLog(@"-----%d",per.age);
per = nil; // 這兩個必須要寫
};
per.block(); // 這兩個必須要寫 可能有內存泄漏,
}
NSLog(@"-----000000000");
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
mrc環境下:
__unsafe_unretained 和__block
mrc不支持__weak。
int main(int argc, char * argv[]) {
@autoreleasepool {
__unsafe_unretained Person *per = [[Person alloc] init];
per.age = 10;
per.block = [^{
NSLog(@"-----%d",per.age);
} copy];
[per release];
}
NSLog(@"-----000000000");
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
2018-09-10 19:42:27.889 newxc[34540:11722615] person-dealloc----
2018-09-10 19:42:27.890 newxc[34540:11722615] -----000000000
__block在mrc下不會產生強引用對立面的對象。
__block Person *per = [[Person alloc] init];
per.age = 10;
per.block = [^{
NSLog(@"-----%d",per.age);
} copy];
[per release];
大總結:面試
1:block的原理是怎樣的,本質是什麼?
封裝了函數調用以及調用環境OC對象。
2:__block的作用?有什麼使用的注意點?
封裝成了一個對象,在mrc環境下,不會產生強引用對立面的對象
3:block的屬性修飾詞爲什麼是copy?使用block有哪些使用注意?
block沒有copy,就不會在堆上,copy到堆上,就可以進行內存管理了,使用注意:循環引用
4:block在修改NSMUtablearray,需不需要添加__block?
不需要,
- (void)test
{
__weak typeof(self) weakSelf = self;
self.age = 10;
self.block = ^{
NSLog(@"-----%d",weakSelf->_age);
};
}
這個會報錯,可能是會空值,不能產生強引用,可能會空值
所以就要用一個強指針,來騙編譯器,這是編譯器行爲
- (void)test
{
__weak typeof(self) weakSelf = self;
self.age = 10;
self.block = ^{
__strong typeof(weakSelf) myself = weakSelf;
NSLog(@"-----%d",myself->_age);
};
}
另外 用這個強指針 可以確保不會保住這個對象。