ObjC Runtime 中 Weak 属性的实现 (中)

【iOS分类、关联对象】如何使用关联对象给分类实现一个weak的属性 objc_setAssociatedObject实际上存储的是WeakObjWrapper对象,对WeakObjWrapper对象产生强引用,WeakObjWrapper对象内部弱持有传递进去的值,保证在对象释放的时候,自动把值设置为nil,避免了崩溃;注意:在派生的子类,添加的实现dealloc的方法中,重新调用一下父类的dealloc保证原有的类的释放关系不被破坏;看以下测试代码,使用policy为OBJC_ASSOCIATION_ASSIGN的策略,会发生什么样的情况?1、通过中间对象的方式。 阅读详情

导语

在上一篇中简单分析了 Weak 属性是如何被存储,获取和销毁的,其中的 SideTable 结构体当做黑盒进行处理。本文尝试对 SideTable 的结构进行一些分析。

观察

struct SideTable {
    spinlock_t slock;
    RefcountMap refcnts;
    weak_table_t weak_table;

    SideTable() {
        memset(&weak_table, 0, sizeof(weak_table));
    }

    ~SideTable() {
        _objc_fatal("Do not delete SideTable.");
    }

    void lock() { slock.lock(); }
    void unlock() { slock.unlock(); }
    void forceReset() { slock.forceReset(); }

    // Address-ordered lock discipline for a pair of side tables.

    template<HaveOld, HaveNew>
    static void lockTwo(SideTable *lock1, SideTable *lock2);
    template<HaveOld, HaveNew>
    static void unlockTwo(SideTable *lock1, SideTable *lock2);
};

SideTable 主要分为 3 部分

  • weak_table_t: weak 引用的全局 hash 表
  • RefcountMap : 引用计数的 hash 表
  • slock: 保证原子操作的自旋锁

在 static id storeWeak(id *location, objc_object *newObj) 方法中有

// 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;
}

可知对于弱引用变量的保存,主要还是看 weak_table 这个属性

测试代码

#import <Foundation/Foundation.h>

@interface WeakProperty : NSObject

@property (nonatomic,weak) NSObject *obj;
@property (nonatomic,weak) NSObject *obj2;
@property (nonatomic,weak) NSObject *obj3;
@property (nonatomic,weak) NSObject *obj4;
@property (nonatomic,weak) NSObject *obj5;

@end

@implementation WeakProperty

- (void)dealloc {
    NSLog(@"%s",__func__);
}

@end


int main(int argc, const char * argv[]) {
    @autoreleasepool {
        WeakProperty *property = [[WeakProperty alloc] init];
        NSObject *obj = [[NSObject alloc] init];
        property.obj = obj;
        NSLog(@"%@",property.obj);

        // [1]
        property.obj2 = obj;

        // [2]
        property.obj3 = obj;
        property.obj4 = obj;
        property.obj5 = obj;

        // [3]
        property.obj = nil;        
    }
    return 0;
}

结构体: weak_table_t

weak_table_t 的定义在 objc-weak.h 中

