浅谈iOS之weak底层实现原理

iOS 内存管理浅谈 iOS 内存管理理解 阅读详情

前言

在iOS开发过程中,会经常使用到一个修饰词“weak”,使用场景大家都比较清晰,用于一些对象相互引用的时候,避免出现强强引用,对象不能被释放,出现内存泄露的问题。

weak 关键字的作用弱引用,所引用对象的计数器不会加一,并在引用对象被释放的时候自动被设置为 nil。

weak底层原理

1.weak编译解析

首先需要看一下weak编译之后具体出现什么样的变化,通过Clang的方法把weak编译成C++

int main(){
    NSObject *obj = [[NSObject alloc] init];
    id __weak obj1 = obj;
}

编译之后的weak,通过objc_ownership(weak)实现weak方法,objc_ownership字面意思是:获得对象的所有权,是对对象weak的初始化的一个操作。

在使用clang编译过程中会报错误,使用下方的方法编码编译出现error
clang -rewrite-objc -fobjc-arc -stdlib=libc++ -mmacosx-version-min=10.7 -fobjc-runtime=macosx-10.7 -Wno-deprecated-declarations main.m

int main(){
    NSObject *obj = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));
    id __attribute__((objc_ownership(weak))) obj1 = obj;
}
2.weak的实现原理

第一、通过weak编译解析,可以看出来weak通过runtime初始化的并维护的;
第二、weak和strong都是Object-C的修饰词,而strong是通过runtime维护的一个自动计数表结构。
综上:weak是有Runtime维护的weak表。

在runtime源码中,可以找到’objc-weak.h’和‘objc-weak.mm’文件,并且在objc-weak.h文件中关于定义weak表的结构体以及相关的方法。

2.1.weak表

weak_table_t是一个全局weak 引用的表,使用不定类型对象的地址作为 key,用 weak_entry_t 类型结构体对象作为 value 。其中的 weak_entries 成员

/**
 * 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指针   weak_entries的对象
    size_t    num_entries;              // weak对象的存储空间
    uintptr_t mask;                      //参与判断引用计数辅助量
    uintptr_t max_hash_displacement;    //hash key 最大偏移值
};

weak全局表中的存储weak定义的对象的表结构weak_entry_t,weak_entry_t是存储在弱引用表中的一个内部结构体,它负责维护和存储指向一个对象的所有弱引用hash表。其定义如下:

typedef objc_object ** weak_referrer_t;
struct weak_entry_t {
    DisguisedPtr<objc_object> referent;  //范型
    union {
        struct {
            weak_referrer_t *referrers;
            uintptr_t        out_of_line : 1;
            uintptr_t        num_refs : PTR_MINUS_1;
            uintptr_t        mask;
            uintptr_t        max_hash_displacement;
        };
        struct {
            // out_of_line=0 is LSB of one of these (don't care which)
            weak_referrer_t  inline_referrers[WEAK_INLINE_COUNT];
        };
    }
}

在 weak_entry_t 的结构中,DisguisedPtr referent 是对泛型对象的指针做了一个封装,通过这个泛型类来解决内存泄漏的问题。从注释中写 out_of_line 成员为最低有效位,当其为0的时候, weak_referrer_t 成员将扩展为多行静态 hash table。其实其中的 weak_referrer_t 是二维 objc_object 的别名,通过一个二维指针地址偏移,用下标作为 hash 的 key,做成了一个弱引用散列。

out_of_line:最低有效位,也是标志位。当标志位 0 时,增加引用表指针纬度。
num_refs:引用数值。这里记录弱引用表中引用有效数字,因为弱引用表使用的是静态 hash 结构,所以需要使用变量来记录数目。
mask:计数辅助量。
max_hash_displacement:hash 元素上限阀值。
其实 out_of_line 的值通常情况下是等于零的,所以弱引用表总是一个 objc_objective 指针二维数组。一维 objc_objective 指针可构成一张弱引用散列表,通过第三纬度实现了多张散列表,并且表数量为 WEAK_INLINE_COUNT 。

objc_object是weak_entry_t表中weak弱引用对象的范型对象的结构体结构。

struct objc_object {
private:
    isa_t isa;

public:

    // ISA() assumes this is NOT a tagged pointer object
    Class ISA();

    // getIsa() allows this to be a tagged pointer object
    Class getIsa();

    // initIsa() should be used to init the isa of new objects only.
    // If this object already has an isa, use changeIsa() for correctness.
    // initInstanceIsa(): objects with no custom RR/AWZ
    // initClassIsa(): class objects
    // initProtocolIsa(): protocol objects
    // initIsa(): other objects
    void initIsa(Class cls /*indexed=false*/);
    void initClassIsa(Class cls /*indexed=maybe*/);
    void initProtocolIsa(Class cls /*indexed=maybe*/);
    void initInstanceIsa(Class cls, bool hasCxxDtor);

    // changeIsa() should be used to change the isa of existing objects.
    // If this is a new object, use initIsa() for performance.
    Class changeIsa(Class newCls);

    bool hasIndexedIsa();
    bool isTaggedPointer();
    bool isClass();

    // object may have associated objects?
    bool hasAssociatedObjects();
    void setHasAssociatedObjects();

    // object may be weakly referenced?
    bool isWeaklyReferenced();
    void setWeaklyReferenced_nolock();

    // object may have -.cxx_destruct implementation?
    bool hasCxxDtor();

    // Optimized calls to retain/release methods
    id retain();
    void release();
    id autorelease();

    // Implementations of retain/release methods
    id rootRetain();
    bool rootRelease();
    id rootAutorelease();
    bool rootTryRetain();
    bool rootReleaseShouldDealloc();
    uintptr_t rootRetainCount();

    // Implementation of dealloc methods
    bool rootIsDeallocating();
    void clearDeallocating();
    void rootDealloc();

