参考资料

理解 iOS 的内存管理
Swift自动引用计数
如何理解iOS的ARC和ARC的实现原理
Swift3——Swift内存管理
iOS内功篇:内存管理
iOS实录15:浅谈iOS Crash(二)
ARC 下内存泄露的那些点
黑幕背后的Autorelease

内存管理的原理

内存管理是指软件运行时对计算机内存资源的分配和使用技术,其最主要的目的是如何高效、快速的分配内存,并且在适当的时候释放和回收内存。

通过引用计数来管理内存
Swift通过自动引用计数(ARC)的机制来追踪和管理应用程序的内存(在 Compile Sources 中对 Swift 文件的 Compile Flags 中添加 -fno-objc-arc 标记无效)。

内存管理的原则

  • 自己生成的对象 自己持有
  • 非自己生成的对象 自己也能持有
  • 自己持有的对象不再需要时自己释放
  • 非自己持有的对象 自己不能释放

引用计数

引用计数(Reference Counting)是一种简单而有效的管理对象生命周期的方式。
包含:手动引用计数(MRC: Manual Reference Counting) 自动引用计数(ARC: Automatic Reference Counting)

当我们创建一个新的对象的时候,它的引用计数为1;
当有新的强指针指向这个对象的时候,他的引用计数+1;
当有一个强指针不再指向这个对象的时候,他的引用计数-1;
当引用计数为0时,这个对象会被释放。
引用计数并非Objective-C、Swift独有,像微软的COM C++11等语言也提供了关于引用计数的内存管理方式。

ARC 的原理

引用计数的内存管理方式虽然简单,但手工大量的操作引用计数不仅繁琐而且容易遗漏。于是苹果在2011年引入了自动引用计数ARC。
ARC的想法来源于苹果早期设计Xcode的Analyzer的时候,发现编译器在编译的时候会发现代码中的很多内存问题。后来苹果想能不能在编译的时候把内存管理的代码自动补上,带着这种想法,苹果就修改了内存管理代码的书写方式,也就是后来的ARC。
ARC的原理:当我们编译代码的时候,编译器会分析代码中每个对象的生命周期,然后基于这些对象的生命周期,自动的添加相应的引用计数的代码。所以,ARC是工作在编译器的一种工作方案。
实际上,由于 ARC 在编译期自动添加了引用计数的代码,所以编译之后,ARC和MRC的代码没有区别,这也是二者可以混编的原因。
在 Objective-C 工程 target -> Build Phases(编译阶段) -> Compile Sources(编译源) 中修改相应文件的 Compile Flags(编译标记) 即可实现 ARC 与 MRC 的混编。 ARC 项目中可以通过添加编译参数 -fno-objc-arc 来关闭部分文件的ARC功能;MRC 项目中可以通过添加编译参数 -fobjc-arc 来打开部分文件的ARC功能。通过这个操作也可以反证出 ARC 是在编译期添加进行内存管理代码的添加操作的。

关于dealloc

NSObject为我们提供了dealloc方法,当对象被销毁时,系统会自动调用它(不能手动调用)。这个方法的作用是清空对象自身内存和它所持有的资源。

@interface Person : NSObject
@property (retain) NSString *firstName;
@property (retain) NSString *lastName;
@property (assign, readonly) NSString *fullName;
@end
@implementation Person

- (void)dealloc
   [_firstName release];
   [_lastName release];
   [super dealloc]; // 最后调用
}

@end

block属性的一定要加关键词使用copy吗?

实际上,block使用copy关键字是MRC遗留下来的代码习惯,将block从栈区copy到堆区。在ARC中编译器会自动对block进行copy工作,无论是写了copy还是strong。只是写上copy可以让开发者更加明确的知道编译器会做这个copy操作。在Swift中一般都不会再写copy。

弱引用weak和无主引用unowned都可以解决循环强引用问题,二者有什么区别?

弱引用和无主引用允许循环引用中的一个实例引用另外一个实例,但不保持强引用。这样实例能够相互引用而不产生循环强引用。