/**
 * 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;
    size_t    num_entries;
    uintptr_t mask;
    uintptr_t max_hash_displacement;
};

说明:

  • 一个指向 weak_entry_t 的指针
  • size_t(即 unsigned ) 类型的 num_entries ,用于描述 hash 表的长度
  • uintptr_t(即 unsigned long) 类型的 mask(掩码)
  • uintptr_t(即 unsigned long) 类型的 max_hash_displacement

结构体: weak_entry_t

struct weak_entry_t {
    DisguisedPtr<objc_object> referent;
    union {
        struct {
            weak_referrer_t *referrers;
            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];
        };
    };

    bool out_of_line() {
        return (out_of_line_ness == REFERRERS_OUT_OF_LINE);
    }

    weak_entry_t& operator=(const weak_entry_t& other) {
        memcpy(this, &other, sizeof(other));
        return *this;
    }

    weak_entry_t(objc_object *newReferent, objc_object **newReferrer)
        : referent(newReferent)
    {
        inline_referrers[0] = newReferrer;
        for (int i = 1; i < WEAK_INLINE_COUNT; i++) {
            inline_referrers[i] = nil;
        }
    }
};

在 C++ 中,结构体是由关键词 struct 定义的一种数据类型。他的成员和基类默认为公有的(public)。由关键词 class 定义的成员和基类默认为私有的(private)。这是 C++ 中 结构体和类仅有的区别。

类: DisguisedPtr
// DisguisedPtr<T> acts like pointer type T*, except the 
// stored value is disguised to hide it from tools like `leaks`.
// nil is disguised as itself so zero-filled memory works as expected, 
// which means 0x80..00 is also disguised as itself but we don't care.
// Note that weak_entry_t knows about this encoding.
template <typename T>
class DisguisedPtr {
    uintptr_t value; // unsigned long

    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<T>& ptr) 
        : value(ptr.value) { }

    DisguisedPtr<T>& operator = (T* rhs) {
        value = disguise(rhs);
        return *this;
    }
    DisguisedPtr<T>& operator = (const DisguisedPtr<T>& 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];
    }

    // pointer arithmetic operators omitted 
    // because we don't currently use them anywhere
};
// The address of a __weak variable.
// These pointers are stored disguised so memory analysis tools
// don't see lots of interior pointers from the weak table into objects.
typedef DisguisedPtr<objc_object *> weak_referrer_t;

小结

weak_entry_t 包含一个 DisguisedPtr<objc_object>,Disguised 是伪装的意思,根据注释可知,可以将 DisguisedPtr<T> 当成 T * 指针类型即可,在当前场景可看作是一个指向 objc_object 的指针类型

weak_referrer_t 是 DisguisedPtr<objc_object *> 可以看成是 objc_object 指针的地址

接着是一个 union ,out_of_line_ness 与 inline_referrers[1] 共用了低 2 位,因为

// out_of_line_ness field overlaps with the low two bits of inline_referrers[1].
// inline_referrers[1] is a DisguisedPtr of a pointer-aligned address.
// The low two bits of a pointer-aligned DisguisedPtr will always be 0b00
// (disguised nil or 0x80..00) or 0b11 (any other address).
// Therefore out_of_line_ness == 0b10 is used to mark the out-of-line state.
#define REFERRERS_OUT_OF_LINE 2

注释说明了 DisuisedPtr 的低 2 位不是 0b00 就是 0b11,所以要表示 out-of-line 只能使用 out_of_line_ness == 0b10 (当 out_of_line_ness 为 0b01 或 0b10 会分别得到 false 和 true )

num_refs 占 30 (32位系统) / 62(64位系统) 位
mask 和 max_hash_displacement 在 weak_table_t 中也有。

out_of_line() 方法在上面算是已经说过了

weak_entry_t& operator=(const weak_entry_t& other) {...} 重载运算符 =

The memcpy() function copies n bytes from memory area src
to memory area dest. The memory areas may not overlap.
Use memmove(3) if the memory areas do overlap.

从参数 other 所指的内存地址的起始位置开始拷贝 sizeof(other) 字节到 this 指针指向的当前对象的起始地址。

“weak_entry_t(objc_object *newReferent, objc_object **newReferrer)
referent(newReferent)“

: referent(newReferent) 是初始化列表,代表用参数 newReferent来初始化结构体中的 referent 属性。联合类型中的 inline_referrers[0] 接收参数 newReferrer ,并将剩下的 1,2,3 都置为 nil

函数: weak_register_no_lock

注释 [2] & [3]

/// Adds an (object, weak pointer) pair to the weak table.
/// 添加一个 (对象,弱引用指针)到 weak hash 表中
id weak_register_no_lock(weak_table_t *weak_table, id referent, 
                         id *referrer, bool crashIfDeallocating);

具体实现如下:

/** 
 * Registers a new (object, weak pointer) pair. Creates a new weak
 * object entry if it does not exist.
 * 
 * @param weak_table The global weak table.
 * @param referent The object pointed to by the weak reference.
 * @param referrer The weak pointer address.
 */
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;

    if (!referent  ||  referent->isTaggedPointer()) return referent_id;

    // ensure that the referenced object is viable
    bool deallocating;
    if (!referent->ISA()->hasCustomRR()) {
        deallocating = referent->rootIsDeallocating();
    }
    else {
        BOOL (*allowsWeakReference)(objc_object *, SEL) = 
            (BOOL(*)(objc_object *, SEL))
            object_getMethodImplementation((id)referent, 
                                           SEL_allowsWeakReference);
        if ((IMP)allowsWeakReference == _objc_msgForward) {
            return nil;
        }
        deallocating =
            ! (*allowsWeakReference)(referent, SEL_allowsWeakReference);
    }

    if (deallocating) {
        if (crashIfDeallocating) {
            _objc_fatal("Cannot form weak reference to instance (%p) of "
                        "class %s. It is possible that this object was "
                        "over-released, or is in the process of deallocation.",
                        (void*)referent, object_getClassName((id)referent));
        } else {
            return nil;
        }
    }

    // now remember it and where it is being stored
    weak_entry_t *entry;
    if ((entry = weak_entry_for_referent(weak_table, referent))) {
        append_referrer(entry, referrer);
    } 
    else {          
        weak_entry_t new_entry(referent, referrer);
        weak_grow_maybe(weak_table);
        weak_entry_insert(weak_table, &new_entry);
    }

    // Do not set *referrer. objc_storeWeak() requires that the 
    // value not change.

    return referent_id;
}

