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

【OC】为category添加weak属性 通过中间类对象持有weak属性-分类属性关联中间类对象的形式完成分类添加weak属性的功能。友友们都知道,分类当中是不能添加property的,因为运行时系统不会自动完成@synthesize,所以需要使用runtime的关联对象形式完成property。看来关联对象的assign修饰和属性一样,离开作用域之后对象销毁,assign指针成为野指针,所以我们还是要想办法完成weak修饰。有copy,有retain,还有assign,看起来足够用了。这里有几种方法,目前使用的先记录下来,有空将剩下的补全。 阅读详情

前言

OC 中的 weak 属性是怎么实现的,为什么在对象释放后会自动变成 nil?本文对这个问题进行了一点探讨。

环境

mac OS Sierra 10.12.4
objc709

参考答案

搜索后发现runtime 如何实现 weak 属性给出了一个参考答案。

runtime 对注册的类, 会进行布局,对于 weak 对象会放入一个 hash 表中。 用 weak 指向的对象内存地址作为 key,当此对象的引用计数为 0 的时候会 dealloc,假如 weak 指向的对象内存地址是 a ,那么就会以 a 为键, 在这个 weak 表中搜索,找到所有以 a 为键的 weak 对象,从而设置为 nil 。

测试

代码

#import <Foundation/Foundation.h>

@interface WeakProperty : NSObject

@property (nonatomic,weak) NSObject *obj;


@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);   

        // 会触发函数 ``id objc_initWeak(id *location, id newObj)``       
        // NSObject *obj = [[NSObject alloc] init];
        // __weak NSObject *obj2 = obj;
        // 会触发函数 ``void objc_copyWeak(id *dst, id *src)``
        // __weak NSObject *obj3 = obj2;
    }
    return 0;
}

结果

对象的 weak 属性调用 setter 时

  • 调用 id objc_storeWeak(id *location, id newObj)
  • 调用 static id storeWeak(id *location, objc_object *newObj)
    …

使用 NSLog 输出 property.obj 属性时

  • 调用 id objc_loadWeakRetained(id *location)

当 dealloc 释放对象时

  • 调用 void objc_destroyWeak(id *location)

相关函数

查看 NSObject.mm 源码发现

  • id objc_storeWeak(id *location, id newObj)
  • id objc_storeWeakOrNil(id *location, id newObj)
  • id objc_initWeak(id *location, id newObj)
  • id objc_initWeakOrNil(id *location, id newObj)
  • void objc_destroyWeak(id *location)

都调用了 static id storeWeak(id *location, objc_object *newObj) , objc_xxxWeakOrNil 多了一点额外的处理,但并不影响整体的理解。而 void objc_destroyWeak(id *location) 在调用 static id storeWeak(id *location, objc_object *newObj) 时 newObj 参数传递的是 nil 这一点与上面提到的参考答案中关于 dealloc 释放对象时,将哈希表中指定的键对应的值设置为 nil 是符合的。

小结

  • storeWeak 函数用于为 weak 属性赋值 (包括销毁)
  • objc_loadWeakRetained 函数用于获取 weak 属性

观察 & 分析

对于函数 storeWeak 主要分析两种情况下的调用

  1. 赋值,即 id objc_storeWeak(id *location, id newObj)
  2. 销毁,即 void objc_destroyWeak(id *location)

而对于 weak 属性的获取主要分析

  1. 函数 id objc_loadWeakRetained(id *location)

观察: id objc_storeWeak(id *location, id newObj)

/** 
 * This function stores a new value into a __weak variable. It would
 * be used anywhere a __weak variable is the target of an assignment.
 * 
 * @param location The address of the weak pointer itself
 * @param newObj The new object this weak ptr should now point to
 * 
 * @return \e newObj
 */
id
objc_storeWeak(id *location, id newObj)
{
    return storeWeak<DoHaveOld, DoHaveNew, DoCrashIfDeallocating>
        (location, (objc_object *)newObj);
}

该函数单纯的调用了 storeWeak 函数

观察: void objc_destroyWeak(id *location)

/** 
 * Destroys the relationship between a weak pointer
 * and the object it is referencing in the internal weak
 * table. If the weak pointer is not referencing anything, 
 * there is no need to edit the weak table. 
 *
 * This function IS NOT thread-safe with respect to concurrent 
 * modifications to the weak variable. (Concurrent weak clear is safe.)
 * 
 * @param location The weak pointer address. 
 */
