美文网首页
iOS循序渐进之篇四:OC对象的生成和释放

iOS循序渐进之篇四:OC对象的生成和释放

作者: song91425 | 来源:发表于2021-01-20 19:29 被阅读0次

篇三上说iOS ARC(Automatic Reference Counting)管理的是堆上的OC对象。

1 开发人员对OC对象管理的四条规则

  • 自己生成的对象,自己持有
  • 非自己生成的对象,自己也能持有
  • 不需要自己持有的对象时释放
  • 非自己持有的对象不能释放
    上面的四条规则能够帮助开发人员完成对OC对象的声明周期的管理,而ARC的工作原理也是模仿这一方式,通过引用计数。它会在合适的地方给我添加对OC对象的引用和释放的代码。当一个OC对象被引用时,那么这个OC对象的引用数加1;当一个OC对象失去引用时,那么这个OC对象的数减1。当OC对象引用数为0的时候就会被系统回收,回收的时机在下一个RunLoop循环开始,回收上一个循环的OC对象

2 在内存中管理对象的操作方法

Objective-c 方法名 功能
alloc/new/copy/mutableCopy 方法 生成并持有对象,即使用这些方法生成对象,那么对这个对象的引用至少为1
retain 方法 持有对象,即使这个对象的引用数加1
release 方法 释放对象,即使这个对象的引用数减1,当这个引用数为0的时候,系统自动调用dealloc方法释放对象所占用的空间
dealloc 方法 释放对象,这个释放必须是对象的引用数为零才能释放

2.1 Tips:copy和mutableCopy的区别

方法 功能 协议 实现 可变对象调用 不可变对象调用
copy 生成并持有不可变对象的副本 NSCopying copyWithZone: 深复制 浅复制
mutableCopy 生成并持有可变对象的副本 NSMutableCopying mutableCopyWithZone: 深复制 深复制

由 上表可以知道,使用copy方法复制的并不都是浅复制,为了避免出错,如果要对一个对象进行深复制,则使用mutableCopy方法。

2.2 autorelease/__autoreleasing 关键字

autorelease是在MRC环境下的关键字,而__autoreleasingARC环境下的关键字,它们是等价的,只是看编译环境是不是支持ARC。该关键字:它可以取得对象,但是不持有对象,利用这个特性我们可以在下面的方法中返回一个不会被持有的对象和降低内存峰值。

-(id) object{
  id obj = [NSObject new];
  [obj autorelease]; // obj不持有[NSObject new] 生成的对象
  return obj; // 等价于返回一个引用
}

问题1:为什么autorelease它可以取得对象,但是不持有对象?
原因是: 调用这个方法,它可以把这个对象加入到自动释放池中,在清理自动释放池的时候,系统自动会为释放池中的对象调用[obj release];
问题2:如何降低内存峰值?
在回答这个问题之前,先介绍内存峰值和对比在MRCARC的自动释放池。
内存峰值:它是指在某一段较小的时间占用的内存高于它邻近时间的内存占有量

在MRC环境下的创建自动释放池,分为三步骤:
第一步:生成并持有释放池NSAutoreleasePool对象;
第二步:调用对象的autorelease方法,使对象加入连接池中;
第三步:释放NSAutoreleasePool对象,然后系统会对这个池中的每一个对象调用release方法;

//MRC环境下:
//第一步:生成并持有释放池NSAutoreleasePool对象;
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

// 在堆内存中生成一个对象
id obj = [[NSObject alloc] init];

// 第二步:调用autorelease方法,使对象加入连接池中;
[obj autorelease];

//第三步:释放NSAutoreleasePool对象;
[pool drain];   //向池中的所有对象调用release方法,此处由系统调用[obj release];

//obi已经释放,再次调用会崩溃
NSLog(@"%@", obj); 

由上例代码可知,对自动释放池的生成和释放的代码是固定不变的,所以在ARC环境下,它把自动释放池的创建和释放交给编译器了,直接使用@autoreleasepool块即可。例如上例可以改成:

@autoreleasepool{// 这里生成NSAutoreleasePool对象
// 在堆内存中生成一个对象
id obj = [[NSObject alloc] init];
} // 在这里自动调用[pool drain];

接下来假设一个减少内存峰值的一个应用场景:通过一个for循环加载大量文件,并用一个字符串来接收,如下所示:

// 此处只是模拟场景,
NSArray *urls = <# An array of file URLs #>;
for (NSURL *url in urls) {
      NSError *error;
      NSString *fileContents = [NSString stringWithContentsOfURL:url
                                         encoding:NSUTF8StringEncoding error:&error];
      /*处理字符串,创建并自动释放更多对象。 */
}

上例代码,在for循环内会占用大量的内存,这时就会产生内存峰值,如果我们使用自动释放池就可以降低内存峰值,如下所示。

// 此处只是模拟场景,
NSArray *urls = <# An array of file URLs #>;
for (NSURL *url in urls) {
 
    @autoreleasepool {
        NSError *error;
        NSString *fileContents = [NSString stringWithContentsOfURL:url
                                         encoding:NSUTF8StringEncoding error:&error];
        /*处理字符串,创建并自动释放更多对象。 */
    }// 执行到这里,会释放池内的所有局部变量,这里会把fileContents释放掉。
}

3 自动释放池(NSAutoreleasePool)

自动释放池还有一个特性就是可以嵌套使用,如下面所示。

@autoreleasepool {
    // . . .
    @autoreleasepool {
        // . . .
    }
  //  . . .
}

注意:虽然自动释放池,能够帮助我们清理对象,但是不建议大量的使用自动释放池,原因有二:一是虽然自动释放池虽然占用较小,但是大量的话也会占有一定的内存;二是频繁的清理对象,会消耗CPU的性能和耗电。

3.1 RunLoop和NSAutoreleasePool的关系

每一个RunLoop都会创建一个Autorelease Pool对象,系统会为同一个RunLoop 的所有Autorelease Pool对维护一个栈,每个新创建的Autorelease Pool对象会压入栈顶,待RunLoop结束时,弹出栈顶,同时清理Autorelease Pool的所有对象。

3.2 线程和NSAutoreleasePool的关系

每一个线程都会有自己的Autorelease Pool 和一个RunLoop,但是非主线程的RunLoop,默认不回开启,需要手动开启。当线程终止的时候,系统会自动清理该线程的Autorelease Pool 对象。所以在程序开始运行的时候,主线程创建后,RunLoop和Autorelease Pool也会创建,并且RunLoop启动。

总结

在现在的iOS的开发几乎都是处于ARC环境下,ARC的工作原理也是非常简单的,就是监控一个对象的引用数,如果这个对象的引用数为0,系统就会回收这个对象。这样就帮助开发人员从MRC编程释放出来。本文主要介绍了对一个对象从生成到释放和在ARC环境下,@autorelease的用法和它的背后原理。

相关文章

  • oc对象实现图解

    ios第一篇(oc对象) ios类对象和元类对象以及对象的关系 类对象中存储的是对象的实例方法,属性,成员变量,协...

  • iOS-Xcode --- swift生成技术文档之jazzy

    iOS-Xcode --- swift生成技术文档之jazzy 在网上看了很多都是针对OC项目的文档生成,找了好久...

  • iOS底层原理总结-- KVO/KVC的本质

    iOS底层原理总结--OC对象的本质(一) - 掘金 iOS底层原理总结--OC对象的本质(二) - 掘金 iOS...

  • 建立你自己的iOS开发知识体系

    目录: 底层相关 iOS底层原理总结 - 探寻Class的本质iOS底层原理总结篇-- 探寻OC对象的本质iOS底...

  • OC对象的本质(上)

    iOS | OC对象本质 | Objective-C 什么是OC语言,OC对象、类的本质是什么,OC对象的内存布局...

  • OC内存管理--对象的生成与销毁

    原文链接OC内存管理--对象的生成与销毁 在iOS开发中了,我们每天都会使用+ alloc和- init这两个方进...

  • OC的内存管理

    内存管理四句箴言: 自己生成的自己持有非自己生成的对象自己也可持有不再需要自己持有时释放非自己持有的对象无法释放 ...

  • 聊一聊对象(一)

    Apple源码[opensource.apple.com/tarballs/] iOS开发之OC对象本质[http...

  • 内存管理的思想

    1.ARC编译器自动生成内存管理的代码 几十M的程序,闪退想象,内存超出的时候 堆(对象)(手动释放,oc给对象发...

  • 窥探iOS底层实现--OC对象的本质(二)

    窥探iOS底层实现--OC对象的本质(一) - 掘金 窥探iOS底层实现--OC对象的本质(二) - 掘金 窥探i...

网友评论

      本文标题:iOS循序渐进之篇四:OC对象的生成和释放

      本文链接:https://www.haomeiwen.com/subject/ldfwoktx.html