弱引用属性 obj 在函数 weak_register_no_lock 中传递给行参 referent_id, 赋值给局部变量 referent ,location 传递给形参 referrer_id,赋值给局部变量 referrer。

经过一些检查,比如是否允许弱引用,弱引用对象是否可用。

/** 
 * Return the weak reference table entry for the given referent. 
 * If there is no entry for referent, return NULL. 
 * Performs a lookup.
 *
 * @param weak_table 
 * @param referent The object. Must not be nil.
 * 
 * @return The table of weak referrers to this object. 
 */
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];
}

根据 referent 为 key ,在 weak_table 中通过遍历 weak_entries 数组,对referent 属性值进行比较的方式来查找元素,未找到,走 else

weak_entry_t(objc_object *newReferent, objc_object **newReferrer)
   : referent(newReferent)
{
   inline_referrers[0] = newReferrer;
   for (int i = 1; i < WEAK_INLINE_COUNT; i++) {
       inline_referrers[i] = nil;
   }
}

执行 weak_entry_t 结构体的初始化

通过强转操作来伪装指针。接收 newReferrer 即 referrer 为 inline_referrers[0] 在这里 *referrer 等于 nil 所以 inline_referres 数组元素全指向 nil,因为是无符号长整数,因此就是 0。

函数: weak_grow_maybe

当弱引用的 hash 表的空间使用率达到 3/4 后,扩充 hash 表

// Grow the given zone's table of weak references if it is full.
static void weak_grow_maybe(weak_table_t *weak_table)
{
    size_t old_size = TABLE_SIZE(weak_table);

    // Grow if at least 3/4 full.
    if (weak_table->num_entries >= old_size * 3 / 4) {
        weak_resize(weak_table, old_size ? old_size*2 : 64);
    }
}
函数: weak_entry_insert

添加元素到弱引用的 hash 表中

/** 
 * Add new_entry to the object's table of weak references.
 * Does not check whether the referent is already in the table.
 */
static void weak_entry_insert(weak_table_t *weak_table, weak_entry_t *new_entry)
{
    weak_entry_t *weak_entries = weak_table->weak_entries;
    assert(weak_entries != nil);

    size_t begin = hash_pointer(new_entry->referent) & (weak_table->mask);
    size_t index = begin;
    size_t hash_displacement = 0;
    while (weak_entries[index].referent != nil) {
        index = (index+1) & weak_table->mask;
        if (index == begin) bad_weak_table(weak_entries);
        hash_displacement++;
    }

    weak_entries[index] = *new_entry;
    weak_table->num_entries++;

    if (hash_displacement > weak_table->max_hash_displacement) {
        weak_table->max_hash_displacement = hash_displacement;
    }
}