void
objc_destroyWeak(id *location)
{
    (void)storeWeak<DoHaveOld, DontHaveNew, DontCrashIfDeallocating>
        (location, nil);
}

该函数也只是单纯的调用了 storeWeak 函数

函数 storeWeak 源码

template <HaveOld haveOld, HaveNew haveNew,
          CrashIfDeallocating crashIfDeallocating>
static id 
storeWeak(id *location, objc_object *newObj)
{
    assert(haveOld  ||  haveNew);
    if (!haveNew) assert(newObj == nil);

    Class previouslyInitializedClass = nil;
    id oldObj;
    SideTable *oldTable;
    SideTable *newTable;

    // Acquire locks for old and new values.
    // Order by lock address to prevent lock ordering problems. 
    // Retry if the old value changes underneath us.
 retry:
    if (haveOld) {
        oldObj = *location;
        oldTable = &SideTables()[oldObj];
    } else {
        oldTable = nil;
    }
    if (haveNew) {
        newTable = &SideTables()[newObj];
    } else {
        newTable = nil;
    }

    SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable);

    if (haveOld  &&  *location != oldObj) {
        SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
        goto retry;
    }

    // Prevent a deadlock between the weak reference machinery
    // and the +initialize machinery by ensuring that no 
    // weakly-referenced object has an un-+initialized isa.
    if (haveNew  &&  newObj) {
        Class cls = newObj->getIsa();
        if (cls != previouslyInitializedClass  &&  
            !((objc_class *)cls)->isInitialized()) 
        {
            SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
            _class_initialize(_class_getNonMetaClass(cls, (id)newObj));

            // If this class is finished with +initialize then we're good.
            // If this class is still running +initialize on this thread 
            // (i.e. +initialize called storeWeak on an instance of itself)
            // then we may proceed but it will appear initializing and 
            // not yet initialized to the check above.
            // Instead set previouslyInitializedClass to recognize it on retry.
            previouslyInitializedClass = cls;

            goto retry;
        }
    }

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

    SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);

    return (id)newObj;
}

可以结合 lldb 边调试边对其进行分析,

分析: id objc_storeWeak(id *location, id newObj)

// Template parameters.
enum HaveOld { DontHaveOld = false, DoHaveOld = true };
enum HaveNew { DontHaveNew = false, DoHaveNew = true };

对于模板参数,传递的是 DoHaveOld(true) & DoHaveNew(true)

在64位汇编中,当参数少于7个时, 参数从左到右放入寄存器: rdi, rsi, rdx, rcx, r8, r9。此处 location 和 newObj 分别来自 rdi 和 rsi。

根据注释加地址比较,可知 location 为 指向弱引用的地址,newObj 为要求 弱引用指向的地址,在当前场景下为赋值给 WeakProperty 的 obj 属性的 obj 变量。

在当前场景下即为执行 storeWeak 后,内存地址 0x0000000101301638 上保存的值为 0x0000000101301490

铺垫: 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);
};

关于 spinlock_t,Wiki 上关于 Spinlock 词条的解释如下

In software engineering, a spinlock is a lock which causes a thread trying to acquire it to simply wait in a loop (“spin”) while repeatedly checking if the lock is available. Since the thread remains active but is not performing a useful task, the use of such a lock is a kind of busy waiting. Once acquired, spinlocks will usually be held until they are explicitly released, although in some implementations they may be automatically released if the thread being waited on (that which holds the lock) blocks, or “goes to sleep.

例子

; Intel syntax

locked:                      ; The lock variable. 1 = locked, 0 = unlocked.
     dd      0               ; 定义 lock 变量 默认为 0 