private:
    void initIsa(Class newCls, bool indexed, bool hasCxxDtor);

    // Slow paths for inline control
    id rootAutorelease2();
    bool overrelease_error();

#if SUPPORT_NONPOINTER_ISA
    // Unified retain count manipulation for nonpointer isa
    id rootRetain(bool tryRetain, bool handleOverflow);
    bool rootRelease(bool performDealloc, bool handleUnderflow);
    id rootRetain_overflow(bool tryRetain);
    bool rootRelease_underflow(bool performDealloc);

    void clearDeallocating_weak();

    // Side table retain count overflow for nonpointer isa
    void sidetable_lock();
    void sidetable_unlock();

    void sidetable_moveExtraRC_nolock(size_t extra_rc, bool isDeallocating, bool weaklyReferenced);
    bool sidetable_addExtraRC_nolock(size_t delta_rc);
    bool sidetable_subExtraRC_nolock(size_t delta_rc);
    size_t sidetable_getExtraRC_nolock();
#endif

    // Side-table-only retain count
    bool sidetable_isDeallocating();
    void sidetable_clearDeallocating();

    bool sidetable_isWeaklyReferenced();
    void sidetable_setWeaklyReferenced_nolock();

    id sidetable_retain();
    id sidetable_retain_slow(SideTable *table);

    bool sidetable_release(bool performDealloc = true);
    bool sidetable_release_slow(SideTable *table, bool performDealloc = true);

    bool sidetable_tryRetain();

    uintptr_t sidetable_retainCount();
#if !NDEBUG
    bool sidetable_present();
#endif
};

总之:
1.weak_table_t(weak 全局表):采用hash(哈希表)的方式把所有weak引用的对象,存储所有引用weak对象
2.weak_entry_t(weak_table_t表中hash表的value值,weak对象体):用于记录hash表中weak对象
3.objc_object(weak_entry_t对象中的范型对象,用于标记对象weak对象):用于标示weak引用的对象。

详细讲解weak存储对象结构,对接下来对weak操作使用可以更加清晰的理解weak的使用。

2.2.weak底层实现原理

在runtime源码中的NSObject.mm文件中找到了关于初始化和管理weak表的方法

  • 初始化weak表方法
