static StripedMap& SideTables() {
return *reinterpret_cast*>(SideTableBuf);
}
template
class StripedMap {
#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
enum { StripeCount = 8 };
#else
enum { StripeCount = 64 };
#endif
struct PaddedT {
T value alignas(CacheLineSize);
//enum { CacheLineSize = 64 };
};
PaddedT array[StripeCount];
//enum { StripeCount = 64 };
static unsigned int indexForPointer(const void *p) {
uintptr_t addr = reinterpret_cast(p);
return ((addr >> 4) ^ (addr >> 9)) % StripeCount;
}
public:
//重载
T& operator[] (const void *p) {
return array[indexForPointer(p)].value;
}
const T& operator[] (const void *p) const {
return const_cast>(this)[p];
}
};
T& operator[] (const void *p) {
return array[indexForPointer(p)].value;
}
struct SideTable {
spinlock_t slock; //自旋锁
RefcountMap refcnts; //存放引用计数
weak_table_t weak_table; //weak_table是一个哈希
};
//RefcountMap
typedef objc::DenseMap,size_t,true> RefcountMap;
//DenseMap
template >
class DenseMap
: public DenseMapBase,
KeyT, ValueT, KeyInfoT, ZeroValuesArePurgeable> {
// Lift some types from the dependent base class into this class for
// simplicity of referring to them.
typedef DenseMapBase BaseT;
//ZeroValuesArePurgeable:ZeroValuesArePurgeable 默认值是 false, 但 RefcountMap 指定其初始化为 true. 这个成员标记是否可以使用值为 0 (引用计数为 1) 的桶.
typedef typename BaseT::BucketT BucketT;
friend class DenseMapBase;
BucketT *Buckets;
//Buckets 指针管理一段连续内存空间, 也就是数组, 数组成员是 BucketT 类型的对象桶的 key 为 EmptyKey 时是空桶
//typedef std::pair BucketT;
//桶的数据类型std::pair,将对象地址和对象的引用计数(这里的引用计数类似于 isa, 也是使用其中的几个 bit 来保存引用计数, 留出几个 bit 来做其它标记位)组合成一个数据类型.
unsigned NumEntries;
//NumEntries 记录数组中已使用的非空的桶的个数.
unsigned NumTombstones;
//当一个对象的引用计数为0, 要从桶中取出时, 其所处的位置会被标记为 TombstoneNumTombstones 就是数组中的墓碑的个数. 后面会介绍到墓碑的作用
unsigned NumBuckets;
//NumBuckets 桶的数量, 因为数组中始终都充满桶, 所以可以理解为数组大小.
}
//苹果的注释:
//DisguisedPtr与指针类型T*类似,除了对存储值进行伪装,以使其不受“leaks”等工具的影响。
//nil被伪装成它自己,所以零填充的内存按预期工作,
//这意味着0x80..00也被伪装成自己,但我们不在乎。
//注意,weak_entry_t不知道这个代码。
template
class DisguisedPtr {
uintptr_t value;
static uintptr_t disguise(T* ptr) {
return -(uintptr_t)ptr;
}
static T* undisguise(uintptr_t val) {
return (T*)-val;
}
public:
DisguisedPtr() { }
DisguisedPtr(T* ptr)
: value(disguise(ptr)) { }
DisguisedPtr(const DisguisedPtr& ptr)
: value(ptr.value) { }
DisguisedPtr& operator = (T* rhs) {
value = disguise(rhs);
return *this;
}
DisguisedPtr& operator = (const DisguisedPtr& rhs) {
value = rhs.value;
return *this;
}
operator T* () const {
return undisguise(value);
}
T* operator -> () const {
return undisguise(value);
}
T& operator * () const {
return *undisguise(value);
}
T& operator [] (size_t i) const {
return undisguise(value)[i];
}
};
这个DenseMap本质上是std::pair
DenseMap同样是个hash结构,下面来分析它的流程【很多参数的解释我都在代码注释里写了】
这里借用下runtime(二) SideTables大神的图解完全讲清楚了问题,厉害
首先我们有一个初始化好的, 大小为 9 的桶数组
/**
* The global weak references table. Stores object ids as keys,
* and weak_entry_t structs as their values.
*/
struct weak_table_t {
weak_entry_t *weak_entries; //连续地址空间的头指针, 数组
//管理所有指向某对象的weak指针,也是一个hash
size_t num_entries; //数组中已占用位置的个数
uintptr_t mask; //数组下标最大值(即数组大小 -1)
uintptr_t max_hash_displacement; //最大哈希偏移值
};
struct weak_entry_t {
DisguisedPtr referent; //被指对象的地址。前面循环遍历查找的时候就是判断目标地址是否和他相等。
union {
struct {
weak_referrer_t *referrers; //可变数组,里面保存着所有指向这个对象的弱引用的地址。当这个对象被释放的时候,referrers里的所有指针都会被设置成nil。
//指向 referent 对象的 weak 指针数组
uintptr_t out_of_line_ness : 2; //这里标记是否超过内联边界, 下面会提到
uintptr_t num_refs : PTR_MINUS_2; //数组中已占用的大小
uintptr_t mask; //数组下标最大值(数组大小 - 1)
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]; //只有4个元素的数组,默认情况下用它来存储弱引用的指针。当大于4个的时候使用referrers来存储指针。
//当指向这个对象的weak指针不超过4个,则直接使用数组inline_referrers,省去hash
};
};
这里有见到了我们的老朋友union,也就提示我们Apple同样是使用过同一段内存去存放不同的信息
这里有两个数组,一个是weak_referrer_t inline_referrers[WEAK_INLINE_COUNT],数组长度只有4,当某个对象的weak指针个数小于四的时候,会存入这个数组,当大于四的时候才会存到referrers当中
至此,所有的数据模型总结完毕
是不是感觉有点懵?
id
objc_initWeak(id *location, id newObj)
{
if (!newObj) {
*location = nil;
return nil;
}
return storeWeak
(location, (objc_object*)newObj);
}
id oldObj;
SideTable *oldTable;
SideTable *newTable;
// Clean up old value, if any.
if (haveOld) {
weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
}
// Assign new value, if any.
if (haveNew) {
newObj = (objc_object *)
weak_register_no_lock(&newTable->weak_table, (id)newObj, location,
crashIfDeallocating);
// weak_register_no_lock returns nil if weak store should be rejected
// Set is-weakly-referenced bit in refcount table.
if (newObj && !newObj->isTaggedPointer()) {
newObj->setWeaklyReferenced_nolock();
}
// Do not set *location anywhere else. That would introduce a race.
*location = (id)newObj;
}
else {
// No new value. The storage is not changed.
}
return (id)newObj;
//从原有的表中先删除这个 weak 指针注销已注册的弱引用。当引用者的存储即将消失,但引用还没有死。(否则,稍后归零引用将是内存访问错误。)如果引用/引用不是当前活动的弱引用,则不执行任何操作。引用不为零。
void
weak_unregister_no_lock(weak_table_t *weak_table, id referent_id,
id *referrer_id)
{
objc_object *referent = (objc_object *)referent_id; //weak 指针指向的对象
objc_object **referrer = (objc_object **)referrer_id; //referrer_id是 weak 指针, 操作时需要用到这个指针的地址
weak_entry_t *entry;
if (!referent) return;
if ((entry = weak_entry_for_referent(weak_table, referent))) { //查找 referent 对象对应的 entry
remove_referrer(entry, referrer); //从 referent 对应的 entry 中删除地址为 referrer 的 weak 指针
bool empty = true;
if (entry->out_of_line() && entry->num_refs != 0) { //如果 entry 中的数组容量大于 4 并且数组中还有元素
empty = false; //entry 非空
}
else {
for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
if (entry->inline_referrers[i]) { //否则循环查找 entry 数组, 如果 4 个位置中有一个非空
empty = false; //entry 非空
break;
}
}
}
if (empty) {
weak_entry_remove(weak_table, entry); //从 weak_table 中移除该条 entry
}
}
}
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;
size_t index = begin;
size_t hash_displacement = 0;
while (weak_table->weak_entries[index].referent != referent) {
index = (index+1) & weak_table->mask;
if (index == begin) bad_weak_table(weak_table->weak_entries);
hash_displacement++;
if (hash_displacement > weak_table->max_hash_displacement) {
return nil;
}
}
return &weak_table->weak_entries[index];
}
&weak_table->mask
这个操作是?这个mask实际值是表的size-1,而size是2的n次方进行扩张的,所以mask的形式就1111 1111 1111
这种,索引和mask位与之后的值必定就落在了[0, size]范围内。也就是说,先是通过hash_pointer对地址进行hash映射,得到下标,接下来通过位遮蔽,保证这个映射是在范围内的,不会超出范围id
weak_register_no_lock(weak_table_t *weak_table, id referent_id,
id *referrer_id, bool crashIfDeallocating)
{
objc_object *referent = (objc_object *)referent_id;
objc_object **referrer = (objc_object **)referrer_id;
// now remember it and where it is being stored
weak_entry_t *entry; //如果 weak_table 有对应的 entry
if ((entry = weak_entry_for_referent(weak_table, referent))) {
append_referrer(entry, referrer); //将 weak 指针存入对应的 entry 中
}
else {
weak_entry_t new_entry(referent, referrer); //创建新的 entry
weak_grow_maybe(weak_table); //查看是否需要调整 weak_table 中 weak_entries 数组大小
weak_entry_insert(weak_table, &new_entry); //将新的 entry 插入到 weak_table 中
}
return referent_id;
}
static void append_referrer(weak_entry_t *entry, objc_object **new_referrer)
{
if (! entry->out_of_line()) { //如果数组大小没超过 4
// Try to insert inline.
for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
if (entry->inline_referrers[i] == nil) { //循环查找数组成员
entry->inline_referrers[i] = new_referrer; //把新的 weak 指针插入到空位置
return;
}
}
//也就是说走到这就是刚好之前有四个引用,现在添加第五个
//数组中的 4 个位置都非空, 就要调整策略使用 referrers 了
//从这里开始, 这一段是把 inline_referrers 数组调整为使用 referrers 的形式
// Couldn't insert inline. Allocate out of line.
weak_referrer_t *new_referrers = (weak_referrer_t *)
calloc(WEAK_INLINE_COUNT, sizeof(weak_referrer_t)); //还是开辟 4 个 weak_referrer_t 大小的空间
// This constructed table is invalid, but grow_refs_and_insert
// will fix it and rehash it.
for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
new_referrers[i] = entry->inline_referrers[i]; //将 inline_referrers 中的值赋值给 referrers
}
entry->referrers = new_referrers;
entry->num_refs = WEAK_INLINE_COUNT;
entry->out_of_line_ness = REFERRERS_OUT_OF_LINE;
entry->mask = WEAK_INLINE_COUNT-1;
entry->max_hash_displacement = 0;
}
assert(entry->out_of_line());
if (entry->num_refs >= TABLE_SIZE(entry) * 3/4) {
return grow_refs_and_insert(entry, new_referrer);
}
//开始哈希算法
size_t begin = w_hash_pointer(new_referrer) & (entry->mask);
size_t index = begin; //使用哈希算法计算到一个起始下标
size_t hash_displacement = 0; //哈希偏移次数
while (entry->referrers[index] != nil) { //循环找空位置
hash_displacement++;
index = (index+1) & entry->mask;
if (index == begin) bad_weak_table(entry);
}
//这里记录下移位的最大值, 那么数组里的任何一个数据, 存储时的移位次数都不大于这个值
//可以提升查找时的效率, 如果移位次数超过了这个值都没有找到, 就证明要查找的项不在数组中
if (hash_displacement > entry->max_hash_displacement) {
entry->max_hash_displacement = hash_displacement;
}
weak_referrer_t &ref = entry->referrers[index];
ref = new_referrer;
entry->num_refs++;
}
weak_referrer_t &ref = entry->referrers[index];
ref = new_referrer;
entry->num_refs++;
entry->referrers[index] = new_referrer;
entry->num_refs++;
#include
using namespace std;
struct Padd {
int a;
float b;
}bucket[10];
int main(int argc, const char * argv[]) {
// insert code here...
std::cout << "Hello, World!\n";
int j = 0;
float k = 16;
for (Padd &i : bucket) {
i.a = j++;
i.b = k++;
}
for (Padd &i : bucket) {
cout << i.a << "XXX" << i.b << endl;
}
Padd *testPadd = (Padd *)malloc(sizeof(Padd));
testPadd->a = 99;
testPadd->b = 109;
Padd &ref = bucket[4];
ref = *testPadd;
printf("%p\n", &bucket[4]);
printf("%p\n", &ref);
for (Padd &i : bucket) {
cout << i.a << "XXX" << i.b << endl;
}
return 0;
}
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);
if (entry == nil) {
return;
}
weak_referrer_t *referrers;
size_t count;
if (entry->out_of_line()) {
referrers = entry->referrers;
count = TABLE_SIZE(entry);
}
else {
referrers = entry->inline_referrers;
count = WEAK_INLINE_COUNT;
}
for (size_t i = 0; i < count; ++i) {
objc_object **referrer = referrers[i];
if (referrer) {
if (*referrer == referent) {
*referrer = nil;
}
}
}
weak_entry_remove(weak_table, entry);
}
id __weak obj1 = obj0;
NSLog(@"class = %@",[obj1 class]);
id __weak obj1 = obj0;
id __autoreleasing tmp = obj1;
NSLog(@"class = %@",[tmp class]);//实际访问的是注册到自动释放池的对象
//实验代码
NSObject *obj0 = [[NSObject alloc] init];
NSObject *obj1 __weak = obj0;
printf("retain count = %ld\n",CFGetRetainCount((__bridge CFTypeRef)(obj0))); //结果为1
printf("retain count = %ld\n",CFGetRetainCount((__bridge CFTypeRef)(obj1))); //断点打在这里 结果为2
id
objc_loadWeakRetained(id *location)
{
id obj;
id result;
Class cls;
SideTable *table;
retry:
// fixme std::atomic this load
obj = *location;
if (!obj) return nil;
if (obj->isTaggedPointer()) return obj;
table = &SideTables()[obj];
table->lock();
if (*location != obj) {
table->unlock();
goto retry;
}
result = obj;
cls = obj->ISA();
if (! cls->hasCustomRR()) {
// Fast case. We know +initialize is complete because
// default-RR can never be set before then.
assert(cls->isInitialized());
if (! obj->rootTryRetain()) { //rootTryRetain执行了retain操作
result = nil;
}
}
table->unlock();
return result;
}
inline uintptr_t
objc_object::rootRetainCount()
{
if (isTaggedPointer()) return (uintptr_t)this;
sidetable_lock();
isa_t bits = LoadExclusive(&isa.bits);
ClearExclusive(&isa.bits);
if (bits.nonpointer) {
uintptr_t rc = 1 + bits.extra_rc;
if (bits.has_sidetable_rc) {
rc += sidetable_getExtraRC_nolock();
}
sidetable_unlock();
return rc;
}
sidetable_unlock();
return sidetable_retainCount();
}