1皆怕、Objective-C的本質(zhì)
Objective-C代碼捅伤,底層實(shí)現(xiàn)其實(shí)都是C\C++代碼对竣。
Objective-C的對(duì)象、類主要是基于C\C++的結(jié)構(gòu)體實(shí)現(xiàn)的捏卓。
將Objective-C代碼轉(zhuǎn)換為C\C++代碼
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc OC源文件 -o 輸出的CPP文件
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc ViewController.m -o ViewController.cpp
如果需要鏈接其他框架极祸,使用-framework參數(shù)。比如-framework UIKit
2怠晴、OC對(duì)象的本質(zhì)
OC對(duì)象占用字節(jié)數(shù)
NSObject *obj = [[NSObject alloc] init];
// 獲得NSObject實(shí)例對(duì)象的成員變量所占用的大小 >> 8個(gè)字節(jié)
NSLog(@"%zd", class_getInstanceSize([NSObject class]));
// 獲得obj指針?biāo)赶騼?nèi)存的大小 >> 16個(gè)字節(jié)
NSLog(@"%zd", malloc_size((__bridge const void *)obj));
NSObject底層實(shí)現(xiàn)
NSObject其實(shí)是用結(jié)構(gòu)體實(shí)現(xiàn)遥金,有個(gè)isa屬性。
@interface NSObject {
Class isa;
}
轉(zhuǎn)化為結(jié)構(gòu)體
struct NSObject_IMPL {
Class isa; // 8個(gè)字節(jié)
};
//typedef struct objc_class *Class;//指針
獲取分配字節(jié)的函數(shù)
- 創(chuàng)建一個(gè)實(shí)例對(duì)象蒜田,至少需要多少內(nèi)存
#import <objc/runtime.h>
class_getInstanceSize([NSObject class]);
- 創(chuàng)建一個(gè)實(shí)例對(duì)象稿械,實(shí)際上分配了多少內(nèi)存
#import <malloc/malloc.h>
malloc_size((__bridge const void *)obj);
繼承的類占用字節(jié)數(shù)
@interface Student : NSObject
{
Class isa;//8
int _no;//4
int _age;//4
int _age1;//4
int _age2;//4
}
@end
NSLog(@"%zd", class_getInstanceSize([Student class]));//24 成員變量占用24個(gè)字節(jié)
NSLog(@"%zd", malloc_size((__bridge const void *)stu));//32 系統(tǒng)分配了32個(gè)字節(jié)
2、OC對(duì)象的分類
- instance對(duì)象(實(shí)例對(duì)象)
- class對(duì)象(類對(duì)象)
- meta-class對(duì)象(元類對(duì)象)
2.1 instance
instance對(duì)象就是通過類alloc出來的對(duì)象冲粤,每次調(diào)用alloc都會(huì)產(chǎn)生新的instance對(duì)象美莫。
NSObject *object1 = [[NSObject alloc] init];
NSObject *object2 = [[NSObject alloc] init];
- object1、object2是NSObject的instance對(duì)象(實(shí)例對(duì)象)
- 它們是不同的兩個(gè)對(duì)象梯捕,分別占據(jù)著兩塊不同的內(nèi)存
- instance對(duì)象在內(nèi)存中存儲(chǔ)的信息包括isa指針厢呵、其他成員變量。
例
@interface Person : NSObject {
@public
int _age;
}
@end
@implementation Person
@end
Person *p1 = [[Person alloc] init];
person1->_age = 3;
Person *p2 = [[Person alloc] init];
person2->_age = 4;
2.2 class
NSObject *object1 = [[NSObject alloc] init];
NSObject *object2 = [[NSObject alloc] init];
Class objectClass1 = [object1 class];
Class objectClass2 = [object2 class];
Class objectClass3 = object_getClass(object1);
Class objectClass4 = object_getClass(object2);
Class objectClass5 = [NSObject class];
Class objectClass6 = [[NSObject class] class];
- objectClass1 ~ objectClass6都是NSObject的class對(duì)象(類對(duì)象)
- 它們是同一個(gè)對(duì)象傀顾。每個(gè)類在內(nèi)存中有且只有一個(gè)class對(duì)象
- class對(duì)象在內(nèi)存中存儲(chǔ)的信息主要包括
- isa指針
- superclass指針
- 類的屬性信息(@property)襟铭、類的對(duì)象方法信息(instance method)
- 類的協(xié)議信息(protocol)、類的成員變量信息(ivar)
2.3 meta-class
Class objectMetaClass = object_getClass([NSObject class]);
- objectMetaClass是NSObject的meta-class對(duì)象(元類對(duì)象)
- 每個(gè)類在內(nèi)存中有且只有一個(gè)meta-class對(duì)象
-
meta-class對(duì)象和class對(duì)象的內(nèi)存結(jié)構(gòu)是一樣的,但是用途不一樣蝌矛,在內(nèi)存中存儲(chǔ)的信息主要包括
- isa指針
- superclass指針
- 類的類方法信息
- 以下代碼獲取的objectClass是class對(duì)象道批,并不是meta-class對(duì)象
Class objectClass6 = [[NSObject class] class];
- 查看Class是否為meta-class
#import <objc/runtime.h>
BOOL result = class_isMetaClass([NSObject class]);
2.4 isa指針
-
instance的isa指向class
當(dāng)調(diào)用對(duì)象方法時(shí),通過instance的isa找到class入撒,最后找到對(duì)象方法的實(shí)現(xiàn)進(jìn)行調(diào)用 -
class的isa指向meta-class
當(dāng)調(diào)用類方法時(shí)隆豹,通過class的isa找到meta-class,最后找到類方法的實(shí)現(xiàn)進(jìn)行調(diào)用
2.5 class對(duì)象的superclass指針
- 當(dāng)Student的instance對(duì)象要調(diào)用Person的對(duì)象方法時(shí)茅逮,會(huì)先通過isa找到Student的class璃赡,然后通過superclass找到Person的class,最后找到對(duì)象方法的實(shí)現(xiàn)進(jìn)行調(diào)用
2.6 meta-class對(duì)象的superclass指針
- 當(dāng)Student的class要調(diào)用Person的類方法時(shí)献雅,會(huì)先通過isa找到Student的meta-class碉考,然后通過superclass找到Person的meta-class,最后找到類方法的實(shí)現(xiàn)進(jìn)行調(diào)用
2.7 isa挺身、superclass總結(jié)
- instance的isa指向class
- class的isa指向meta-class
- meta-class的isa指向基類的meta-class
- class的superclass指向父類的class, 如果沒有父類侯谁,superclass指針為nil
- meta-class的superclass指向父類的meta-class, 基類的meta-class的superclass指向基類的class
- instance調(diào)用對(duì)象方法的軌跡: isa找到class,方法不存在章钾,就通過superclass找父類
- class調(diào)用類方法的軌跡:isa找meta-class墙贱,方法不存在,就通過superclass找父類
注意第5點(diǎn)
@interface NSObject (Test)
+ (void)test;
@end
@implementation NSObject (Test)
- (void)test {
NSLog(@"-[NSObject test] - %p", self);
}
@end
@interface Person : NSObject
+ (void)test;
@end
@implementation Person
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"[Person class] - %p", [Person class]);
NSLog(@"[NSObject class] - %p", [NSObject class]);
[Person test];
// objc_msgSend([Person class], @selector(test))
// isa -> superclass -> suerpclass -> superclass -> .... superclass == nil
[NSObject test];
//objc_msgSend([NSObject class], @selector(test))
}
return 0;
}
3贱傀、ISA_MASK
- 從64bit開始惨撇,isa需要進(jìn)行一次位運(yùn)算府寒,才能計(jì)算出真實(shí)地址
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# elif __x86_64__
# define ISA_MASK 0x00007ffffffffff8ULL
# endif
4、objc_class結(jié)構(gòu)
objc4源碼: https://opensource.apple.com/tarballs/objc4/
struct objc_object {
private:
isa_t isa;
};
struct objc_class : objc_object {
// Class ISA;
Class superclass;
cache_t cache; // 方法緩存
class_data_bits_t bits; // 用于獲取類的信息
class_rw_t *data() {
return bits.data();
}
};
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint32_t version;
const class_ro_t *ro;
method_array_t methods; //方法列表
property_array_t properties; //屬性列表
protocol_array_t protocols; //協(xié)議列表
Class firstSubclass;
Class nextSiblingClass;
char *demangledName;
};
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize; //instance對(duì)象占用的內(nèi)存空間
#ifdef __LP64__
uint32_t reserved;
#endif
const uint8_t * ivarLayout;
const char * name; //類名
method_list_t * baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars;//成員變量列表
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
};
5剖淀、KVO
KVO的全稱是Key-Value Observing祷蝌,俗稱“鍵值監(jiān)聽”帆卓,可以用于監(jiān)聽某個(gè)對(duì)象屬性值的改變
例如下類
@interface Person : NSObject
@property (assign, nonatomic) int age;
@end
@implementation Person
@end
5.1 未使用KVO監(jiān)聽的對(duì)象
5.2 使用KVO監(jiān)聽的對(duì)象
- _NSSet*ValueAndNotify的內(nèi)部實(shí)現(xiàn)
- (void)setAge:(int)age {
[self willChangeValueForKey:@"age"];
[super setAge:age];
[self didChangeValueForKey:@"age"];
}
調(diào)用步驟
- 調(diào)用
willChangeValueForKey:
- 調(diào)用原來的
setter
實(shí)現(xiàn) - 調(diào)用
didChangeValueForKey:
-
didChangeValueForKey:
內(nèi)部會(huì)調(diào)用observer的observeValueForKeyPath:ofObject:change:context
5.3 Observer存在哪里
可通過GNUstep-Foundation源碼查看源碼觀察實(shí)現(xiàn)原理
5.4 KVO代碼例子
#import "ViewController.h"
#import <objc/runtime.h>
@interface ViewController ()
@property (strong, nonatomic) Person *person1;
@property (strong, nonatomic) Person *person2;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.person1 = [[Person alloc] init];
self.person1.age = 1;
self.person2 = [[Person alloc] init];
self.person2.age = 2;
// 給person1對(duì)象添加KVO監(jiān)聽
NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
[self.person1 addObserver:self forKeyPath:@"age" options:options context:@"123"];
NSLog(@"person1.class == %@", object_getClass(self.person1)) ;
NSLog(@"person2.class == %@", object_getClass(self.person2)) ;
[self printMethodNamesOfClass:object_getClass(self.person1)];
[self printMethodNamesOfClass:object_getClass(self.person2)];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
self.person1.age = 20;
self.person2.age = 20;
}
- (void)dealloc {
[self.person1 removeObserver:self forKeyPath:@"age"];
[self.person1 removeObserver:self forKeyPath:@"height"];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
NSLog(@"監(jiān)聽到%@的%@屬性值改變了 - %@ - %@", object, keyPath, change, context);
}
- (void)printMethodNamesOfClass:(Class)cls {
unsigned int count;
Method *methodList = class_copyMethodList(cls, &count);
NSMutableString *methodNames = [NSMutableString string];
for (int i = 0; i < count; i++) {
Method method = methodList[i];
NSString *methodName = NSStringFromSelector(method_getName(method));
[methodNames appendString:methodName];
[methodNames appendString:@", "];
}
free(methodList);
NSLog(@"%@ %@", cls, methodNames);
}
@end
6、KVC
KVC的全稱是Key-Value Coding米丘,俗稱“鍵值編碼”剑令,可以通過一個(gè)key來訪問某個(gè)屬性
- 常見api
- (void)setValue:(id)value forKeyPath:(NSString *)keyPath;
- (void)setValue:(id)value forKey:(NSString *)key;
- (id)valueForKeyPath:(NSString *)keyPath;
- (id)valueForKey:(NSString *)key;
- setValue:forKey:的原理
accessInstanceVariablesDirectly方法的默認(rèn)返回值是YES
-
valueForKey:的原理
ValueforKey原理.jpg
7、Category
7.1 Category底層結(jié)構(gòu)
定義在objc-runtime-new.h中
struct category_t {
const char *name;
classref_t cls;
struct method_list_t *instanceMethods;
struct method_list_t *classMethods;
struct protocol_list_t *protocols;
struct property_list_t *instanceProperties;
// Fields below this point are not always present on disk.
struct property_list_t *_classProperties;
method_list_t *methodsForMeta(bool isMeta) {
if (isMeta) return classMethods;
else return instanceMethods;
}
property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
};
7.1 Category的加載處理過程
- 通過Runtime加載某個(gè)類的所有Category數(shù)據(jù)
- 把所有Category的方法拄查、屬性吁津、協(xié)議數(shù)據(jù),合并到一個(gè)大數(shù)組中, 后面參與編譯的Category數(shù)據(jù)曙博,會(huì)在數(shù)組的前面
- 將合并后的分類數(shù)據(jù)(方法簸搞、屬性、協(xié)議)鸣奔,插入到類原來數(shù)據(jù)的前面
源碼解讀順序
//objc-os.mm
_objc_init
map_images
map_images_nolock
//objc-runtime-new.mm
_read_images
remethodizeClass
attachCategories
attachLists
realloc典尾、memmove役拴、memcpy
7.2 +load方法
- +load方法會(huì)在runtime加載類、分類時(shí)調(diào)用
- 每個(gè)類钾埂、分類的+load河闰,在程序運(yùn)行過程中只調(diào)用一次.
- 先調(diào)用類的+load
- 按照編譯先后順序調(diào)用(先編譯,先調(diào)用)
- 調(diào)用子類的+load之前會(huì)先調(diào)用父類的+load
- 再調(diào)用分類的+load
- 按照編譯先后順序調(diào)用(先編譯褥紫,先調(diào)用)
objc4源碼解讀過程
//objc-os.mm
_objc_init
load_images
prepare_load_methods
schedule_class_load
add_class_to_loadable_list
add_category_to_loadable_list
call_load_methods
call_class_loads
call_category_loads
(*load_method)(cls, SEL_load)
+load方法是根據(jù)方法地址直接調(diào)用姜性,并不是經(jīng)過objc_msgSend函數(shù)調(diào)用
7.3 +initialize方法
+initialize方法會(huì)在類第一次接收到消息時(shí)調(diào)用
先調(diào)用父類的+initialize,再調(diào)用子類的+initialize
(先初始化父類印机,再初始化子類,每個(gè)類只會(huì)初始化1次)+initialize是通過objc_msgSend進(jìn)行調(diào)用的楣责,所以有以下特點(diǎn)
- 如果子類沒有實(shí)現(xiàn)+initialize,會(huì)調(diào)用父類的+initialize(所以父類的+initialize可能會(huì)被調(diào)用多次)
- 如果分類實(shí)現(xiàn)了+initialize沮趣,就覆蓋類本身的+initialize調(diào)用
objc4源碼解讀過程
//objc-msg-arm64.s
objc_msgSend
//objc-runtime-new.mm
class_getInstanceMethod
lookUpImpOrNil
lookUpImpOrForward
_class_initialize
callInitialize
objc_msgSend(cls, SEL_initialize)
7.4 +load方法、+initialize方法區(qū)別
- 調(diào)用方式
- load是根據(jù)函數(shù)地址直接調(diào)用
- initialize是通過objc_msgSend調(diào)用
- 調(diào)用時(shí)刻
- load是runtime加載類、分類的時(shí)候調(diào)用(只會(huì)調(diào)用1次)
- initialize是類第一次接收到消息的時(shí)候調(diào)用凌蔬,每一個(gè)類只會(huì)initialize一次(父類的initialize方法可能會(huì)被調(diào)用多次
- load調(diào)用順序
- 先調(diào)用類的load, 先編譯的類, 優(yōu)先調(diào)用load,調(diào)用子類的load之前, 會(huì)先調(diào)用父類的load
- 再調(diào)用分類的load, 先編譯的分類懈词,優(yōu)先調(diào)用load
- initialize調(diào)用順序
- 先初始化父類
- 再初始化子類(可能最終調(diào)用的是父類的initialize方法)
7.5 給分類"添加成員變量"
默認(rèn)情況下,因?yàn)榉诸惖讓咏Y(jié)構(gòu)的限制,不能添加成員變量到分類中褐桌。但可以通過關(guān)聯(lián)對(duì)象來間接實(shí)現(xiàn)
7.5.1 關(guān)聯(lián)對(duì)象API
//添加關(guān)聯(lián)對(duì)象
void objc_setAssociatedObject(id object, const void * key,
id value, objc_AssociationPolicy policy)
//獲得關(guān)聯(lián)對(duì)象
id objc_getAssociatedObject(id object, const void * key)
//移除所有的關(guān)聯(lián)對(duì)象
void objc_removeAssociatedObjects(id object)
7.5.2 key的常見用法
static void *MyKey = &MyKey;
objc_setAssociatedObject(obj, MyKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
objc_getAssociatedObject(obj, MyKey)
static char MyKey;
objc_setAssociatedObject(obj, &MyKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
objc_getAssociatedObject(obj, &MyKey)
//使用屬性名作為key
objc_setAssociatedObject(obj, @"property", value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_getAssociatedObject(obj, @"property");
//使用get方法的@selecor作為key
objc_setAssociatedObject(obj, @selector(getter), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
objc_getAssociatedObject(obj, @selector(getter))
7.5.3 objc_AssociationPolicy
objc_AssociationPolicy | 對(duì)應(yīng)的修飾符 |
---|---|
OBJC_ASSOCIATION_ASSIGN | assign |
OBJC_ASSOCIATION_RETAIN_NONATOMIC | strong, nonatomic |
OBJC_ASSOCIATION_COPY_NONATOMIC | copy, nonatomic |
OBJC_ASSOCIATION_RETAIN | strong, atomic |
OBJC_ASSOCIATION_COPY | copy, atomic |
7.5.4 關(guān)聯(lián)對(duì)象的原理
實(shí)現(xiàn)關(guān)聯(lián)對(duì)象技術(shù)的核心對(duì)象有
- AssociationsManager
- AssociationsHashMap
- ObjectAssociationMap
- ObjcAssociation
objc4源碼解讀:objc-references.mm
class AssociationsManager {
static AssociationsHashMap *_map;
};
class AssociationsHashMap : public unordered_map<disguised_ptr_t, ObjectAssociationMap *, DisguisedPointerHash, DisguisedPointerEqual, AssociationsHashMapAllocator>
class ObjectAssociationMap : public std::map<void *, ObjcAssociation, ObjectPointerLess, ObjectAssociationMapAllocator>
class ObjcAssociation {
uintptr_t _policy;
id _value;
};
- 關(guān)聯(lián)對(duì)象并不是存儲(chǔ)在被關(guān)聯(lián)對(duì)象本身內(nèi)存中
- 關(guān)聯(lián)對(duì)象存儲(chǔ)在全局的統(tǒng)一的一個(gè)
AssociationsManager
中 - 設(shè)置關(guān)聯(lián)對(duì)象為nil啦撮,就相當(dāng)于是移除關(guān)聯(lián)對(duì)象
8、Block
8.1 block的本質(zhì)
- block本質(zhì)上也是一個(gè)OC對(duì)象织中,它內(nèi)部也有個(gè)isa指針
- block是封裝了函數(shù)調(diào)用以及函數(shù)調(diào)用環(huán)境的OC對(duì)象
-
block的底層結(jié)構(gòu)
Block本質(zhì).jpg
8.2 block的變量捕獲
為了保證block內(nèi)部能夠正常訪問外部的變量,block有個(gè)變量捕獲機(jī)制
int global_var = 10;//全局變量
static int static_global_var = 10;//靜態(tài)全局變量
void (^block)(void);
void test() {
auto int local_var = 10;//局部變量
static int static_local_var = 10;//靜態(tài)局部變量
block = ^{
NSLog(@"%d, %d, %d, %d", global_var, static_global_var, local_var, static_local_var);
};
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
test();
}
return 0;
}
轉(zhuǎn)換為C++代碼
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
static struct __test_block_desc_0 {
size_t reserved;
size_t Block_size;
}
int global_var = 10; //全局變量
static int static_global_var = 10; //靜態(tài)全局變量
void (*block)(void);
struct __test_block_impl_0 {
struct __block_impl impl;
struct __test_block_desc_0* Desc;
int local_var; //捕獲的局部變量
int *static_local_var; //捕獲的靜態(tài)局部變量
__test_block_impl_0(void *fp, struct __test_block_desc_0 *desc, int _local_var, int *_static_local_var, int flags=0) : local_var(_local_var), static_local_var(_static_local_var) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
8.3 block的類型
block有3種類型,可以通過調(diào)用class方法或者isa指針查看具體類型疲吸,最終都是繼承自NSBlock類型
- __NSGlobalBlock__ ( _NSConcreteGlobalBlock )
- __NSStackBlock__ ( _NSConcreteStackBlock )
- __NSMallocBlock__ ( _NSConcreteMallocBlock )
- 每一種類型的block調(diào)用copy后的結(jié)果如下所示
在ARC環(huán)境下,編譯器會(huì)根據(jù)情況自動(dòng)將棧上的block復(fù)制到堆上,比如以下情況
- block作為函數(shù)返回值時(shí)
- 將block賦值給
__strong
指針時(shí) - block作為Cocoa API中方法名含有usingBlock的方法參數(shù)時(shí)
- block作為GCD API的方法參數(shù)時(shí)
8.4 對(duì)象類型的auto變量
當(dāng)block內(nèi)部訪問了對(duì)象類型的auto變量時(shí), 如果block是在棧上,將不會(huì)對(duì)auto變量產(chǎn)生強(qiáng)引用
如果block被拷貝到堆上,會(huì)調(diào)用block內(nèi)部的
copy
函數(shù),copy
函數(shù)內(nèi)部會(huì)調(diào)用_Block_object_assign
函數(shù),_Block_object_assign
函數(shù)會(huì)根據(jù)auto變量的修飾符(__strong
窃祝、__weak
、__unsafe_unretained
)做出相應(yīng)的操作探膊,形成強(qiáng)引用(retain
)或者弱引用。如果block從堆上移除, 會(huì)調(diào)用block內(nèi)部的
dispose
函數(shù),dispose
函數(shù)內(nèi)部會(huì)調(diào)用_Block_object_dispose
函數(shù)
_Block_object_dispose
函數(shù)會(huì)自動(dòng)釋放引用的auto
變量(release
)腌闯。
例子
//MRC環(huán)境下測試
@interface Person : NSObject
@property (assign, nonatomic) int age;
@end
@implementation Person
- (void)dealloc {
NSLog(@"Person - dealloc");
[super dealloc];
}
@end
1、如果block是在棧上分瘦,將不會(huì)對(duì)auto變量產(chǎn)生強(qiáng)引用
//MRC環(huán)境下測試
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);
};//棧block
[person release];
}
NSLog(@"---------%@", block);
NSLog(@"---end---");
}
return 0;
}
轉(zhuǎn)換為C++代碼如下
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;//棧Block
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
2 如果block是在堆上, 根據(jù)auto變量的__strong
翁潘、__weak
渗勘、__unsafe_unretained
形成強(qiáng)引用或者弱引用
strong
//ARC環(huán)境下測試
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);
};
block();
}
NSLog(@"---------%@", block);
NSLog(@"---end---");
}
return 0;
}
轉(zhuǎn)化為C++代碼
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
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;//堆block
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
__weak
//ARC環(huán)境下測試
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", weakPerson.age);
};
block();
}
NSLog(@"---------%@", block);
NSLog(@"---end---");
}
return 0;
}
轉(zhuǎn)化為C++代碼
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
Person *__weak weakPerson; //weak指針
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, Person *__weak _weakPerson, int flags=0) : weakPerson(_weakPerson) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
8.5 __block修飾符
8.5.1 __block原理
-
__block
可以用于解決block內(nèi)部無法修改auto變量值的問題 -
__block
不能修飾全局變量取刃、靜態(tài)變量(static) - 編譯器會(huì)將
__block
變量包裝成一個(gè)對(duì)象
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_age_0 *age; // by ref
};
struct __Block_byref_age_0 {
void *__isa;
__Block_byref_age_0 *__forwarding;
int __flags;
int __size;
int age;
};
8.5.2 __block的內(nèi)存管理
1坯辩、當(dāng)block在棧上時(shí)漆魔,并不會(huì)對(duì)__block變量產(chǎn)生強(qiáng)引用。
2阿纤、當(dāng)block被copy到堆時(shí)
- 會(huì)調(diào)用block內(nèi)部的copy函數(shù)
-
copy
函數(shù)內(nèi)部會(huì)調(diào)用_Block_object_assign
函數(shù) -
_Block_object_assign
函數(shù)會(huì)對(duì)__block
變量形成強(qiáng)引用(retain
)
3、當(dāng)block從堆中移除時(shí)
- 會(huì)調(diào)用block內(nèi)部的
dispose
函數(shù) -
dispose
函數(shù)內(nèi)部會(huì)調(diào)用_Block_object_dispose
函數(shù) -
_Block_object_dispose
函數(shù)會(huì)自動(dòng)釋放引用的__block
變量(release
)
4、__block
的__forwarding
指針
5、對(duì)象類型的auto變量东揣、__block變量
當(dāng)block在棧上時(shí)嘶卧,對(duì)它們都不會(huì)產(chǎn)生強(qiáng)引用
當(dāng)block拷貝到堆上時(shí),都會(huì)通過copy函數(shù)來處理它們
__block
變量(假設(shè)變量名叫做a)
_Block_object_assign((void*)&dst->a, (void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);
對(duì)象類型的auto變量(假設(shè)變量名叫做p)
_Block_object_assign((void*)&dst->p, (void*)src->p, 3/*BLOCK_FIELD_IS_OBJECT*/);
當(dāng)block從堆上移除時(shí),都會(huì)通過dispose函數(shù)來釋放它們
__block
變量(假設(shè)變量名叫做a)
_Block_object_dispose((void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);
對(duì)象類型的auto變量(假設(shè)變量名叫做p)
_Block_object_dispose((void*)src->p, 3/*BLOCK_FIELD_IS_OBJECT*/);
8.6 Block的循環(huán)引用問題
ARC下
使用__weak
、__unsafe_unretained
解決循環(huán)引用問題
__weak
蒙袍、__unsafe_unretained
區(qū)別在于__weak
在對(duì)象釋放時(shí)會(huì)自動(dòng)置nil,而__unsafe_unretained
不會(huì)置nil瘾蛋。
__weak typeof(self) weakSelf = self;
self.block = ^{
NSLog(@"%p", weakSelf);
};
使用__block
解決循環(huán)引用問題,必須調(diào)用Block
__block id weakSelf = self;
self.block = ^{
NSLog(@"%p", weakSelf);
weakSelf = nil;
};
self.block();
MRC下
用__unsafe_unretained
解決循環(huán)引用問題
__unsafe_unretained id weakSelf = self;
self.block = ^{
NSLog(@"%p", weakSelf);
};
用__block
解決循環(huán)引用問題,可以不用調(diào)用Block,
__block id weakSelf = self;
self.block = ^{
NSLog(@"%p", weakSelf);
};