spin_lock:
     mov     eax, 1          ; Set the EAX register to 1. 
                                        ; 设置 EAX 寄存器的值为 1 

     xchg    eax, [locked]   ; Atomically swap the EAX register with
                             ;  the lock variable.
                             ; This will always store 1 to the lock, leaving
                             ;  the previous value in the EAX register.
                             ; 交换 eax 与 lock 变量的值,根据上一步可知,lock 肯定会被赋值为1

     test    eax, eax        ; Test EAX with itself. Among other things, this will
                             ;  set the processor's Zero Flag if EAX is 0.
                             ; If EAX is 0, then the lock was unlocked and
                             ; we just locked it.
                             ; Otherwise, EAX is 1 and we didn't acquire the lock.
                                        ; 将 EAX 与 自身比较,如果 EAX 是 0 则设置 Zeor Flag ,表明当前未加锁,只要加锁操作即可,反之证明已被加锁,不设置 Zero Flag。
     jnz     spin_lock       ; Jump back to the MOV instruction if the Zero Flag is
                             ;  not set; the lock was previously locked, and so
                             ; we need to spin until it becomes unlocked.
                                        ; 如果 Zero Flag 未被设置,则跳转继续 spin_lock
     ret                     ; The lock has been acquired, return to the calling
                             ;  function.
                             ; 获得锁后,继续执行

; 当获得所的操作执行完成后,则 locked 变成 0,另一个线程再次进行 spin_lock 操作 locked 为 0,导致 EAX 为0 ,重新获得了锁,同时 locked 变成 1...

spin_unlock:
     mov     eax, 0          ; Set the EAX register to 0.

     xchg    eax, [locked]   ; Atomically swap the EAX register with
                             ;  the lock variable.

     ret                     ; The lock has been released.

配合 google 的翻译可知,自旋锁会循环等待直到锁可用。

从 weak_table_t 结构体的注释说明了,它会保存 ids 和 keys 的形式保存对象

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

结构体 SideTable 可看做是一个带加锁功能的集合,其中的元素以键值对的形式存放。

在 ObjC 的入口函数 _objc_init 会调用函数 arr_init 来初始化 SideTableBuf 静态变量

正文: id objc_storeWeak(id *location, id newObj)

进入 if (haveOld) 条件

创建新元素,因此 location 地址的原值为 nil

进入 SideTables() 函数

static StripedMap<SideTable>& SideTables() {
    return *reinterpret_cast<StripedMap<SideTable>*>(SideTableBuf);
}

关于 reinterpret_cast 的讨论

reinterpret_cast is the most dangerous cast, and should be used very sparingly. It turns one type directly into another - such as casting the value from one pointer to another, or storing a pointer in an int, or all sorts of other nasty things. Largely, the only guarantee you get with reinterpret_cast is that normally if you cast the result back to the original type, you will get the exact same value (but not if the intermediate type is smaller than the original type). There are a number of conversions that reinterpret_cast cannot do, too. It’s used primarily for particularly weird conversions and bit manipulations, like turning a raw data stream into actual data, or storing data in the low bits of an aligned pointer.

它是一种类型强转的方式

SideTableBuf 是大小为 4096 的 SideTable 缓存数组, oldTable 的赋值相当于在取数组元素,nil 可看成 0 ,即取第一个元素。

同理,haveNew 为 true ,newTable 是以 newObj 为索引在 SideTabBuf 中 查找元素。

调用 SideTable::lockTwo 方法

SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable);

进入 SideTable::lockTwo 方法

template<>
void SideTable::lockTwo<DoHaveOld, DoHaveNew>
    (SideTable *lock1, SideTable *lock2)
{
    spinlock_t::lockTwo(&lock1->slock, &lock2->slock);
}

进入 lockTwo 方法

// Address-ordered lock discipline for a pair of locks.

static void lockTwo(mutex_tt *lock1, mutex_tt *lock2) {
   if (lock1 < lock2) {
       lock1->lock();
       lock2->lock();
   } else {
       lock2->lock();
       if (lock2 != lock1) lock1->lock(); 
   }
}

判断 if (haveOld && *location != oldObj) 条件

haveOld && *location != oldObj ,oldObj 被赋值为 *location 正常情况下,两者相等,不等说明出了问题,算是容错。

判断 if (haveNew && newObj) 条件

haveNew && newObj 根据注释可知也是一个容错的处理

清除旧值

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

以 location 为 key,以 newObj 为值保存到对应的 weak_table_t 的结构体中

调用 SideTable::unlockTwo 方法

SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);

分析: void objc_destroyWeak(id *location)

因为传递的模板参数为 DontHaveNew ,当释放掉旧值后,不会再进入 if (haveNew) 条件中获得新值。

分析: id objc_loadWeakRetained(id *location)

retry:
    // fixme std::atomic this load
    obj = *location;
    ...
    result = obj;
    ... 
    return result