获取 new_entry 的 referent 属性,即弱引用的 obj 属性,以其地址的无符号长整数取相反数来做参数,通过移位与位移进行 hash 操作,通过 weak_table->mask(63 = 0b111111) 掩码保留 hash 操作后的低 6 位( 64 位系统),作为索引,接下来用 while (weak_entries[index].referent != nil) {...} ,解决 hash 碰撞的问题。然后添加到 hash 表中,修改表的长度

效果如上图所示,static id storeWeak(id *location, objc_object *newObj) 的 location 和 newObj 分别被保存到 weak_table_t 结构体的 referent ,inline_referrers 数组的首位。

查找 referent 是否存在的条件是

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;
   }
}

注释 [2] & [3]

进入 append_referrer 函数后

if (! entry->out_of_line()) {
   // 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;
           return;
       }
   }

因为 entry-> out_of_line() 等于 false 会尝试添加到 (entry->inline_referrers 数组中。

取消 [2] 的注释,因为已经达到 4 个,所以在 obj5 时,会扩充。

// Couldn't insert inline. Allocate out of line.
weak_referrer_t *new_referrers = (weak_referrer_t *)
  calloc(WEAK_INLINE_COUNT, sizeof(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];
}
entry->referrers = new_referrers;
entry->num_refs = WEAK_INLINE_COUNT;
entry->out_of_line_ness = REFERRERS_OUT_OF_LINE;

会设置 entry->out_of_line_ness 为 REFERRERS_OUT_OF_LINE

结合注释

/**
 * The internal structure stored in the weak references table. 
 * It maintains and stores
 * a hash set of weak references pointing to an object.
 * If out_of_line_ness != REFERRERS_OUT_OF_LINE then the set
 * is instead a small inline array.
 */

可知当 weak 变量引用数量不多于 4 个时,会使用数组方式进行存储,而多于 4 个后会用 hash 表的方式进行存储。

函数: weak_unregister_no_lock

/// Removes an (object, weak pointer) pair from the weak table.
/// 从 weak hash 表中移除一个(对象,弱引用指针)
void weak_unregister_no_lock(weak_table_t *weak_table, id referent, id *referrer);

具体实现如下:

/** 
 * Unregister an already-registered weak reference.
 * This is used when referrer's storage is about to go away, but referent
 * isn't dead yet. (Otherwise, zeroing referrer later would be a
 * bad memory access.)
 * Does nothing if referent/referrer is not a currently active weak reference.
 * Does not zero referrer.
 * 
 * FIXME currently requires old referent value to be passed in (lame)
 * FIXME unregistration should be automatic if referrer is collected
 * 
 * @param weak_table The global weak table.
 * @param referent The object.
 * @param referrer The weak reference.
 */
void
weak_unregister_no_lock(weak_table_t *weak_table, id referent_id, 
                        id *referrer_id)
{
    objc_object *referent = (objc_object *)referent_id;
    objc_object **referrer = (objc_object **)referrer_id;

    weak_entry_t *entry;

    if (!referent) return;

    if ((entry = weak_entry_for_referent(weak_table, referent))) {
        remove_referrer(entry, referrer);
        bool empty = true;
        if (entry->out_of_line()  &&  entry->num_refs != 0) {
            empty = false;
        }
        else {
            for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
                if (entry->inline_referrers[i]) {
                    empty = false; 
                    break;
                }
            }
        }

        if (empty) {
            weak_entry_remove(weak_table, entry);
        }
    }

    // Do not set *referrer = nil. objc_storeWeak() requires that the 
    // value not change.
}

同样是使用 weak_entry_for_referent 函数查找弱引用是否存在

注释 [1] & [2] ,取消注释 [3]

/** 
 * Remove old_referrer from set of referrers, if it's present.
 * Does not remove duplicates, because duplicates should not exist. 
 * 
 * @todo this is slow if old_referrer is not present. Is this ever the case? 
 *
 * @param entry The entry holding the referrers.
 * @param old_referrer The referrer to remove. 
 */