//初始化weak表
/** 
 * Initialize a fresh weak pointer to some object location. 
 * It would be used for code like: 
 *
 * (The nil case) 
 * __weak id weakPtr;
 * (The non-nil case) 
 * NSObject *o = ...;
 * __weak id weakPtr = o;
 * 
 * @param addr Address of __weak ptr. 
 * @param val Object ptr. 
 */
id objc_initWeak(id *addr, id val)
{
    *addr = 0;
    if (!val) return nil;
    return objc_storeWeak(addr, val); // 存储weak对象
}
  • 存储weak对象的方法
/** 
 * 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)
{
    id oldObj;
    SideTable *oldTable;
    SideTable *newTable;
    spinlock_t *lock1;
#if SIDE_TABLE_STRIPE > 1
    spinlock_t *lock2;
#endif

    // 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:
    oldObj = *location;
    
    oldTable = SideTable::tableForPointer(oldObj);
    newTable = SideTable::tableForPointer(newObj);
    
    lock1 = &newTable->slock;
#if SIDE_TABLE_STRIPE > 1
    lock2 = &oldTable->slock;
    if (lock1 > lock2) {
        spinlock_t *temp = lock1;
        lock1 = lock2;
        lock2 = temp;
    }
    if (lock1 != lock2) spinlock_lock(lock2);
#endif
    spinlock_lock(lock1);

    if (*location != oldObj) {
        spinlock_unlock(lock1);
#if SIDE_TABLE_STRIPE > 1
        if (lock1 != lock2) spinlock_unlock(lock2);
#endif
        goto retry;
    }

    weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
    newObj = weak_register_no_lock(&newTable->weak_table, newObj, location);
    // 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 = newObj;
    
    spinlock_unlock(lock1);
#if SIDE_TABLE_STRIPE > 1
    if (lock1 != lock2) spinlock_unlock(lock2);
#endif

    return newObj;
}
  • 旧对象解除注册操作 weak_unregister_no_lock

该方法主要作用是将旧对象在 weak_table 中接触 weak 指针的对应绑定。根据函数名,称之为解除注册操作。从源码中,可以知道其功能就是从 weak_table 中接触 weak 指针的绑定。而其中的遍历查询,就是针对于 weak_entry 中的多张弱引用散列表。

  • 新对象添加注册操作 weak_register_no_lock

这一步与上一步相反,通过 weak_register_no_lock 函数把心的对象进行注册操作,完成与对应的弱引用表进行绑定操作。

  • 初始化弱引用对象流程一览
    弱引用的初始化,从上文的分析中可以看出,主要的操作部分就在弱引用表的取键、查询散列、创建弱引用表等操作,可以总结出如下的流程图:

111.png

SideTable 这个结构体,是对weak_table_t表的再次封装操作,避免对weak_table_t直接操作,SideTable使用更加方便。

class SideTable {
private:
    static uint8_t table_buf[SIDE_TABLE_STRIPE * SIDE_TABLE_SIZE];

public:
    spinlock_t slock;
    RefcountMap refcnts;
    weak_table_t weak_table;

    SideTable() : slock(SPINLOCK_INITIALIZER)
    {
        memset(&weak_table, 0, sizeof(weak_table));
    }
    
    ~SideTable() 
    {
        // never delete side_table in case other threads retain during exit
        assert(0);
    }

    static SideTable *tableForPointer(const void *p) 
    {
#     if SIDE_TABLE_STRIPE == 1
        return (SideTable *)table_buf;
#     else
        uintptr_t a = (uintptr_t)p;
        int index = ((a >> 4) ^ (a >> 9)) & (SIDE_TABLE_STRIPE - 1);
        return (SideTable *)&table_buf[index * SIDE_TABLE_SIZE];
#     endif
    }

    static void init() {
        // use placement new instead of static ctor to avoid dtor at exit
        for (int i = 0; i < SIDE_TABLE_STRIPE; i++) {
            new (&table_buf[i * SIDE_TABLE_SIZE]) SideTable;
        }
    }
};

总之根据以上对weak进行存储的过程可以通过下边的流程图详细的描述出来
333.png

3.weak释放为nil过程

weak被释放为nil,需要对对象整个释放过程了解,如下是对象释放的整体流程:
1、调用objc_release
2、因为对象的引用计数为0,所以执行dealloc
3、在dealloc中,调用了_objc_rootDealloc函数
4、在_objc_rootDealloc中,调用了object_dispose函数
5、调用objc_destructInstance
6、最后调用objc_clear_deallocating。

对象准备释放时,调用clearDeallocating函数。clearDeallocating函数首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。

在对象被释放的流程中,需要对objc_clear_deallocating方法进行深入的分析

void objc_clear_deallocating(id obj) 
{
    assert(obj);
    assert(!UseGC);
    if (obj->isTaggedPointer()) return;
    obj->clearDeallocating();
}

//执行 clearDeallocating方法
inline void objc_object::clearDeallocating()
{
    sidetable_clearDeallocating();
}
// 执行sidetable_clearDeallocating,找到weak表中的value值
void  objc_object::sidetable_clearDeallocating()
{
    SideTable *table = SideTable::tableForPointer(this);
    // clear any weak table items
    // clear extra retain count and deallocating bit
    // (fixme warn or abort if extra retain count == 0 ?)
    spinlock_lock(&table->slock);
    RefcountMap::iterator it = table->refcnts.find(this);
    if (it != table->refcnts.end()) {
        if (it->second & SIDE_TABLE_WEAKLY_REFERENCED) {
            weak_clear_no_lock(&table->weak_table, (id)this);
        }
        table->refcnts.erase(it);
    }
    spinlock_unlock(&table->slock);
}

对weak置nil的操作最终调用执行weak_clear_no_lock方法用于执行置nil的操作。执行方法如下:

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

objc_clear_deallocating该函数的动作如下:

1、从weak表中获取废弃对象的地址为键值的记录
2、将包含在记录中的所有附有 weak修饰符变量的地址,赋值为nil
3、将weak表中该记录删除
4、从引用计数表中删除废弃对象的地址为键值的记录

其实Weak表是一个hash(哈希)表,然后里面的key是指向对象的地址,Value是Weak指针的地址的数组。

###总结
weak是Runtime维护了一个hash(哈希)表,用于存储指向某个对象的所有weak指针。weak表其实是一个hash(哈希)表,Key是所指对象的地址,Value是weak指针的地址(这个地址的值是所指对象指针的地址)数组。

080、多轴运动控制:插补器设计(螺旋线插补) 本文分享了螺旋线插补器设计的实战经验。针对五轴点胶机Z轴抖动问题,作者分析了螺旋线数学本质,提出基于归一化时间参数的速度规划方法,强调各轴严格同步的重要性。文章详细介绍了速度分解、加减速处理、嵌入式优化技巧(查表法、定点化),并总结了起点闭合、速度波动、数值处理等常见问题解决方案。最后给出5条实用建议:注重速度规划而非纯数学推导、预先仿真、保留调试接口、考虑机械谐振、防范编码器丢脉冲。通过优化插补周期、电子齿轮比和S形速度曲线,成功将设备良率从60%提升至95%。 阅读详情

相关推荐

密码学新手必看:三大顶级会议论文下载与阅读指南(附完整资源链接)

本文为密码学新手提供了系统性的三大顶级会议(Crypto、Eurocrypt、Asiacrypt)论文学习指南。内容涵盖如何高效获取最新论文资源、结构化精读论文的方法论,以及将理论转化为代码实践的路径,旨在帮助初学者快速构建密码学知识体系并应用于研究。

weixin_29214691的博客 318

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

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

iOS_开发 721

基于CRU和WorldClim的高分辨率(1km)气候数据生成

基于CRU和WorldClim的高分辨率(1km)气候数据生成

lhq7842955的博客 1693

iOS底层weak的实现原理

weak是弱引用,所引用对象的计数器不会加一,并在引用对象被释放的时候自动被设置为nil。那么weak的原理是什么呢?weak表其实是一个hash(哈希)表 (字典也是hash表),Key是所指对象的地址,Value是weak指针的地址数组。通常用于解决循环引用问题。下面就分析一下weak的工作原理。 weak 实现原理的概括 Runtime维护了一个weak表,用于存储指向某个对象的所有we...

gcs的博客 3964

AI 生成 ECU 功能测试用例

本文分享了AI辅助汽车电子测试的实战经验。作者以某车企车身控制器(BCM)项目为例,对比了传统测试用例编写方式(需100小时)与AI辅助方式(仅3个工作日)的效率差异,实现76%的效率提升。文章详细展示了从需求文档生成测试用例的全过程,包括需求文档示例、AI提示词模板和AI生成的完整测试用例表格(涵盖正常功能、边界条件、异常场景等测试类型)。通过具体案例说明如何利用AI快速生成初稿后人工审核优化的方法,为ECU测试工程师提供了可复用的效率提升方案。

小明的博客 879

PlutoSDR驱动安装避坑指南:从设备管理器异常到MATLAB 2023b完美识别

本文是一份详细的PlutoSDR驱动安装与MATLAB 2023b集成排错指南。针对Windows系统下常见的设备管理器异常、驱动兼容性问题,提供了从旧驱动清理、手动指定驱动文件到USB连接稳定性排查的完整解决方案。重点阐述了如何通过离线安装硬件支持包,并利用MATLAB命令进行连接测试与射频信号验证,最终实现设备的完美识别与稳定调用。

weixin_29159441的博客 428

【iOS】—— 属性关键字及weak关键字底层原理

使用宏和常量所占用的内存差别不大,宏定义的是常量,常量都放在常量区,只会生成一份内存编译时刻:宏是预编译(编译之前处理),const是编译阶段。导致使用宏定义过多的话,随着工程越来越大,编译速度会越来越慢宏不做检查,不会报编译错误,只是替换,const会编译检查,会报编译错误。宏能定义一些函数,方法。const不能。1.通过计算对象地址的哈希值, 来从SideTables中获取对应的SideTable. 哈希值重复的对象的引用计数存储在同一个SideTable里.2.SideTable使用。

m0_62386635的博客 2148

Claude Code 错误解决方案 API Error: 前缀通用错误

本文分析了Claude Code与Anthropic API通信时出现的通用错误处理问题。当API返回未分类错误时,系统会直接显示"API Error: "前缀的原始错误信息。通过错误链路追踪发现,这是由于未匹配特定错误类型时触发了通用兜底处理逻辑。文章提供了三种解决方案:查看具体错误内容、启用调试日志以及提交问题反馈,并建议用户关注错误码、使用/compact命令处理上下文超限问题。最后强调这些未分类错误是预期行为,用户应通过错误消息中的线索自行排查或反馈开发者。

多媒体编程、网络编程、系统编程、网络安全编程、驱动编程 1349

【iOS】—— weak的基本原理

1、weak的原理在于底层维护了一张结构的hash表,key是所指对象的地址,value是weak指针的地址数组。2、weak关键字的作用是弱引用,所引用对象的计数器不会加1,并在引用对象被释放的时候自动被设置为nil。3、对象释放时,调用函数根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。4、文章中介绍了SideTable、、这样三个结构,它们之间的关系如下图所示。httpshttps。......

m0_55124878的博客 1715

Allegro如何导入芯片的Pin Delay?

Allegro在做等长时,需要导入芯片的Pin Delay才能做真正的等长。因为有些芯片内部的引脚本身就是不等长的,例如海思的部分芯片。在右栏找到芯片U1,然后在Pin Delay栏下找一个PIN脚随便输入一个数值,这一步的目的是导出Pin Delay.csv文件的格式。4、用这份文件的格式,重新编辑需要导入芯片的Pin Delay,然后选择菜单File→Import→Pin delay。2、回到PCB,选择菜单File→Export→Pin delay,导出Pin Delay。

bhxc809751502的博客 5141

【应用】OpenClaw玩转有道云笔记

功能名称核心原理实操示例(直接复制可用)实操避坑+补充说明网页剪藏插件自动抓取网页正文、下载图片,通过 API 发送到你的有道云笔记,内联图片避免防盗链失效帮我把这个网页剪藏到云笔记:https://example.com/article1. 避坑:知乎、公众号等需登录的网站,先在浏览器登录再剪藏;2. 原理:自动过滤广告,只留正文;3. Twitter/X 剪藏需额外配置 APIFY_API_TOKEN(见1.2.4)创建笔记。

海兰 714

「iOS」————weak底层原理

本文详细介绍了iOS中weak关键字的实现原理。weak是一种弱引用,其引用的对象计数器不会增加,且在对象释放时自动置为nil。weak的本质是通过哈希表实现,以引用对象地址为key,weak指针地址集合为value。实现过程分为初始化、添加引用和释放三个阶段。

2301_80253909的博客 1347

【Unity】 预制体Prefab使用说明

在 Unity 中,Prefab 是一个可以预先制作并重复使用的 GameObject 或组件集合,Prefab 中的 GameObject 可以有子对象和其他组件。

灵境引路人 2849

【iOS开发】浅谈iOS之weak底层实现原理

浅谈iOS之weak底层实现原理

Hanrovey Blog 444

廖雪峰Python教程[完整版]

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

efm32 入门.pdf

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

浅谈weak与unowned

iOS开发中,weak和unowned是解决循环引用的两种机制。Objective-C通过全局weak哈希表实现,对象销毁时自动置空weak指针;Swift则采用Sidetable方案,将weak引用计数分离存储,通过惰性置空机制延迟处理。unowned在Swift中会保持对象部分存活,访问时若对象已销毁会触发崩溃但避免野指针。虽然unowned性能略优,但weak更安全可靠,推荐优先使用weak,仅在必要时考虑unowned。两种方式都通过引入额外引用计数而非增加强引用来打破循环引用。

qq_63330040的博客 717

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

J_Knight_ iOS 高级面试题 基础题解答

最近从公司离职了,准备了一下接下来的面试,翻出了J_Knight的高级面试题复习了一下面试 iOS 基础题 分类和扩展有什么区别?可以分别用来做什么?分类有哪些局限性?分类的结构体里面有哪些成员? 区别: extension在编译期决议 它伴随类的产生而产生,亦随之一起消亡 category则完全不一样,它是在运行期决议的 extension可以添加实例变量,而category是无法添加实例...

weixin_34411563的博客 729

iOS开发:浅谈内存管理

内存分布 堆:一般由程序员分配释放,若程序员不释放,则可能会引起内存泄漏。其类似于链表。 栈:由编译器自动分配释放,存放函数的参数值,局部变量等值。其操作方式类似于数据结构中的栈。 内存管理原理 OC语言使用引用计数来管理内存,每个对象都有一个可以递增和递减的计数器。如果有其他对象持用该对象的话,那该对象就递增其引用计数;用完以后就递减其计数,当引用计数为0时,就销毁该对象。 MRC与ARC ...

ctguqsx的专栏 342

ios 添加block 类别_浅谈iOS之Block

前言iOS 4.0版本中,块不包含Objective-C中的新编程发现。 它们也存在于其他编程语言中(例如Javascript)和其他名称,例如Closures。 在iOS中,它们首次出现在4.0版本中,从那时起它们就已经被广泛接受和使用。 在随后的iOS版本中,Apple重新编写或更新了许多框架方法,因此它们采用了块,而且块似乎部分是代码编写方式的未来。 但他们到底有什么关系呢?块是添加到C,O...

weixin_34140828的博客 474

浅谈iOS内存管理

iOS手动内存管理和ARC

Jashion 586

浅谈iOS之Block

前言 iOS 4.0版本中,块不包含Objective-C中的新编程发现。 它们也存在于其他编程语言中(例如Javascript)和其他名称,例如Closures。 在iOS中,它们首次出现在4.0版本中,从那时起它们就已经被广泛接受和使用。 在随后的iOS版本中,Apple重新编写或更新了许多框架方法,因此它们采用了块,而且块似乎部分是代码编写方式的未来。 但他们到底有什么关系呢? 块是添加...

Future_One的专栏 515

iOS 浅谈Block外部的变量传递

背景介绍在iOS开发过程中,我们经常会用到block这个对象,它可以作为函数的回调、类的属性等出现在我们的项目中,作为回调,block相对于代理方法,代码量更少,使用起来更方便,还可以使用与block定义同一作用范围的变量。当然,block的使用会让代码的逻辑不如使用代理清晰,萝卜青菜,各有所爱。下面我就对于block可以使用与它在同一作用范围的变量这一特性展开说一下自己的理解。 正文先看这样一段代

夏日的博客 2399

华山论剑之浅谈iOS瀑布流

心灵鸡汤可不是谁想喝就喝的! --------------栋哥 看完千篇一律的UI布局之后,当我们看到瀑布流的布局是不是觉得有种耳目一新的感觉呢?今天我们就说一下如果实现瀑布流,对于瀑布流,现在iOS中总共存在着三种实现方法. 1.实现瀑布流的布局,我们需要计算每一张图片的尺寸大小,然后根据列数布局到我们的UIScrollView上去 2.UITableView实现瀑布流效果,就是每一列都是一个视图. 3.UICollectionView实现瀑布流就是对UICollectionView的Flow.

得不到的永远是骚栋. 1087

iOS之block浅谈

前言 ios4.0系统已开始支持block,在编程过程中,block被Obj-C看成是对象,它封装了一段代码,这段代码可以在任何时候执行。block可以作为函数参数或者函数的返回值,而其本身又可以带输入参数或返回值。它和传统的函数指针很类似,但是有区别:block是inline的,并且它对局部变量是只读的。 block和函数的相似性:(1)可以保存代码(2)有返回值(3)有形参(4)调用方

czkyes的博客 4102

ARC浅谈

1. Ownership Qualifiers __strong -- 不使用任何修饰符的情况下,默认是__strong。在ARC环境下,编译器会自动为__strong修饰的对象指针生成恰当的release代码,比如出了对象所属作用域,或者发生指针赋值时。__weak -- 使用__weak修饰符,编译器(准确说是Runtime System)会记录被修饰的指针,当指向对象被释放时,

MrLiuZhe'Blog 859

浅谈 AutoreleasePool 的实现原理

面试题: autorelease 对象什么时候释放。 autorelease 的本质就是延迟调用 release 方法 在 MRC 的环境下,可以通过调用 [obj autorelease] 将对象添加到当前的 autoreleasepool 中,来延迟释放内存; 在 ARC的环境下,当我们创建一个对象,可以通过 __autoreleasing 修饰符,会将对象添加到当前的 autorelease...

weixin_33929309的博客 791

华山论剑之浅谈iOS的 target - action设计模式 和 代理模式 以及Bolck

当我们觉得一件事情是理所当然的时候,那正是我们失去它的时候. target - action设计模式 和 代理模式 以及Bolck 不管是哪一个,都是我们在编程过程中比较常用的,但是真正有多少人还记得他们的实现原理的?今天我就浅谈一下target - action设计模式 和 代理模式 以及Bolck 这三个模块.在谈之前,我们先看一张搞笑的图片,让我们对三种模式有个初步的了解. target - action设计模式 target - action设计模式 是一种比较常见的设计模式,比如我们在用.

得不到的永远是骚栋. 675

iOS分段选择器、旅行App、标度尺、对对碰小游戏、自定义相册等源码

iOS精选源码 企业级开源项目,模仿艺龙旅行App 标签选择器--LeeTagView CSSegmentedControl常用的分段选择器,简单易用! 仿微信左滑删除 IOS左滑返回 输入框 iOS 基于PhotoKit框架的自定义相册 JHDraw - 画线,画虚线,画五角星,画矩形,画虚线矩形 标度尺 对对碰小游戏 iOS优质博客 iOS无痕埋点方案分享探究 前言当前互联...

weixin_30824479的博客 86
上一篇: 浅谈iOS之Block
下一篇: 解决Xcode Simulator 黑屏问题
Flame_Dream
博客等级 码龄12年 35粉丝 82原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值