因为弱引用不会保持所引用的实例,即使引用存在,实例也有可能被销毁。因此,ARC会在引用的实例被销毁后自动将其置为nil。并且因为弱引用可以允许它们的值在运行时被赋值为nil,所以它们会被定义为可选类型变量,而不是常量。 当 ARC 设置弱引用为nil时,属性观察不会被触发。

无主引用通常都被期望拥有值。不过 ARC 无法在实例被销毁后将无主引用设为nil,因为非可选类型的变量不允许被赋值为nil。
使用无主引用,你必须确保引用始终指向一个未销毁的实例。如果你试图在实例被销毁后,访问该实例的无主引用,会触发运行时错误。

例如:租客Person相对于公寓Apartment来说,由于公寓在某段时间内可能没有租客,所以租客的生命周期更短;持卡人Customer相对于信用卡CreditCard来说,由于信用卡必须被持卡人持有,所以持卡人的生命周期更长。

Person和Apartment的例子展示了两个属性的值都允许为nil,并会潜在的产生循环强引用。这种场景最适合用弱引用来解决。

Customer和CreditCard的例子展示了一个属性的值允许为nil,而另一个属性的值不允许为nil,这也可能会产生循环强引用。这种场景最适合通过无主引用来解决。

存在着第三种场景,在这种场景中,两个属性都必须有值,并且初始化完成后永远不会为nil。在这种场景中,需要一个类使用无主属性,而另外一个类使用隐式解析可选属性。

在闭包和捕获的实例总是互相引用并且总是同时销毁时,将闭包内的捕获定义为无主引用。

相反的,在被捕获的引用可能会变为nil时,将闭包内的捕获定义为弱引用。弱引用总是可选类型,并且当引用的实例被销毁后,弱引用的值会自动置为nil。这使我们可以在闭包体内检查它们是否存在。

如果被捕获的引用绝对不会变为nil,应该用无主引用,而不是弱引用。

开发中常见的内存问题

内存泄漏:应当废弃的对象在超出其生命周期后继续存在就会造成内存泄漏,如循环引用。

  • 循环引用
    当两个不同的对象各有一个强引用指向对方,那么循环引用便产生了,当然多一个对象产生的环也是一样的。

    一般来讲循环引用也是可以使用工具来检测到的,分为两种:
    1、在product-Analyze中使用静态分析来检测代码中可能存在循环引用的问题。
    2、在Xcode-open developer tool-Instruments打开工具集,选择Leaks工具可以对已安装的应用进行内存泄漏检测,此工具能检测静态分析不会提示,但是到运行时才会出现的内存泄漏问题。

    Leaks工具虽然强大,但是它不能检测到block循环引用导致的内存泄漏,这种情况一般需要自行排查问题,最简单的方法是重写对象的dealloc/deinit方法来监测对象是否正常释放,来确认没有形成循环引用。

  • NSTimer
    一般情况下,Target/Action模式中的Target都是被weak引用,但是NSTimer中是强引用,只有当Timer is invalidated后才不会再引用target。

    target The object to which to send the message specified by aSelector when the timer fires. The timer maintains a strong reference to this object until it (the timer) is invalidated.

    timer必须被添加到RunLoop以后才能起作用,RunLoop对添加进来的timer有强引用,所以当timer被添加到Runloop以后就不必对其保持强引用。

    Timers work in conjunction with run loops. Run loops maintain strong references to their timers, so you don’t have to maintain your own strong reference to a timer after you have added it to a run loop.

     RunLoop.main.add(atimer, forMode: .commonModes)
    

    mainRunLoop的生命周期跟应用程序的生命周期一致,所以如果我们不invalidate timer,runLoop就会一致持有timer,target也就一直不会被释放。就会造成内存泄漏。
    所以,合适的时机invalidate timer就成了关键。

Copyright © shuoliu.com 2018 all right reserved,powered by Gitbook该文件修订时间: 2018-09-03 03:46:41

results matching ""

    No results matching ""