iOS内存管理

属性关键字

assign 修饰基本类型,修饰对象会造成野指针

strong ARC下持有引用

retain MRC下的持有引用

保留新值,释放旧值,赋值新值,指向同一块内存区域

copy

复制新值,释放旧值,赋值新值
修饰对象时,复制的新对象是不可变对象,故修饰不可变对象
赋值对象不可变,则浅拷贝,两者指向同一块内存区域
赋值对象可变,则是深拷贝,两者指向不同内存,互不影响

weak 非持有引用,指向对象释放,自动置空

readonly 只读 只有getter方法,但可直接访问成员变量

readwrite 可读可写

atomic 原子操作 setter/getter方法加锁,效率低###nonatomic 非原子操作,无锁,效率高

内存问题

内存局部暴涨

短时间内申请很多局部变量,造成内存急剧增长
使用自动释放池包裹代码,及时释放,解除内存占用

内存泄漏

实际是持有对象不释放

实际场景

1、Block
2、NStimer
3、Delegate
4、通知
5、单例持有

解决方式

1、使用weak弱引用打破循环
2、合适时机手动置空,打破循环

过度释放

释放的变量再次释放,会造成崩溃

weak实现原理

内存布局

struct SideTable {
	spinlock_t slock; //锁 自旋锁(优先级反转问题)->互斥锁
	RefcountMap refcnts; //引用计数表(无isa优化或者引用计数益处)
	weak_table_t weak_table; //弱引用表(weak的实现核心)
}

struct weak_table_t {
	weak_entry_t *weak_entries; //hash数组,存在弱引用的对象的信息
	size_t    num_entries; //hash数组元素个数
	uintptr_t mask; //hash数组长度 - 1,参与hash运算
	uintptr_t max_hash_displacement; //hash最大冲突次数
}

#define WEAK_INLINE_COUNT 4
#define REFERRERS_OUT_OF_LINE 2
struct weak_entry_t {
	DisguisedPtr<objc_object> referent; // 被弱引用的对象
	// 引用该对象的对象列表,联合。 引用个数小于4,用inline_referrers数组。 用个数大于4,用动态数组weak_referrer_t *referrers
	union {
		struct {
			weak_referrer_t *referrers;  // 超过4个的弱引用该对象的对象指针地址的动态hash数组
			uintptr_t out_of_line_ness : 2;   // 是否使用动态hash数组标记位
			uintptr_t num_refs : PTR_MINUS_2;  // hash数组中的元素个数
			uintptr_t mask; // hash数组长度-1,会参与hash计算				uintptr_t max_hash_displacement;  // hash冲突最大次数
		 };
		struct {
			weak_referrer_t inline_referrers[WEAK_INLINE_COUNT]; // 4个一下弱引用的固定存储表
    		};
	};

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

weak实现逻辑&方法

weak_register_no_lock方法添加弱引用
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; //指向当前对象的弱引用地址的地址
    // 如果当前对象为空或者当前对象采用了taggedPointer优化,直接返回对象地址,不需要弱引用
    if (!referent  ||  referent->isTaggedPointer()) return referent_id;
    // 确保被引用的对象可用(没有在析构,同时应该支持弱引用)
    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);
    }
    // 正在析构的对象,不能够被弱引用,是否抛出异常取决于crashIfDeallocating
    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;
        }
    }
    // 在 weak_table中找到当前对象对应的weak_entry,并将referrer加入到weak_entry中
    weak_entry_t *entry;
    if ((entry = weak_entry_for_referent(weak_table, referent))) { 
        append_referrer(entry, referrer); 	// 将referrer插入到weak_entry_t的引用数组中
    } 
    else { // 如果找不到,就新建一个
        weak_entry_t new_entry(referent, referrer);  
        weak_grow_maybe(weak_table);
        weak_entry_insert(weak_table, &new_entry);
    }
    return referent_id;
}
weak_unregister_no_lock方法删除弱引用
	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))) { // 查找对应的weak_entry_t
       		 remove_referrer(entry, referrer);  // 在对应的weak_entry_t的hash数组中,移除referrer
	       	 // 移除元素之后, 要检查一下weak_entry_t的hash数组是否已经空了
       		 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_t的hash数组已经空了,则需要将weak_entry_t从weak_table中移除
           		 	weak_entry_remove(weak_table, entry);
        		}
    		}
    	}
