内存管理
1、weak的实现原理?SideTable的结构是什么样的
避免出现对象之间的强强引用而造成对象不能被正常释放最终导致内存泄露的问题。weak 关键字的作用是弱引用,所引用对象的计数器不会加1,并在引用对象被释放的时候自动被设置为 nil。
/**
* The global weak references table. Stores object ids as keys,
* and weak_entry_t structs as their values.
*/
//weak_table_t全局的弱引用表,把对象的id作为key,weak_entry_t 是对应的value
struct weak_table_t {
weak_entry_t *weak_entries; //保存了所有被weak指向的对象
size_t num_entries; //weak对象的存储空间
uintptr_t mask;
uintptr_t max_hash_displacement;
};
//weak_entry_t 它负责维护和存储指向一个对象的所有弱引用hash表。
struct weak_entry_t {
DisguisedPtr<objc_object> referent;
union {
struct {
weak_referrer_t *referrers; //hash表,下标索引作为key,value是指向对象的弱引用
uintptr_t out_of_line_ness : 2;
uintptr_t num_refs : PTR_MINUS_2; //引用计数
uintptr_t mask;
uintptr_t max_hash_displacement;
};
struct {
// out_of_line_ness field is low bits of inline_referrers[1]
weak_referrer_t inline_referrers[WEAK_INLINE_COUNT];
};
};
}
总结:
1、weak_table_t是一个全局表,采用hash表的方式实现,保存了所有被weak指向的对象
2、weak_entry_t也是一个hash表,保存的是"某个对象"和指向该对象的所有弱引用对象
//SideTable 这个结构体,是对weak_table_t表的再次封装操作,避免对weak_table_t直接操作,SideTable使用更加方便
struct SideTable {
spinlock_t slock;
RefcountMap refcnts; //refcnts是一个存放着对象引用计数的散列表
weak_table_t weak_table;
}
//有弱引用或者sideTable有引用计数
objc_object::clearDeallocating()
{
if (slowpath(!isa.nonpointer)) {
// Slow path for raw pointer isa.
sidetable_clearDeallocating();
}
else if (slowpath(isa.weakly_referenced || isa.has_sidetable_rc)) {
// Slow path for non-pointer isa with weak refs and/or side table data.
clearDeallocating_slow();
}
assert(!sidetable_present());
}
NEVER_INLINE void
objc_object::clearDeallocating_slow()
{
SideTable& table = SideTables()[this];
table.lock();
if (isa.weakly_referenced) {
weak_clear_no_lock(&table.weak_table, (id)this);
}
if (isa.has_sidetable_rc) {
table.refcnts.erase(this);
}
table.unlock();
}
//返回当前对象的弱引用表
static weak_entry_t *
weak_entry_for_referent(weak_table_t *weak_table, objc_object *referent)
{
assert(referent);
weak_entry_t *weak_entries = weak_table->weak_entries; //获取全局的弱引用表所有对象
if (!weak_entries) return nil;
size_t begin = hash_pointer(referent) & weak_table->mask; //获取对象在hash表中的索引
size_t index = begin;
size_t hash_displacement = 0;
while (weak_table->weak_entries[index].referent != referent) { //如果当前索引对应的不是当前对象
index = (index+1) & weak_table->mask; //索引+1
if (index == begin) bad_weak_table(weak_table->weak_entries);
hash_displacement++; //hash移位 +1
if (hash_displacement > weak_table->max_hash_displacement) {
return nil; //如果遍历了一遍还没有找到,返回nil
}
}
//返回弱引用表中当前对象对应的weak_entry_t
return &weak_table->weak_entries[index];
}
void
weak_clear_no_lock(weak_table_t *weak_table, id referent_id)
{
objc_object *referent = (objc_object *)referent_id; //正在释放的对象
weak_entry_t *entry = weak_entry_for_referent(weak_table, referent);//返回弱引用表中当前对象对应的weak_entry_t
if (entry == nil) {
/// XXX shouldn't happen, but does with mismatched CF/objc
//printf("XXX no entry for clear deallocating %p\n", referent);
return;
}
// zero out references
weak_referrer_t *referrers; //一个对象的引用
size_t count;
if (entry->out_of_line()) {
referrers = entry->referrers;
count = TABLE_SIZE(entry); //hash表中的如果扩充过,count为mask+1
}
else {
referrers = entry->inline_referrers;
count = WEAK_INLINE_COUNT; //默认为4
}
for (size_t i = 0; i < count; ++i) {
objc_object **referrer = referrers[i]; //遍历当前对象指向的弱引用
if (referrer) {
if (*referrer == referent) { //如果指向当前对象的引用,指向的是当前对象,就将弱引用置为nil
*referrer = nil;
}
else if (*referrer) {
_objc_inform("__weak variable at %p holds %p instead of %p. "
"This is probably incorrect use of "
"objc_storeWeak() and objc_loadWeak(). "
"Break on objc_weak_error to debug.\n",
referrer, (void*)*referrer, (void*)referent);
objc_weak_error();
}
}
}
weak_entry_remove(weak_table, entry);
}
总结:
将那些弱引用存在hashTable里面,到时候对象要销毁,就会取出当前对象对应的弱引用表,把弱引用表里面的那些弱引用都给清除掉。
2、关联对象的应用?系统如何实现关联对象的
@interface UIView (Theme)
@property (nonatomic, copy) NSDictionary *themeMap;
@end
- (void)setThemeMap:(NSDictionary *)themeMap
{
objc_setAssociatedObject(self, &kUIView_ThemeMap, themeMap, OBJC_ASSOCIATION_COPY_NONATOMIC);
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(themeChanged) name:kThemeDidChangeNotification object:nil];
}
- (void)themeChanged
{
//获取存储的属性
NSDictionary *map = objc_getAssociatedObject(self, &kUIView_ThemeMap);
if ([self isKindOfClass:[UIButton class]]) {
UIButton *obj = (UIButton *)self;
if (map[kThemeMapKeyColorName]) {
[obj setTitleColor:TColor(map[kThemeMapKeyColorName]) forState:UIControlStateNormal];
}
}
}
doneBtn.themeMap = @{kThemeMapKeyColorName : @"main_theme_color"}; //main_theme_color对应具体的颜色,可通过TColor(map[kThemeMapKeyColorName]转换成UIColor
关联对象的主要应用是为UIView的分类添加属性,比如上面的主题管理为UIButton添加背景色属性,title颜色等属性设置到字典里,切换主题接收到通知,获取设置的属性。
以下我们从添加关联对象、获得关联对象、移除关联对象3个部分的源码,对关联对象有个更清楚的认识。
先看一张关联对象原理的结构图,再找到关联对象的源码进行解读。
2.1 、添加关联对象 _object_set_associative_reference
void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
if (!object && !value) return;
// retain the new value (if any) outside the lock.
ObjcAssociation old_association(0, nil);
id new_value = value ? acquireValue(value, policy) : nil; //根据policy对value进行持有(copy、retain...)
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
disguised_ptr_t disguised_object = DISGUISE(object); //把object转换为disguised_ptr_t
if (new_value) {
// break any existing association.
AssociationsHashMap::iterator i = associations.find(disguised_object);//在_map中查找disguised_object,得到AssociationsHashMap对应的索引
if (i != associations.end()) { //之前对object建立过关联关系,存在索引
// secondary table exists
ObjectAssociationMap *refs = i->second;//拿出disguised_objec对应的hashMap(ObjectAssociationMap)
ObjectAssociationMap::iterator j = refs->find(key);//通过key取出ObjectAssociationMap对应的索引
if (j != refs->end()) { //存在key对应的值
old_association = j->second;
j->second = ObjcAssociation(policy, new_value); //之前的key存在对应的ObjcAssociation,更新数据,并赋值给ObjcAssociation
} else {
(*refs)[key] = ObjcAssociation(policy, new_value);//不存在则创建对应关系
}
} else {//第一次object加入关联对象
// create the new association (first time).
ObjectAssociationMap *refs = new ObjectAssociationMap; //创建一个ObjectAssociationMap
associations[disguised_object] = refs; //把对象的disguised_object作为key,ObjectAssociationMap作为value
(*refs)[key] = ObjcAssociation(policy, new_value);//新传入的key作为ObjectAssociationMap的key,传入的value和policy创建的对象作为value
object->setHasAssociatedObjects();//has_assoc属性设置为true
}
} else { //value 为nil
// setting the association to nil breaks the association.
AssociationsHashMap::iterator i = associations.find(disguised_object); //找到disguised_object对应的AssociationsHashMap
if (i != associations.end()) {
ObjectAssociationMap *refs = i->second; //找到ObjectAssociationMap
ObjectAssociationMap::iterator j = refs->find(key); //找到key对应的hash table的索引
if (j != refs->end()) {
old_association = j->second; //拿到旧值,用于释放对旧value的释放与acquireValue(对value进行retain、copy引用计数+1的操作)对应
refs->erase(j); //在ObjectAssociationMap 删除下标索引为j的元素
}
}
}
}
// release the old value (outside of the lock).
if (old_association.hasValue()) ReleaseValue()(old_association); //释放旧值
}
//根据传入的policy对value进行强引用
static id acquireValue(id value, uintptr_t policy) {
switch (policy & 0xFF) {
case OBJC_ASSOCIATION_SETTER_RETAIN:
return objc_retain(value);
case OBJC_ASSOCIATION_SETTER_COPY:
return ((id(*)(id, SEL))objc_msgSend)(value, SEL_copy);
}
return value;
}
2.2 、获得关联对象 _object_get_associative_reference
id _object_get_associative_reference(id object, void *key) {
id value = nil;
uintptr_t policy = OBJC_ASSOCIATION_ASSIGN;
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
disguised_ptr_t disguised_object = DISGUISE(object);
AssociationsHashMap::iterator i = associations.find(disguised_object); //获取AssociationsHashMap对应的索引
if (i != associations.end()) {
ObjectAssociationMap *refs = i->second;//拿到ObjectAssociationMap列表
ObjectAssociationMap::iterator j = refs->find(key); //在hash table 中找到key对应的索引
if (j != refs->end()) {
ObjcAssociation &entry = j->second; //取出对应的ObjcAssociation
value = entry.value();
policy = entry.policy();
if (policy & OBJC_ASSOCIATION_GETTER_RETAIN) {
objc_retain(value); //value的引用计数+1
}
}
}
}
if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {
objc_autorelease(value);
}
return value;
}
2.3 、移除关联对象 objc_removeAssociatedObjects
void _object_remove_assocations(id object) {
vector< ObjcAssociation,ObjcAllocator<ObjcAssociation> > elements; //创建一个数组
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
if (associations.size() == 0) return;
disguised_ptr_t disguised_object = DISGUISE(object);
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
// copy all of the associations that need to be removed.
ObjectAssociationMap *refs = i->second; //拿到ObjectAssociationMap整个hash table
for (ObjectAssociationMap::iterator j = refs->begin(), end = refs->end(); j != end; ++j) {
elements.push_back(j->second); //把整个hash table里面的ObjcAssociation对象全加到数组中
}
// remove the secondary table.
delete refs; //删除指针disguised_object对应的ObjectAssociationMap
associations.erase(i); //删除 disguised_object 在_map中的那一行数据
}
}
// the calls to releaseValue() happen outside of the lock.
for_each(elements.begin(), elements.end(), ReleaseValue()); //逐个删除elements数组中的ObjcAssociation对象
}
static void releaseValue(id value, uintptr_t policy) {
if (policy & OBJC_ASSOCIATION_SETTER_RETAIN) {
return objc_release(value);
}
}
struct ReleaseValue {
void operator() (ObjcAssociation &association) {
releaseValue(association.value(), association.policy());
}
};
//根据传入的policy对value进行强引用
static id acquireValue(id value, uintptr_t policy) {
switch (policy & 0xFF) {
case OBJC_ASSOCIATION_SETTER_RETAIN:
return objc_retain(value);
case OBJC_ASSOCIATION_SETTER_COPY:
return ((id(*)(id, SEL))objc_msgSend)(value, SEL_copy);
}
return value;
}
我们发现对value的内存管理只有RETAIN和COPY而为什么没有weak呢?
如果没有对ObjcAssociation中的value进行强引用,可能会存在设置的时候有值,取的时候就已经释放了。导致取值可能取出来的为nil。policy 取 OBJC_ASSOCIATION_ASSIGN也会存在类似的问题,value释放了,OBJC_ASSOCIATION_ASSIGN里面的实现不是弱引用,再去访问会出现坏内存访问问题。
3、关联对象的如何进行内存管理的?关联对象如何实现weak属性,就是对象释放时怎么置为nil,而不是野指针?
3.1、关联对象的如何进行内存管理的?
首先从源码里面看下对象释放的时候,调用dealloc的过程,在这个函数_object_remove_assocations里面会把对象地址对应在AssociationsHashMap中的数据进行清空。
- (void)dealloc {
_objc_rootDealloc(self);
}
_objc_rootDealloc(id obj)
{
obj->rootDealloc();
}
objc_object::rootDealloc()
{
if (isTaggedPointer()) return; // fixme necessary?
if (fastpath(isa.nonpointer &&
!isa.weakly_referenced &&
!isa.has_assoc &&
!isa.has_cxx_dtor &&
!isa.has_sidetable_rc))
{
assert(!sidetable_present());
free(this);
}
else {
object_dispose((id)this);
}
}
id
object_dispose(id obj)
{
if (!obj) return nil;
objc_destructInstance(obj);
free(obj);
return nil;
}
void *objc_destructInstance(id obj)
{
if (obj) {
// Read all of the flags at once for performance.
bool cxx = obj->hasCxxDtor();
bool assoc = obj->hasAssociatedObjects();
// This order is important.
if (cxx) object_cxxDestruct(obj);
if (assoc) _object_remove_assocations(obj);
obj->clearDeallocating();
}
return obj;
}
acquireValue函数、releaseValue函数会对传入的value根据传入policy,进行内存的管理。
3.2、关联对象如何实现weak属性?
实现
weak引用(而非assign引用)的前提是存在一个__weak指针指向到被引用对象的地址,只有这样,当对象被销毁时,指针才能被runtime找到然后被设置为nil;NSObject对象和其associated object关联对象的关系,并不存在指针这样的中间媒介,因此只存在OBJC_ASSOCIATION_ASSIGN选项,而不存在OBJC_ASSOCIATION_WEAK选项。
关联对象如何实现weak属性?
可以通过曲线救国的方式声明一个class类 持有一个weak的成员变量,然后通过 实例化 我们自定义的class的实例,然后把这个实例作为关联对象即可。
@interface WeakAssociatedObjectWrapper : NSObject
@property (nonatomic, weak) id object;
@end
@implementation WeakAssociatedObjectWrapper
@end
@interface UIView (ViewController)
@property (nonatomic, weak) UIViewController *vc;
@end
@implementation UIView (ViewController)
- (void)setVc:(UIViewController *)vc {
WeakAssociatedObjectWrapper *wrapper = [WeakAssociatedObjectWrapper new];
wrapper.object = vc;
objc_setAssociatedObject(self, @selector(vc), wrapper, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (UIViewController *)vc {
WeakAssociatedObjectWrapper *wrapper = objc_getAssociatedObject(self, _cmd);
return wrapper.object;
}
@end
4、Autoreleasepool的原理?所使用的的数据结构是什么?
AutoreleasePool是OC中的一种内存自动回收机制,在正常情况下,创建的变量会在超出其作用域的时候release,但是如果将变量加入AutoreleasePool,那么release将延迟执行。
4.1、Autoreleasepool的OC代码重写成C++的实现
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"Hello, World!");
}
return 0;
}
//在终端中使用clang -rewrite-objc命令将上面的OC代码重写成C++的实现
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; //结构体开始调用构造函数,结束调用析构函数
NSLog((NSString *)&__NSConstantStringImpl__var_folders_kb_06b822gn59df4d1zt99361xw0000gn_T_main_d39a79_mi_0);
}
return 0;
}
struct __AtAutoreleasePool {
__AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
void * atautoreleasepoolobj;
};
4.2、综上所述,每一个 @autoreleasepool,会转换成objc_autoreleasePoolPush()和objc_autoreleasePoolPop()函数。
@autoreleasepool {
Person *person = [Person new];
@autoreleasepool {
Person *person2 = [Person new];
}
}
实际上会是下面这样
@autoreleasepool {
atautoreleasepoolobj = objc_autoreleasePoolPush();
Person *person = [Person new];
@autoreleasepool {
atautoreleasepoolobj2 = objc_autoreleasePoolPush();
Person *person2 = [Person new];
objc_autoreleasePoolPop(atautoreleasepoolobj2);
}
objc_autoreleasePoolPop(atautoreleasepoolobj);
}
4.3、自动释放池的主要底层数据结构是:__AtAutoreleasePool、AutoreleasePoolPage。
调用了autorelease的对象最终都是通过AutoreleasePoolPage对象来管理的,每个AutoreleasePoolPage对象占用4096*4字节内存,除了用来存放它内部的成员变量,剩下的空间用来存放autorelease对象的地址。
class AutoreleasePoolPage
{
magic_t const magic;
id *next;
pthread_t const thread;
AutoreleasePoolPage * const parent;
AutoreleasePoolPage *child;
uint32_t const depth;
uint32_t hiwat;
}
id * begin() {
return (id *) ((uint8_t *)this+sizeof(*this)); //this一共56个字节
}
id * end() {
return (id *) ((uint8_t *)this+SIZE); //最新的size是4096*4个字节
}
4.4、AutoreleasePool并没有单独的结构,而是由若干个AutoreleasePoolPage以双向链表的形式组合而成的栈结构。入栈的时候返回一个boundary,出栈的时候传入boundary,对双向链表中的对象逐个进行release操作。
# define POOL_BOUNDARY nil
static inline void *push()
{
id *dest;
if (DebugPoolAllocation) {
// Each autorelease pool starts on a new pool page.
dest = autoreleaseNewPage(POOL_BOUNDARY); //一个新的pool page会入栈一个POOL_BOUNDARY
} else {
dest = autoreleaseFast(POOL_BOUNDARY);
}
assert(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY);
return dest;
}
//stop就是传入的POOL_BOUNDARY
void releaseUntil(id *stop)
{
while (this->next != stop) {
// Restart from hotPage() every time, in case -release
// autoreleased more objects
AutoreleasePoolPage *page = hotPage();
// fixme I think this `while` can be `if`, but I can't prove it
while (page->empty()) {
page = page->parent;
setHotPage(page);
}
page->unprotect();
id obj = *--page->next;
memset((void*)page->next, SCRIBBLE, sizeof(*page->next));
page->protect();
if (obj != POOL_BOUNDARY) {
objc_release(obj);
}
}
setHotPage(this);
}
static inline id *autoreleaseFast(id obj)
{
AutoreleasePoolPage *page = hotPage();
if (page && !page->full()) {
return page->add(obj); //把调用autorelease的对象加入到page中
} else if (page) {
return autoreleaseFullPage(obj, page);
} else {
return autoreleaseNoPage(obj);
}
}
4.5、调用autorelease的对象什么时候释放?是由RunLoop来控制的,可能会在自己所处那次runloop循环休眠之前调用release。
NSLog(@"main-loop - %@",[NSRunLoop currentRunLoop]); //在主线程打印,结果如下,0x6000007443c0、0x600000744460这两个CFRunLoopObserver是监听AutoreleasePool的状态
0x6000007443c0 监听的状态 activities = 0x1 //kCFRunLoopEntry
0x600000744460 监听的状态 activities = 0xa0 //160 128+32 =kCFRunLoopBeforeWaiting & kCFRunLoopExit
/* Run Loop Observer Activities */
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0), //1 (进入)
kCFRunLoopBeforeTimers = (1UL << 1),//2
kCFRunLoopBeforeSources = (1UL << 2),//4
kCFRunLoopBeforeWaiting = (1UL << 5), //32 (休眠之前)
kCFRunLoopAfterWaiting = (1UL << 6), //64
kCFRunLoopExit = (1UL << 7), //128 (退出)
kCFRunLoopAllActivities = 0x0FFFFFFFU
};
observers = (
"<CFRunLoopObserver 0x6000007443c0 [0x7fff805eff70]>{valid = Yes, activities = 0x1, repeats = Yes, order = -2147483647, callout = _wrapRunLoopWithAutoreleasePoolHandler (0x7fff47571f14), context = <CFArray 0x60000387b480 [0x7fff805eff70]>{type = mutable-small, count = 1, values = (\n\t0 : <0x7fec77012038>\n)}}",
"<CFRunLoopObserver 0x600000758280 [0x7fff805eff70]>{valid = Yes, activities = 0x20, repeats = Yes, order = 0, callout = _UIGestureRecognizerUpdateObserver (0x7fff4712091e), context = <CFRunLoopObserver context 0x600001d59110>}",
"<CFRunLoopObserver 0x600000744280 [0x7fff805eff70]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 1999000, callout = _beforeCACommitHandler (0x7fff475a22b6), context = <CFRunLoopObserver context 0x7fec76c02170>}",
"<CFRunLoopObserver 0x600000744500 [0x7fff805eff70]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 2000000, callout = _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv (0x7fff2affcc4a), context = <CFRunLoopObserver context 0x0>}",
"<CFRunLoopObserver 0x600000744320 [0x7fff805eff70]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 2001000, callout = _afterCACommitHandler (0x7fff475a231f), context = <CFRunLoopObserver context 0x7fec76c02170>}",
"<CFRunLoopObserver 0x600000744460 [0x7fff805eff70]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 2147483647, callout = _wrapRunLoopWithAutoreleasePoolHandler (0x7fff47571f14), context = <CFArray 0x60000387b480 [0x7fff805eff70]>{type = mutable-small, count = 1, values = (\n\t0 : <0x7fec77012038>\n)}}"
),
iOS在主线程的Runloop中注册了2个Observer。
第1个Observer监听了kCFRunLoopEntry事件,会调用objc_autoreleasePoolPush()
第2个Observer监听了kCFRunLoopBeforeWaiting事件,会调用objc_autoreleasePoolPop()、objc_autoreleasePoolPush()
监听了kCFRunLoopBeforeExit事件,会调用objc_autoreleasePoolPop()
5、ARC的实现原理?ARC下对retain & release做了哪些优化?
5.1、ARC的实现原理?
引用计数(Reference Count)是一个简单而有效的管理对象生命周期的方式。当我们创建一个新对象的时候,它的引用计数为 1,当有一个新的指针指向这个对象时,我们将其引用计数加 1,当某个指针不再指向这个对象是,我们将其引用计数减 1,当对象的引用计数变为 0 时,说明这个对象不再被任何指针指向了,这个时候我们就可以将对象销毁,回收内存。当我们编译源码的时候,编译器会分析源码中每个对象的生命周期,然后基于这些对象的生命周期,来添加相应的引用计数操作代码。
ARC帮我们做了什么?
ARC是LLVM+Runtime配合实现的,ARC利用LLVM编译器自动帮我们生成release、retain、autorelease代码。
像弱引用这样的存在,是需要runtime支持的,是在程序运行的过程中监听到对象销毁的时候,就会对象对应的弱引用都给清除掉。
5.2、ARC下对retain & release做了哪些优化?
5.2.1、retain的优化
1、如果对象是TaggedPointer直接返回
2、如果是未优化的isa指针,nonpointer = NO,直接调用sidetable_retain()
3、如果是优化的isa指针,引用计数+1,如果溢出了保留一半引用计数newisa.extra_rc = RC_HALF,并且把newisa.has_sidetable_rc设为true,将另一半的引用计数复制到 sidetable,sidetable_addExtraRC_nolock(RC_HALF);
ALWAYS_INLINE id objc_object::rootRetain(bool tryRetain, bool handleOverflow) {
if (isTaggedPointer()) return (id)this; // 如果是 TaggedPointer 直接返回
bool sideTableLocked = false;
bool transcribeToSideTable = false;
isa_t oldisa;
isa_t newisa;
do {
transcribeToSideTable = false;
oldisa = LoadExclusive(&isa.bits); // 获取 isa
newisa = oldisa;
if (slowpath(!newisa.nonpointer)) {
ClearExclusive(&isa.bits);// 未优化的 isa 部分
if (!tryRetain && sideTableLocked) sidetable_unlock();
if (tryRetain) return sidetable_tryRetain() ? (id)this : nil;
else return sidetable_retain();
}
if (slowpath(tryRetain && newisa.deallocating)) { // 正在被释放的处理
ClearExclusive(&isa.bits);
if (!tryRetain && sideTableLocked) sidetable_unlock();
return nil;
}
// extra_rc 未溢出时引用计数++
uintptr_t carry;
newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry); // extra_rc++
// extra_rc 溢出
if (slowpath(carry)) {
// newisa.extra_rc++ overflowed
if (!handleOverflow) {
ClearExclusive(&isa.bits);
return rootRetain_overflow(tryRetain); // 重新调用该函数 入参 handleOverflow 为 true
}
// 保留一半引用计数,准备将另一半复制到 side table.
if (!tryRetain && !sideTableLocked) sidetable_lock();
sideTableLocked = true;
transcribeToSideTable = true;
newisa.extra_rc = RC_HALF;
newisa.has_sidetable_rc = true;
}
// 更新 isa 值
} while (slowpath(!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)));
if (slowpath(transcribeToSideTable)) {
sidetable_addExtraRC_nolock(RC_HALF); // 将另一半复制到 side table side table.
}
if (slowpath(!tryRetain && sideTableLocked)) sidetable_unlock();
return (id)this;
}
5.2.2、release的优化
1、如果对象是TaggedPointer直接返回false
2、如果是未优化的isa指针,直接调用sidetable_release(performDealloc);,performDealloc是是否执行Dealloc函数
3、如果未优化的isa指针引用计数直接extra_rc--,如果引用计数为负数了,处理下溢
如果使用了 sidetable_rc,调用rootRelease_underflow(performDealloc);,size_t borrowed = sidetable_subExtraRC_nolock(RC_HALF); // 从 sidetable 中借位引用计数给 extra_rc,借位后的引用计数执行--,最后再把减之后的引用计数还给sidetable,最后执行释放调用 ((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc);
ALWAYS_INLINE bool objc_object::rootRelease(bool performDealloc, bool handleUnderflow)
{
if (isTaggedPointer()) return false;
bool sideTableLocked = false;
isa_t oldisa;
isa_t newisa;
retry:
do {
oldisa = LoadExclusive(&isa.bits);
newisa = oldisa;
if (slowpath(!newisa.nonpointer)) {
ClearExclusive(&isa.bits);// 未优化 isa
if (sideTableLocked) sidetable_unlock();
return sidetable_release(performDealloc);// 入参是否要执行 Dealloc 函数,如果为 true 则执行 SEL_dealloc
}
newisa.bits = subc(newisa.bits, RC_ONE, 0, &carry); // extra_rc--
if (slowpath(carry)) {
// donot ClearExclusive()
goto underflow;
}
// 更新 isa 值
} while (slowpath(!StoreReleaseExclusive(&isa.bits,
oldisa.bits, newisa.bits)));
if (slowpath(sideTableLocked)) sidetable_unlock();
return false;
underflow:
// 处理下溢,从 side table 中借位或者释放
newisa = oldisa;
if (slowpath(newisa.has_sidetable_rc)) { // 如果使用了 sidetable_rc
if (!handleUnderflow) {
ClearExclusive(&isa.bits);// 调用本函数处理下溢
return rootRelease_underflow(performDealloc);
}
size_t borrowed = sidetable_subExtraRC_nolock(RC_HALF); // 从 sidetable 中借位引用计数给 extra_rc
if (borrowed > 0) {
// extra_rc 是计算额外的引用计数,0 即表示被引用一次
newisa.extra_rc = borrowed - 1; // redo the original decrement too
bool stored = StoreReleaseExclusive(&isa.bits,
oldisa.bits, newisa.bits);
// 保存失败,恢复现场,重试
if (!stored) {
isa_t oldisa2 = LoadExclusive(&isa.bits);
isa_t newisa2 = oldisa2;
if (newisa2.nonpointer) {
uintptr_t overflow;
newisa2.bits =
addc(newisa2.bits, RC_ONE * (borrowed-1), 0, &overflow);
if (!overflow) {
stored = StoreReleaseExclusive(&isa.bits, oldisa2.bits,
newisa2.bits);
}
}
}
// 如果还是保存失败,则还回 side table
if (!stored) {
sidetable_addExtraRC_nolock(borrowed);
goto retry;
}
sidetable_unlock();
return false;
}
else {
// Side table is empty after all. Fall-through to the dealloc path.
}
}
// 没有使用 sidetable_rc ,或者 sidetable_rc 计数 == 0 的就直接释放
// 如果已经是释放中,抛个过度释放错误
if (slowpath(newisa.deallocating)) {
ClearExclusive(&isa.bits);
if (sideTableLocked) sidetable_unlock();
return overrelease_error();
}
// 更新 isa 状态
newisa.deallocating = true;
if (!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)) goto retry;
if (slowpath(sideTableLocked)) sidetable_unlock();
// 执行 SEL_dealloc 事件
__sync_synchronize();
if (performDealloc) {
((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc);
}
return true;
}
6、ARC下哪些情况会造成内存泄漏
-
block中的循环引用
-
NSTimer的循环引用
-
addObserver的循环引用
-
delegate的强引用
-
大次数循环内存爆涨
-
非OC对象的内存处理(需手动释放)
7、Method Swizzle注意事项?
1、+load方法会在类初始化加载的时候调用,Method Swizzle方法应该写在+load方法中。
+loadis sent when the class is initially loaded,Swizzling should always be done in+load.
2、Method Swizzle应该使用dispatch_once保证只调用一次即使是在不同的线程也是如此。
Swizzling should always be done in a
dispatch_once.
3、避免交换父类方法
如果当前类没有实现被交换的方法且父类实现了,此时父类的实现会被交换,若此父类的多个继承者都在交换时会引起多次交换导致混乱,所有交换之前要判断。
//1、class_addMethod check能否添加方法,给类cls的SEL添加一个实现IMP, 返回YES则表明类cls并未实现此方法,返回NO则表明类已实现了此方法。
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)
//2、class_replaceMethod 替换类cls的SEL的函数实现为imp
class_replaceMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp,
const char * _Nullable types)
//3、method_exchangeImplementations 最终方法交换
method_exchangeImplementations(Method _Nonnull m1, Method _Nonnull m2)
4、添加前缀避免碰撞,如果和其他分类的方法同名了,会调到分类里面,给分类添加前缀,并且要确保你的代码和你依赖的代码没有相同的功能出现。简单的copy swizzling代码不理解它是怎么工作的是非常危险的。
Remember to prefix your swizzled method name as you would any other contentious category method.
Prefix category methods, and make damn well sure that nothing else in your code base (or any of your dependencies) are monkeying around with the same piece of functionality as you are.
Simply copy-pasting swizzling code without understanding how it works is not only dangerous.
Method Swizzling
8、属性修饰符atomic的内部实现是怎么样的?能保证线程安全吗?
//getter
id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
...
id *slot = (id*) ((char*)self + offset);
if (!atomic) return *slot;
// Atomic retain release world
spinlock_t& slotlock = PropertyLocks[slot];
slotlock.lock();
id value = objc_retain(*slot);
slotlock.unlock();
return objc_autoreleaseReturnValue(value);
}
//setter
static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy)
{
...
if (!atomic) {
oldValue = *slot;
*slot = newValue;
} else {
spinlock_t& slotlock = PropertyLocks[slot];
slotlock.lock();
oldValue = *slot;
*slot = newValue;
slotlock.unlock();
}
objc_release(oldValue);
}
property 的 atomic 是采用 spinlock_t自旋锁实现的,atomic只能保证多线程读和写的安全,并不能保证取的值是一致的,XYZPerson有first和last name被设置用atomic 设置在一个线程,如果另外有一个线程访问first和last name同时,atomic将返回完整的字符串不会崩溃,但是不能保证访问到的值和其他的线程是一致的,如果first name在改变之前,last name访问在改变之后,访问的结果就是不稳定的,不匹配的名字。
Consider an
XYZPersonobject in which both a person’s first and last names are changed using atomic accessors from one thread. If another thread accesses both names at the same time, the atomic getter methods will return complete strings (without crashing), but there’s no guarantee that those values will be the right names relative to each other. If the first name is accessed before the change, but the last name is accessed after the change, you’ll end up with an inconsistent, mismatched pair of names.
简单来说就是atomic属性本身是读写安全的,但XYZPerson对象和线程安全不是同义词,比如你可以对一个XYZPerson对象的first 和 last names 进行加锁处理,这样XYZPerson对象就是线程安全的,atomic 线程不安全应该指的是颗粒度的问题。
什么是原子性操作?
以下3行代码,只能放在一起执行,一个线程访问的时候,其他线程不能访问,就是原子性操作
int a = 10;
int b = 20;
int c = a + b;
再举个例子,说明一下atomic不是线程安全的
@property (atomic, strong) NSMutableArray *data; //加上atomic,就是对属性的getter、setter方法进行原子性操作,就是会对getter、setter方法加锁
p.data = [NSMutableArray array]; //这个设置过程是线程安全的
[[p data] addObject:@"1"]; //这个获取的过程是线程安全的,但是这个添加对象可能存在多个线程同时访问,就不是线程安全的
atomic-properties-vs-thread-safe-in-objective-c
9、iOS 中内省的几个方法有哪些?内部实现原理是什么?
1、iOS 中内省就是反射
- (BOOL)isKindOfClass:(Class)aClass; //判断是否是这个类或者这个类的子类的实例
- (BOOL)isMemberOfClass:(Class)aClass; //判断是否是这个类的实例
- (BOOL)conformsToProtocol:(Protocol *)aProtocol; //判断是否遵守某个协议
+ (BOOL)conformsToProtocol:(Protocol *)protocol; //判断某个类是否遵守某个协议
- (BOOL)respondsToSelector:(SEL)aSelector; //判读实例是否有这样方法
+ (BOOL)instancesRespondToSelector:(SEL)aSelector; //判断类是否有这个方法
2、内部实现原理是什么?
//1、isKindOfClass
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->superclass) { //获取当前类的元类对象,是否和cls一致,不一致的话就去判断元类的父类和传入的cls是否一致,不一致一直向上找,找不到返回NO
if (tcls == cls) return YES;
}
return NO;
}
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) { //获取实例对象的类对象,是否和cls一致,不一致的话就去判断类对象的父类和传入的cls是否一致,不一致一直向上找,找不到返回NO
if (tcls == cls) return YES;
}
return NO;
}
//2、isMemberOfClass
+ (BOOL)isMemberOfClass:(Class)cls {
return object_getClass((id)self) == cls; //拿出当前类的元类对象和cls比较
}
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls; //拿出当前实例对象的类对象和cls比较
}
//3、conformsToProtocol
+ (BOOL)conformsToProtocol:(Protocol *)protocol {
if (!protocol) return NO;
for (Class tcls = self; tcls; tcls = tcls->superclass) {
if (class_conformsToProtocol(tcls, protocol)) return YES;
}
return NO;
}
BOOL class_conformsToProtocol(Class cls, Protocol *proto_gen)
{
protocol_t *proto = newprotocol(proto_gen);
if (!cls) return NO;
if (!proto_gen) return NO;
mutex_locker_t lock(runtimeLock);
checkIsKnownClass(cls);
assert(cls->isRealized());
for (const auto& proto_ref : cls->data()->protocols) { //遍历当前类遵守的所有协议
protocol_t *p = remapProtocol(proto_ref);
if (p == proto || protocol_conformsToProtocol_nolock(p, proto)) { //查找返回YES
return YES;
}
}
return NO;
}
//4、respondsToSelector
+ (BOOL)respondsToSelector:(SEL)sel {
return class_respondsToSelector_inst(self, sel, self->ISA());
}
respondsToSelector:
|__ class_respondsToSelector_inst()
|__ lookUpImpOrNil()
|__ lookUpImpOrForward()
返回IMP结果
这个就是消息发送,查找方法的过程
10、class、objc_getClass方法有什么区别?
1.当参数obj为Object实例对象
object_getClass(obj)与[obj class]输出结果一直,均获得isa指针,即指向类对象的指针。
2.当参数obj为Class类对象
object_getClass(obj)返回类对象中的isa指针,即指向元类对象的指针;[obj class]返回的则是其本身
3.当参数obj为Metaclass类对象
object_getClass(obj)返回元类对象中的isa指针,因为元类对象的isa指针指向根类,所有返回的是根类对象的地址指针;[obj class]返回的则是其本身。
4.obj为Rootclass类对象
object_getClass(obj)返回根类对象中的isa指针,因为跟类对象的isa指针指向Rootclass‘s metaclass(根元类),即返回的是根元类的地址指针;[obj class]返回的则是其本身。
总结:
经上面初步的探索得知,object_getClass(obj)返回的是obj中的isa指针。
而[obj class]则分两种情况:
一是当obj为实例对象时,[obj class]返回的obj对象中的isa指针;
二是当obj为类对象(包括元类和根类以及根元类)时,调用的是类方法:+ (Class)class,返回的结果为其本身。