static void remove_referrer(weak_entry_t *entry, objc_object **old_referrer)
{
    if (! entry->out_of_line()) {
        for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
            if (entry->inline_referrers[i] == old_referrer) {
                entry->inline_referrers[i] = nil;
                return;
            }
        }
        ...
        objc_weak_error();
        return;
    }

    size_t begin = w_hash_pointer(old_referrer) & (entry->mask);
    size_t index = begin;
    size_t hash_displacement = 0;
    while (entry->referrers[index] != old_referrer) {
        index = (index+1) & entry->mask;
        if (index == begin) bad_weak_table(entry);
        hash_displacement++;
        if (hash_displacement > entry->max_hash_displacement) {
            ...
            return;
        }
    }
    entry->referrers[index] = nil;
    entry->num_refs--;
}

移除时,是以 referrer 属性来比较,发现地址相同,将其置为 nil 来实现移除的效果。

函数: weak_clear_no_lock

/// Called on object destruction. Sets all remaining weak pointers to nil.
/// 在对象调用析构方法时,设置所有留下的弱引用指针为nil
void weak_clear_no_lock(weak_table_t *weak_table, id referent);

具体实现如下:

/** 
 * Called by dealloc; nils out all weak pointers that point to the 
 * provided object so that they can no longer be used.
 * 
 * @param weak_table 
 * @param referent The object being deallocated. 
 */
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) {
        /// 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);
    } 
    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;
            }
            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);
}

会被 dealloc 调用,根据注释和代码可知同样以 referent 为 key 遍历,然后依次将置为 nil,但是测试时,走的都是 if (entry == nil) 然后直接 return。

总结

弱引用查找根据 referent 属性,首次会被存储到 weak_table_t 结构体 referent 和 inline_referrers[0],当继续添加时,如果引用次数不大于 4 个保存在数组inline_referrers 中,当超过 4 个后以 hash 表的形式进行存储。移除时,根据 referrer 从 inline_referrers 中移除。

参考

  1. int - What is size_t in C? - Stack Overflow
  2. wiki - C++类
  3. wiki - 位段
  4. Linux Programmer’s Manual memcpy
  5. inline bool objc_object::isTaggedPointer(); How does the function work?
  6. PHP哈希表碰撞攻击原理
  7. wiki - 掩码
  8. wiki - 哈希表
  9. When to use reinterpret_cast?
  10. OSObject
  11. c++ operator操作符的两种用法:重载和隐式类型转换,string转其他基本数据类型的简洁实现string_cast
  12. c++中冒号(:)和双冒号(::)的用法
  13. ARC 引用计数之weak
【OC】为category添加weak属性 通过中间类对象持有weak属性-分类属性关联中间类对象的形式完成分类添加weak属性的功能。友友们都知道,分类当中是不能添加property的,因为运行时系统不会自动完成@synthesize,所以需要使用runtime的关联对象形式完成property。看来关联对象的assign修饰和属性一样,离开作用域之后对象销毁,assign指针成为野指针,所以我们还是要想办法完成weak修饰。有copy,有retain,还有assign,看起来足够用了。这里有几种方法,目前使用的先记录下来,有空将剩下的补全。 阅读详情

相关推荐

iOS 经典全部面试题(上)