weak_entry_for_referent取weak_entry_t元素
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;  // 这里通过 & weak_table->mask的位操作,来确保index不会越界
    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); // 触发bad weak table crash
        hash_displacement++;
        if (hash_displacement > weak_table->max_hash_displacement) { 
        	// 当hash冲突次数超过上限,说明元素没有在hash表中,返回nil 
            return nil;
        }
    }
    return &weak_table->weak_entries[index];
}
append_referrer添加weak指针元素
static void append_referrer(weak_entry_t *entry, objc_object **new_referrer) {
    if (! entry->out_of_line()) { // 如果weak_entry 尚未使用动态数组,走这里
        //尝试插入到固定的inline_referrers数组
        for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
            if (entry->inline_referrers[i] == nil) {
                entry->inline_referrers[i] = new_referrer;
                return;
            }
        }
        // 如果inline_referrers已满,则转为referrers*动态数组,创建动态数组
        weak_referrer_t *new_referrers = (weak_referrer_t *)calloc(WEAK_INLINE_COUNT, sizeof(weak_referrer_t));
        //存储inline_referrers元素到动态数组
        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->mask = WEAK_INLINE_COUNT-1;
        entry->max_hash_displacement = 0;
    }
    // 对于动态数组的插入
    assert(entry->out_of_line()); // 断言: 此时一定使用的动态数组
    if (entry->num_refs >= TABLE_SIZE(entry) * 3/4) { 
    	// 如果动态数组中元素个数大于或等于数组位置总空间的3/4,则扩展数组空间为当前长度的一倍
    	return grow_refs_and_insert(entry, new_referrer); // 扩容,并插入
    }
    
    // 如果不需要扩容,直接插入到weak_entry中
    size_t begin = w_hash_pointer(new_referrer) & (entry->mask); // 确保了 begin的位置只能大于或等于数组的长度
    size_t index = begin;  // 初始的hash index
    size_t hash_displacement = 0;  // 用于记录hash冲突的次数,也就是hash再位移的次数
    while (entry->referrers[index] != nil) {
        hash_displacement++;
        index = (index+1) & entry->mask;  // index + 1, 移到下一个位置,再试一次能否插入
        if (index == begin) bad_weak_table(entry); // index == begin 意味着数组绕了一圈都没有找到合适位置,这时候一定是出了什么问题。
    }
    if (hash_displacement > entry->max_hash_displacement) { //重新设置hash冲突极限上限
        entry->max_hash_displacement = hash_displacement;
    }
    // 将ref存入hash数组,同时,更新元素个数num_refs
    weak_referrer_t &ref = entry->referrers[index];
    ref = new_referrer;
    entry->num_refs++;
}

对象内存释放

当对象的引用计数为0时,系统会开始调用dealloc方法释放当前对象的内存
dealloc会调用_objc_rootDealloc方法对对象进行释放,_objc_rootDealloc方法里面会调用rootDealloc方法

rootDealloc

inline void objc_object::rootDealloc() {
    if (isTaggedPointer()) return; //过滤taggedPointer形式
    if (fastpath(isa.nonpointer  &&  
                 !isa.weakly_referenced  &&  
                 !isa.has_assoc  &&  
                 !isa.has_cxx_dtor  &&  
                 !isa.has_sidetable_rc))
    {//isa优化、不存在弱表、关联对象、边表计数、C++析构函数释放会快
        assert(!sidetable_present());
        free(this);
    } else {
        object_dispose((id)this);
    }
}

object_dispose&&objc_destructInstance

void *objc_destructInstance(id obj) 
{
    if (obj) {
        bool cxx = obj->hasCxxDtor(); //C++ 析构方法
        bool assoc = obj->hasAssociatedObjects(); //关联对象.
        if (cxx) object_cxxDestruct(obj);
        if (assoc) _object_remove_assocations(obj);
        obj->clearDeallocating();
    }
    return obj;
}