通过 * 取值符号操作 location ,获得弱引用指向的地址。

总结

本文通过对 ObjC 运行时粗略分析,来了解 weak 属性是如何进行存储,使用与释放的。ObjC 的类结构中一个静态的键值对表变量,它保存着对象的弱引用属性,其中的键为指向弱引用的内存地址,值为弱引用,当对象销毁时通过键查表,然后将对应的弱引用从表中移除。

参考

  1. When should static_cast, dynamic_cast, const_cast and reinterpret_cast be used?
  2. The LLDB Debugger
  3. 64位汇编参数传递
  4. Wiki - Spinlock
  5. alignas specifier
  6. 4.3.7. MOV and MVN
iOS内存管理 —— 散列表和弱引用表 iOS内存管理 —— 散列表和弱引用表1. 散列表2. 弱引用表 1. 散列表 散列表的本质是哈希表。散列表在手机上有8张,在模拟器或者其他设备上为64张。散列表里面有锁,引用计数表和弱引用表。其中锁用来保证线程安全,引用计数表是用来存超过最大个数的引用计数的,弱引用表则是用来储存__weak修饰的变量和weak修饰的属性等。如果在整个系统中只有一张散列表,那么在使用过程中开表和关表,为了保证线程安全就要开锁和解锁,如果所有对象挤在一个表里,就会导致性能等消耗。多张表还可以及时回收相关内存。 2. 弱引用表 阅读详情

相关推荐

【iOS分类、关联对象】如何使用关联对象给分类实现一个weak的属性

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

allanGold 2221

__weak详解

object默认的修饰符是__strong,然而在开发中我们也经常使用__weak,用它来解决循环引用的问题:两个对象相互引用无法释放,造成内存泄露。用__weak来破坏一个强引用,来达到正常释放的目的。这种情况常见于block中,但是有没有想过为什么block会强引用block中的对象呢?答案也很简单,我们手动或者编译器帮我们对block进行了copy的操作,所以block会对其块中的对象就行强...

lixuezhi 1万+

iOS 经典全部面试题(上)

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

2401_84434237的博客 1010

从RunTime源码回看weak底层实现

文章目录关于我的仓库前言准备工作数据模型SideTables()StripedMap补充知识:哈希桶补充知识:模版函数补充知识:alignas()补充知识:重载SideTablespinlock_t slock【自旋锁】补充知识:锁补充知识:分离锁&&拆分锁补充知识:自旋锁补充知识:并行&&并发苹果的抉择RefcountMap refcnts【存放引用计数】Dens...

KevinAshen的博客 693

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

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

╰つ栺尖篴夢ゞ 893

AI Agent核心架构解析:从Tool、Skill到MCP协议的智能体构建实战

在人工智能领域,大语言模型(LLM)赋予了AI自主规划与决策的能力,推动了智能体(Agent)技术的演进。其核心原理在于构建一个具备感知、规划、行动与反思能力的自主系统。这一架构的技术价值在于将AI从被动应答升级为主动执行复杂任务的工作伙伴,显著提升了自动化水平与问题解决效率。应用场景广泛覆盖了市场分析、自动化办公、智能客服与垂直领域集成等。本文聚焦于实现这一能力的关键组件:基础工具(Tool)、组合技能(Skill)与标准化协议(MCP)。其中,Skill通过封装多个Tool形成可复用的高级能力,而MCP

chuannaoxuan4674的博客 316

OC 看objc源码认识weak

weak修饰有什么用? 声明为weak的指针,weak指针指向的对象一旦被释放,weak的指针都将被赋值为nil #####weak的赋值与访问 NSObject *obj = [[NSObject alloc] init]; // weak的三种赋值情况 // (1)属性赋值 _weakObj = obj; // 编译为:objc_storeWeak...

weixin_34375251的博客 383

Windows 11下WSL2安装CUDA避坑指南:从驱动版本匹配到环境变量配置

本文详细解析了在Windows 11的WSL2中搭建CUDA开发环境的完整流程与核心避坑要点。关键在于理解“驱动在Windows,工具链在Linux”的独特架构,并确保显卡驱动与CUDA Toolkit版本严格匹配。指南涵盖了从驱动验证、WSL-Ubuntu专用仓库安装、环境变量配置到GCC版本冲突、GPU算力不匹配等常见问题的解决方案,帮助开发者高效构建稳定的深度学习与高性能计算环境。