/队友咆哮道:你在干嘛?!!不要在 setter 里进行像这样的判断。(该观点参考链接:什么情况会在 copy setter 里做 if 判断?例如,车速可能就有最高速的限制,车速也不可能出现负值,如果车子的最高速为300,则 setter 的方法就要改写成这样:回到这个题目,如果单单就上文的代码而言,我们不需要也不能重写 name 的 setter :由于是 name 是只读属性,所以编译器不会为其创建对应的“设置方法”,用初始化方法设置好属性值之后,就不能再改变了。

2401_84434237的博客 1010

iOS弱引用表 SideTable weak_table_t weak_entry_t

一、DisguisedPtr伪装指针类介绍 主要是用来把对象的指针映射到long类型的数值,来保存对象的指针,至于为什么不直接保存指针,估计是处于安全考虑, 防止空指针造成的坏的影响; 地层大量使用了DisguisedPtr,DisguisedPtr也不是很复杂; 可以看出DisguisedPtr是个模版类,可以看作是iOS中的范型, 里面定义了一个属性value,用来保存处理后的对象指针;是个unsigned long类型, 和其他几个函数; template <typename T> cla

xuyushiguang的博客 1336

iOS经典面试题之深入解析runtime是如何实现weak变量的自动置nil

一、weak 属性特点 weak 表明该属性定义了一种“非拥有关系” (nonowning relationship); 为 weak 属性设置新值时,设置方法既不保留新值,也不释放旧值; 同 assign 类似,然而在属性所指的对象释放时候,属性值也会清空(nil out)。 二、runtime 如何实现 weak 属性 ① 实现步骤 初始化时:runtime 会调用 objc_initWeak 函数,初始化一个新的 weak 指针指向对象的地址。 添加引用时:objc_initWeak 函数会调用

╰つ栺尖篴夢ゞ 893

Objective-C runtime机制(7)——SideTables, SideTable, weak_table, weak_entry_t

在runtime中,有四个数据结构非常重要,分别是SideTables,SideTable,weak_table_t和weak_entry_t。它们和对象的引用计数,以及weak引用相关。 关系 先说一下这四个数据结构的关系。 在runtime内存空间中,SideTables是一个64个元素长度的hash数组,里面存储了SideTable。SideTables的hash键值就是一个对象obj的ad...

无忘无往 1万+

从2-RSS-1U并联踝关节到双足机器人动态行走:雅可比矩阵与力矩控制实践

本文深入探讨了2-RSS-1U并联踝关节在双足机器人动态行走中的关键作用,详细解析了其设计原理、雅可比矩阵的应用及力矩控制实践。通过优化几何参数和控制算法,实现了低转动惯量、高刚度和紧凑设计,显著提升了机器人的行走稳定性和能量效率。文章还分享了从仿真到实物的跨越经验及未来优化方向。

weixin_42641998的博客 594

Anaconda镜像源切换全攻略:从清华源到恢复默认设置的完整流程

本文详细介绍了Anaconda镜像源的管理与优化,重点讲解了如何配置清华源以提升国内下载速度。内容涵盖镜像源工作原理、清华源详细配置步骤、多源管理策略、常见问题排查以及如何安全恢复默认设置,帮助用户构建完整的镜像源管理体系。

weixin_28828243的博客 1020

iOS关联对象技术原理

iOS 通过 runtime 的 API 可以给分类添加属性,关联属性总共有下边3个 API ///获取某个对象的关联属性 id objc_getAssociatedObject(id object, const void *key) { return _object_get_associative_reference(object, (void *)key); } ///给某个对象添加...

weixin_34101229的博客 173

思科模拟器Cisco Packet Tracer交换机的管理配置

一、交换机的管理方式:带内管理和带外管理 通过交换机的 Console 端口管理交换机属于带外管理;这种管理方式不占用交换机的网络端口,第一次配置交换机必须利用 Console 端口进行配置。 通过 Telnet、拨号等方式属于带内管理。 二、控制台配置思科交换机 1.新建拓扑图 新建拓扑,选择console线连接交换机和PC,一端接交换机的console口,一端接PC的RS 232 口(...

qq_37992321的博客 2万+

iOS-底层原理 18:关联对象底层原理探索

其底层原理的实现,主要分为两部分: 通过objc_setAssociatedObject设值流程 通过objc_getAssociatedObject取值流程 关联对象-设值流程 在分类LG中重写属性cate_name的set、get方法,通过runtime的属性关联方法实现 运行程序,断点断在main中cate_name赋值处 继续往下运行,断在分类的setCate_name方法中 其中objc_setAssociatedObject方法有四

changcongcong_ios的博客 842

UE5.3.2 + VS2022社区版在Win11上的C++编译环境配置全解

Unreal Engine 5的C++开发依赖严格的工具链协同,其核心在于Visual Studio编译器版本、Windows SDK与引擎构建系统(UBT)的精准匹配。UE5.3.2强制要求MSVC v143.4(即VS2022 17.4+)及Windows 11 SDK 10.0.22621.0,任何偏差都会引发LNK2019链接失败、C1083头文件缺失或MSB8066构建中断等典型错误。该配置不仅关乎安装顺序,更涉及注册表清理、PATH环境变量、SDK路径注入与运行时DLL加载等底层机制。适用于Wi

weixin_33978722的博客 367

基于STM32L4XX、HAL库的SIM65M定位模块驱动程序设计

SIM65M是一款基于4G LTE Cat.1的无线通信模块,支持最大下行速率10Mbps和上行速率5Mbps。它兼容多种网络协议(TCP/UDP/HTTP/HTTPS/FTP/MQTT等),适用于物联网应用。PC0 <---> PWRKEY (模块电源控制)PC1 <---> RESET (模块复位)支持LTE-FDD/LTE-TDD。工作电压:3.4V~4.2V。

zhusl6688的博客 196

ios-关联对象

假设对象 objc, 属性 key 属性值 value - objc 求hash ==> index - buckets[index] ==> bucket - bucket->second ==> sub_buckets (bucke->first ==> objc) - key 求hash ==> sub_index - sub_buckets[sub_index] ==> sub_bucket - sub_bucket->second ==> association....

coding不死,也未曾凋零 1751

Sigrity Power Tree如何生成电源树操作指导

Sigrity Power Tree可以方便地生成电源树,仅需要网表文件就能将系统的电源树直观的显示出来,生成的电源树文件可以用Power DC,optimizePI进行直流和交流仿真,下面介绍如何生成电源树。Base on [Device_TYPE and VALUE]就是识别网表中器件的Device_TYPE作为power tree中的模型的名字。Allegro layout netlist file,就是从Allegro layout文件中输出的netlist第三方网表文件。如何生成电源树操作指导。

weixin_54787054的博客 1532

ObjC Runtime 中 Weak 属性的实现 (上)

前言OC 中的 weak 属性是怎么实现的,为什么在对象释放后会自动变成 nil?本文对这个问题进行了一点探讨。环境 mac OS Sierra 10.12.4 objc709 参考答案搜索后发现runtime 如何实现 weak 属性给出了一个参考答案。 runtime 对注册的类, 会进行布局,对于 weak 对象会放入一个 hash 表中。 用 weak 指向的对象内存地址作为

ioSugar 4216

电子设计(1)二极管防电源反接电路

二极管经常用来作电源防反接保护,所需器件少,电路简单。

记得诚 1万+

深度解析电力负荷预测:基于深度学习的完整实战指南

电力负荷预测是智能电网运行中的核心技术之一,准确的负荷预测对于电网的稳定运行、电力市场交易以及能源调度具有重要意义。本文将详细介绍电力负荷预测的完整解决方案,从数据预处理、特征工程到模型构建与评估,提供完整的可执行代码。我们将使用LSTM(长短期记忆网络)和Transformer两种主流深度学习模型进行实现,并通过实际数据集验证模型效果。电力负荷预测是指根据历史负荷数据、气象因素、时间特征等多维信息,预测未来某一时段的电力需求。超短期负荷预测:未来几分钟到几小时短期负荷预测:未来1天到1周中期负荷预测。

YOLO 111

iOS Principle:weak

iOS的weak的原理

u013712343的博客 89

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

面试题

iOS面试题 字数5186 阅读2082 评论3 喜欢24 一个区分度很大的面试题 考察一个面试者基础咋样,基本上问一个 @property 就够了: @property 后面可以有哪些修饰符? 线程安全的: atomic,nonatomic 访问权限的 readonly,readwrite 内存管理(ARC) assign,strong,w

u011552290的博客 405

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

从欢聚时代iOS校招C卷看Objective-C与Runtime核心考点

在iOS开发中,Objective-C和Runtime是绕不开的底层基石,更是校招与社招面试中高频考察的技术栈。理解内存管理、引用计数、消息转发与RunLoop机制,不仅有助于分析属性关键字、block循环引用等经典问题,也能为多线程并发和性能优化提供扎实的理论支撑。从直播、IM等高实时互动场景到列表流畅度优化,这些原理始终贯穿于工程实践。本文通过复盘一份2017年欢聚时代iOS工程师校招笔试卷,梳理其题型结构与出题逻辑,提炼出依然适用于当今面试的常青知识点,帮助开发者建立系统化的底层认知,从而在技术面试与

weixin_33796177的博客 307

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

iOS进阶 - Runtime

1.Category 的实现原理? Category 实际上是 Category_t的结构体,在运行时,新添加的方法,都被以倒序插入到原有方法列表的最前面,所以不同的Category,添加了同一个方法,执行的实际上是最后一个。 Category 在刚刚编译完的时候,和原来的类是分开的,只有在程序运行起来后,通过 Runtime ,Category 和原来的类才会合并到一起。 2.isa指针的理解,对象的isa指针指向哪里?isa指针有哪两种类型? isa 等价于 is kind o

hiccuptang 224

移动端开发iOS系列之Runtime

作者:洋仔 链接:https://juejin.cn/post/6961653605652758564 1.objc在向一个对象发送消息时,发生了什么? objc在向一个对象发送消息时,runtime会根据对象的isa指针找到该对象实际所属的类,然后在该类中的方法列表以及其父类方法列表中寻找方法运行,如果一直到根类还没找到,转向拦截调用,走消息转发机制,一旦找到 ,就去执行它的实现IMP 。 2.objc中向一个nil对象发送消息将会发生什么? 如果向一个nil对象发送消息,首先在寻找对象的isa指针时就是

henry_lei的博客 459

(转)ios学习--你会遇到的runtime面试题(详)

1、了解runtime吗?是什么? 2、你怎么知道的? 3、对象如何找到对应方法去调用的 于是我总结了很多网上被问到的一些关于runtime的题目,并做了详细的回答,并在后面补充了我在学习runtime时敲的一些代码,如果想吃透runtime的朋友,可以把后面补充的内容好好看完 一、你会被问到的关于runtime笔试题: 1. runtime怎么添加属性、方法等 2. run...

aishi9935的博客 144

runtime 如何实现 weak 属性

出题者简介: 孙源(sunnyxx),目前就职于百度 整理者简介:陈奕龙(子循),目前就职于滴滴出行。 转载者:豆电雨(starain)微信:doudianyu 要实现 weak 属性,首先要搞清楚 weak 属性的特点: weak 此特质表明该属性定义了一种“非拥有关系” (nonowning relationship)。为这种属性设置新值时,设置...

ad4576690的专栏 250

iOS Runtime实现weak属性

weak的特点: 同assign一样,是一种非持有关系,不同在于当属性所指对象被释放后,属性值也会自动清空。 如何实现? 当你初始化一个weak变量的时候,runtime会调用objc_initweak(id * object, id value) id __weak obj1 = obj; objc_initWeak(&obj1, obj); objc_initweak-> objc_storeWeak(object, value) 首先通过value去找它对应的弱引用表

606

Objective-C runtime机制(6)——weak引用的底层实现原理

前言 提起弱引用,大家都知道它的作用: (1)不会添加引用计数 (2)当所引用的对象释放后,引用者的指针自动置为nil 那么,围绕它背后的实现,是怎么样的呢?在许多公司面试时,都会问到这个问题。那么,今天就带大家一起分析一下weak引用是怎么实现的,希望能够搞清楚每一个细节。 Store as weak 当我们要weak引用一个对象,我们可以这么做: int main(int argc, char...

无忘无往 6436
上一篇: ObjC Runtime 中 Weak 属性的实现 (上)
下一篇: Clang学习历程 概述
忘川三
博客等级 码龄12年 208粉丝 45原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值