iOS学习-内存管理

iOS内存管理是iOS开发中非常重要的一个话题,本文将深入探讨iOS内存管理的基本原理和相关知识点,包括strong和weak、ARC自动引用计数、内存泄漏以及相关面试问题。希望本文能够帮助iOS开发人员更好地理解和掌握iOS内存管理。

一、引用计数

iOS通过引用计数来进行内存管理,每个对象都有一个引用计数器(retain count),用于记录有多少个指针指向该对象。当对象的引用计数器为0时,表示该对象不再被使用,可以被释放。引用计数器的增减通过retain和release方法来完成。

retain方法会使对象的引用计数器加1,表示有一个新的指针指向了该对象;release方法会使对象的引用计数器减1,表示一个指针不再指向该对象。当对象的引用计数器为0时,会自动调用dealloc方法来释放对象的内存空间。

二、MRC和ARC

在Objective-C中,我们可以使用MRC(手动引用计数)或者ARC(自动引用计数)两种方式来管理内存。下面分别介绍一下这两种方式下的内存管理:

MRC下的内存管理:

在MRC下,我们需要手动管理对象的引用计数,当对象不再被使用时,我们需要将其引用计数减1,当引用计数为0时,对象会被自动释放。

例如,在MRC下,我们可以通过retain方法增加对象的引用计数,release方法减少对象的引用计数,autorelease方法将对象放入自动释放池中等方式来管理对象的引用计数。

ARC下的内存管理:

在ARC下,我们不需要手动管理对象的引用计数,系统会自动在合适的时机对对象进行引用计数的增减和释放操作,从而避免了手动管理引用计数的繁琐和出错。同时,ARC也提供了一些关键字和修饰符,如strong、weak、__unsafe_unretained、__autoreleasing等,用于更精细地控制对象的生命周期。
例如,在ARC下,我们可以使用strong和weak关键字来声明属性,使用__autoreleasing关键字来修饰方法返回值,使用__bridge关键字来进行类型转换等方式来管理对象的引用计数。

  • 引用计数的获取:
    • 在MRC下,我们可以使用retainCount方法来获取对象的引用计数。而在ARC下,由于系统自动管理引用计数,我们不能直接使用retainCount方法来获取引用计数,因为这样做可能会导致错误的结果。
    • 在ARC下,我们可以使用一些其他的方式来获取对象的引用计数。例如,可以使用CFGetRetainCount函数来获取对象的引用计数,或者使用日志输出来观察引用计数的变化。

三、强引用和弱引用

在iOS中,强引用(strong)和弱引用(weak)是最常见的两种引用类型。

强引用会使对象的引用计数器加1,表示该对象被强引用,不会被释放,直到所有的强引用都被移除。一般情况下,我们使用强引用来持有自己创建的对象,例如在一个类中定义一个属性,这个属性应该是强引用类型。

弱引用不会使对象的引用计数器加1,表示该对象被弱引用,不会阻止对象的释放。当对象的引用计数器为0时,弱引用会被自动设置为nil。一般情况下,我们使用弱引用来解决循环引用问题,例如在两个类之间互相引用时,可以使用弱引用来避免循环引用。

四、内存泄漏

内存泄漏是指在程序中分配了内存空间,但在程序结束时没有正确释放该内存空间,导致内存泄漏。在iOS开发中,内存泄漏是一个非常严重的问题,会导致应用程序出现卡顿、崩溃等问题。

内存泄漏的主要原因是由于程序中存在循环引用,即两个对象互相引用,但都没有被其他对象所引用,导致它们之间的引用计数器无法自动减少到0,导致内存无法释放。因此,我们需要避免循环引用的产生,可以使用弱引用、解除引用或Block的方式来避免循环引用。

五、ARC下的内存管理实践

在ARC下,由于不需要手动管理引用计数,我们需要注意以下几个方面来保证内存的正常管理。

避免循环引用

循环引用是ARC下的一个常见问题,解决循环引用的方法通常有两种:

(1)使用weak关键字

当我们在一个类中需要引用另一个类的实例时,可以使用weak关键字来避免循环引用。

例如,一个ViewController中需要引用一个Model的实例时,可以将Model的实例定义为weak属性:

@property (nonatomic, weak) Model *model;

这样,当ViewController的实例被释放时,model的引用计数器会自动减1,如果没有其他强引用对象指向它,model会被自动释放。

(2)使用__block解除引用

在Block中,我们也需要避免循环引用的产生。一种常见的方法是使用解除引用(__block)关键字。

例如,当我们在一个Block中需要引用一个对象时,可以将该对象定义为__block类型:

__block Model *model = self.model;
[self doSomething:^{
    [model doSomething];
}];

这样,在Block中对model进行操作时,不会使model的引用计数器加1,也不会产生循环引用。

注意Block中的循环引用

在使用Block时,需要特别注意循环引用的问题。如果Block中引用了该Block所在的对象,就会产生循环引用的问题。

例如,下面的代码中,Block引用了self,导致self无法释放,从而产生了内存泄漏的问题。

[self doSomething:^{
    [self doSomethingElse];
}];

解决这个问题的方法通常是使用弱引用或者解除引用。例如,将self定义为weak属性:

__weak typeof(self) weakSelf = self;
[self doSomething:^{
    [weakSelf doSomethingElse];
}];
避免Block中使用循环引用的对象

在Block中,我们需要避免使用循环引用的对象,以免产生内存泄漏的问题。

例如,下面的代码中,self引用了timer,timer引用了Block,block引用了self,就会产生循环引用的问题。

self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
    [self doSomething];
}];

解决这个问题的方法通常是使用弱引用或者解除引用。例如,将self定义为weak属性,或者使用__block关键字来解除引用。

避免使用retainCount

在ARC下,我们不再需要手动管理对象的引用计数,因此也就不需要使用retainCount方法来获取对象的引用计数。

retainCount方法并不总是返回准确的引用计数,因此在ARC下使用retainCount方法并不可靠,可能会导致错误的结果。

避免使用__unsafe_unretained

在ARC下,我们应该尽量避免使用__unsafe_unretained关键字来声明属性,因为它不会自动将对象置为nil,容易产生野指针的问题。
例如,下面的代码

@property (nonatomic, unsafe_unretained) Model *model;

解决这个问题的方法通常是使用weak关键字来声明属性。

六、isa指针优化

在Objective-C中,每个对象都有一个isa指针,指向它所属所属的类对象。在MRC下,isa指针是指向Class结构体的指针,而在ARC下,isa指针是指向objc_class结构体的指针。

为了优化内存使用和提高程序的性能,iOS系统对isa指针进行了优化。具体来说,iOS系统使用了两种类型的isa指针,分别是普通指针和优化指针。

普通指针:

普通指针是最基本的isa指针类型,它直接指向类对象的地址。在普通指针下,当我们调用对象的方法时,系统会根据isa指针找到对象所属的类对象,并在类对象中查找方法的实现。

优化指针:

在iOS 2.0之前,isa指针是直接存储在对象中的,这样会占用大量的内存。在iOS 2.0之后,isa指针行了优化,变成了一个联合体(union)结构,将64位的内存空间用来存储地址、引用计数以及一些标记数据。

以ARM64为例,其中的33位存储具体的地址值,19位存储引用计数,而剩下的存储了更多的信息,如是否有weak指针,是否开启指针优化、是否有C++或Objc的析构器等等。

这种方式发挥了联合体的优势节省了比较多的内存空间,可以有效地降低内存的使用,尤其是在使用大量对象时。然而,由于位域的使用是由编译器自动完成的,因此有时会出现一些不可预测的问题,例如位域的长度不够等。因此,在进行性能优化时,需要谨慎使用isa指针优化,避免出现问题。

七、面试问题

下面列举一些常见的面试问题:

请介绍一下iOS中的内存管理方式,包括ARC和MRC两种方式。
请解释一下strong和weak关键字的区别和作用。
请介绍一下weak关键字的实现原理。
请解释一下isa指针的优化方式。
请解释一下循环引用的问题,以及如何避免循环引用。
请介绍一下iOS中的内存泄漏问题,以及如何避免内存泄漏。
请介绍一下autorelease池的作用和实现原理。
怎么样进行内存泄漏的排查和解决?
对于大内存的处理和优化,你有哪些思路和方法?
对于内存问题的优化,你有哪些实际的经验和技巧?

总结

在iOS开发中,内存管理是一个非常重要的话题,无论是手动管理引用计数,还是使用ARC自动管理引用计数,都需要我们注意以下几个方面来保证内存的正常管理:

了解对象的引用计数和引用计数的变化方式;
避免产生循环引用,可以使用weak关键字、解除引用或Block的方式来避免循环引用;
在ARC下,注意Block中的循环引用问题,避免Block中使用循环引用的对象,避免在Block中使用强引用;
避免使用retainCount方法,避免使用__unsafe_unretained关键字来声明属性。
以上是iOS内存管理的一些实践经验,希望能对你有所帮助。在iOS开发中,合理地管理内存是一项重要的技能,希望你能够在实践中逐渐掌握这项技能。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容