clearDeallocating

inline void objc_object::clearDeallocating() {
    if (slowpath(!isa.nonpointer)) {
        //采用isa优化
        sidetable_clearDeallocating();
    } else if (slowpath(isa.weakly_referenced  ||  isa.has_sidetable_rc)) {
        // 存在weak表或者使用sideTable引用计数
        clearDeallocating_slow();
    }
    assert(!sidetable_present());
}

clearDeallocating_slow

NEVER_INLINE void objc_object::clearDeallocating_slow()
{// 未采用isa优化,且存在弱表或着SideTable引用计数
    assert(isa.nonpointer  &&  (isa.weakly_referenced || isa.has_sidetable_rc));
    SideTable& table = SideTables()[this]; // 在全局的SideTables中,以this指针为key,找到对应的SideTable
    table.lock();
    if (isa.weakly_referenced) { // 如果obj被弱引用
        weak_clear_no_lock(&table.weak_table, (id)this); // 在SideTable的weak_table中对this进行清理工作
    }
    if (isa.has_sidetable_rc) { // 如果采用了SideTable做引用计数
        table.refcnts.erase(this); // 在SideTable的引用计数中移除this
    }
    table.unlock();
}

weak_clear_no_lock

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); // 找到referent在weak_table中对应的weak_entry_t
    if (entry == nil) { return; }
    weak_referrer_t *referrers;
    size_t count;
    // 找出weak引用referent的weak 指针地址数组以及数组长度
    if (entry->out_of_line()) {
        referrers = entry->referrers;
        count = TABLE_SIZE(entry);
    } else {
        referrers = entry->inline_referrers;
        count = WEAK_INLINE_COUNT;
    }
    //遍历,对所有的weak指针置空
    for (size_t i = 0; i < count; ++i) {
        objc_object **referrer = referrers[i]; // 取出每个weak ptr的地址
        if (referrer) {
            if (*referrer == referent) { // 如果weak ptr确实weak引用了referent,则将weak ptr设置为nil,这也就是为什么weak 指针会自动设置为nil的原因
                *referrer = nil;
            } else if (*referrer) { // 如果所存储的weak ptr没有weak 引用referent,这可能是由于runtime代码的逻辑错误引起的,报错
                _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); // 由于referent要被释放了,因此referent的weak_entry_t也要移除出weak_table
}

内存优化

小对象优化

Taged Pointer:64位架构上对于NSString、NSNumber、NSDate小对象的存储优化
 iOS平台判断对象指针最高有效位为1 ,就是Taged Pointer ,取最低有效位 (p&(1<63))==1,当指针不够存储时会变回动态分配内存方式存储数据

isa的优化

在ARM64架构之前,objc的isa指针就是一个指向类或者元类的指针,而ARM64系统之后,对isa进行了优化,变成了一个共用体(union),用位域来存储更多信息
![isa结构](https://img-blog.csdnimg.cn/82e71aed625d4aeb8399540ddd0b01e6.png)
![isa结构体注释](https://img-blog.csdnimg.cn/e010ea77e39e4157aeb2a7a0dca8dc09.png)

局部优化记录

图片
1、常用的图片,通过UIImage(named: "")方法加载,存入内存缓存,伴随APP退出释放;不常用图片,通过UIImage(contentsOfFile: "")方式加载,用完即释放
2、纯色图片创建 1px的就行;水平渐变图片根据方向设置生成宽高,节约内存
3、图片展示大小设置,设置与展示视图大小相同即可
4、改变图片颜色通过tintColor&&renderMode方式
视图
1、自定义UIVIew使用CAShapeLayer绘制代替drawRect方法
2、UILabel大小设置接近ContentSize大小
3、大视图分块复用展示&&延迟加载,类似tableview
4、Masory布局内存消耗高于frame
缓存
1、设置上限,适时缓存,及时清理
2、YYModel、SDWebImage、Lottie动画
模型
1、减少非需要字段
2、减少常驻内存对象,如单例
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: iOS内存管理版本记录如下: 1. iOS 2.0及更早版本:使用手动管理内存的方式。 2. iOS 3.0:引入了基于引用计数的自动内存管理,使用retain和release函数来增加或减少对象的引用计数。 3. iOS 5.0:引入了ARC(自动引用计数)机制,ARC会在编译时自动插入retain和release代码,减少手动管理内存的工作。 4. iOS 7.0:引入了内存诊断工具Memory Usage Report,可以监测App内存使用情况,帮助开发者优化内存管理。 5. iOS 8.0:引入了一些新的API,如NSCache和NSURLSession,使得内存管理更加方便和灵活。 6. iOS 11.0:引入了基于图片大小的UIImage渲染机制,减少了内存占用。 7. iOS 13.0:引入了叫做“Scene”的多任务环境,使得内存管理更加复杂,需要更加小心谨慎地处理内存问题。 总的来说,随着iOS版本的不断更新,内存管理的机制也在不断地完善和优化,使得iOS应用能够更加高效地使用内存,提高用户体验。 ### 回答2: iOS内存管理是由操作系统自动管理的,在不同的版本中有所不同。 在iOS 5之前的版本中,内存管理主要依赖于手动管理引用计数(reference counting)来管理对象的生命周期。开发者需要手动调用retain和release方法来增加或减少对象的引用计数,以确保对象在不再需要时能够被正确释放。这种方式需要开发者非常谨慎地管理对象的引用,以避免内存泄漏或野指针等问题。 从iOS 5开始,iOS引入了自动引用计数(Automatic Reference Counting,ARC)的内存管理机制。ARC可以自动地插入retain、release和autorelease等方法的调用,使得开发者不再需要手动进行内存管理。开发者只需要关注对象的创建和使用,而不需要关心具体的内存管理细节。ARC减少了内存管理的工作量,提高了开发效率,并且减少了内存泄漏和野指针等问题的发生。不过,ARC并不是完全的自动化内存管理,开发者仍然需要遵循一些规则,比如避免循环引用等,以保证内存的正确释放。 随着iOS版本的不断更新,苹果不断改进和优化内存管理机制。每个新版本都带来了更好的性能和更高效的内存管理。开发者可以通过关注苹果的官方文档和开发者社区中的更新内容来了解每个版本中的具体变化和改进。 总结来说,iOS内存管理从手动的引用计数到自动引用计数的演变,极大地简化了开发者的工作,并提高了应用的性能和稳定性。随着不断的改进和优化,iOS内存管理会越来越高效和可靠。 ### 回答3: iOS内存管理版本记录是指苹果公司在不同版本的iOS操作系统中对于内存管理方面的改进和更新记录。随着iOS版本的不断迭代,苹果在内存管理方面进行了一系列的优化和改进,以提高系统的稳定性和性能。 首先,在早期的iOS版本中,苹果采用了手动内存管理的方式,即开发人员需要手动创建和释放内存,容易出现内存泄漏和内存溢出等问题。为了解决这些问题,苹果在iOS5版本中引入了自动引用计数(ARC)机制。ARC机制能够通过编译器自动生成内存管理代码,避免了手动管理内存带来的问题。 其次,iOS6版本引入了内存分页机制。这个机制能够将应用程序内存分成不同的页,将不常用的页置于闲置列表中,从而释放出更多的内存空间。这些闲置列表中的页能够在需要时快速恢复到内存中,减少了内存压力。 此外,iOS7版本中进一步提升了内存管理的能力。苹果在这个版本中引入了内存压缩技术,将内存中的数据进行压缩,从而提高了内存利用率。此外,iOS7还引入了资源清理功能,可以自动清理不再使用的资源,释放内存空间。 最后,在iOS13版本中,苹果进一步改进了内存管理策略。该版本中引入了后台内存优化功能,能够自动优化应用在后台运行时的内存占用,减少了后台应用对于系统内存的占用和影响。 综上所述,iOS内存管理版本记录反映了苹果在不同版本的iOS操作系统中对于内存管理方面的改进和优化。这些改进和优化使得iOS系统更加稳定和高效,并且提升了应用程序的性能和用户体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值