smartcontract5的博客 465

iOS底层原理:weak的实现原理

作者丨夜幕降临耶链接:https://juejin.im/post/5e7a322f6fb9a07ca24f79bb来源:掘金在iOS开发过程中,会经常...

iOS_开发 720

强化学习_06_pytorch-TD3实践(BipedalWalkerHardcore-v3)

DDPG的critic会高估, 从而导致actor策略失败。TD3是增加了三个关键技巧优化DDPG。经过优化后的TD3(Twin Dalayed DDPG 双延迟深度确定性策略梯度算法)适合于具有高维连续动作空间的任务。

RL小站 2688

基于Web的智能家居(源码+开题)

实践上,智能家居系统的普及和应用将极大改善人们的居住体验,提高生活质量,同时也有助于促进智能家居产业链的发展和完善,为社会经济发展注入新的活力。此外,本研究还将对基于Web的智能家居系统的用户体验进行深入研究,通过用户调研和数据分析,不断优化系统功能和界面设计,提升用户的使用体验和满意度。针对以上问题,本研究将综合运用物联网、云计算、大数据等新一代信息技术,通过系统架构设计、功能模块开发、安全性保障、用户体验优化等多个方面的研究工作,力求为基于Web的智能家居系统的研发和应用提供全面而有效的解决方案。

sryue06的博客 1902

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

导语在上一篇中简单分析了 Weak 属性是如何被存储,获取和销毁的,其中的 SideTable 结构体当做黑盒进行处理。本文尝试对 SideTable 的结构进行一些分析。观察struct SideTable { spinlock_t slock; RefcountMap refcnts; weak_table_t weak_table; SideTable() {

ioSugar 1368

保姆级避坑指南:在Ubuntu 22.04上成功复现VMamba模型(附完整环境依赖清单)

本文提供了在Ubuntu 22.04上复现VMamba模型的详细指南,包括环境准备、CUDA与PyTorch的精确匹配、依赖安装的疑难杂症解决以及完整环境清单。特别针对Linux系统下的环境复现问题,提供了实用的避坑技巧和验证方法,帮助开发者高效完成VMamba模型的部署。

weixin_29083373的博客 262

iOS Principle:weak

iOS的weak的原理

u013712343的博客 89

【Python基础】FastAPI 从入门到项目实战操作详解

【Python基础】FastAPI从入门到使用实战操作详解

congge_study的博客 434

《大数据最全面试题-Offer直通车》目录

做好这些不用担心试用期不通过:程序员入职新公司如何快速上手项目本人遇到大数据面试题和参考答案(超过1万字精华版)大数据面试英文自我介绍参考(万字长文)大数据运维应用场景面试题汇总及参考答案(持续更新)万字数据仓库面试题及参考答案数据仓库数据质量监控和处理方法最佳实践数据仓库建模方法万字详解Doris的3种数据模型详解和数据仓库每一层的模型选用大数据面试临阵磨枪不知看什么?看这份心理就有底了-大数据常用技术栈常见面试100道题最全Hive面试题2024年(2万字详解)Elasticsearch 面试题及参考答

大模型大数据攻城狮的专栏 2633

面试题

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

u011552290的博客 405

打开 AI 投研新时代 —— 深度解析 GitHub 高星项目 daily_stock_analysis

GitHub热门开源项目"daily_stock_analysis"是一款AI驱动的智能股票分析工具,能自动抓取行情数据、分析新闻语义并生成结构化报告。该项目采用Python开发,通过GitHub Actions实现全自动运行,支持邮件、企业微信等多渠道推送。核心功能包括AI决策仪表盘、多数据源整合和零成本部署方案,特别适合需要自动化投研辅助的个人投资者。项目强调其仅为分析工具而非投资建议,提醒用户注意AI模型的潜在误差。该工具通过智能化处理重复工作,帮助投资者提升决策效率,成为量化投资

陈奕昆的博客 1522

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

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

weixin_33796177的博客 307

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

iOS进阶 - Runtime

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

hiccuptang 224

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

移动端开发iOS系列之Runtime

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

henry_lei的博客 459

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

(转)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
上一篇: Masonary-简化自动布局
下一篇: ObjC Runtime 中 Weak 属性的实现 (中)
忘川三
博客等级 码龄12年 208粉丝 45原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值