乐虎游戏|乐虎国际登录|欢迎你

iOS内部存款和储蓄器处理(MRC、ARC)深入显出

日期:2020-03-29编辑作者:计算机资讯

前言

从我开始学习iOS的时候,身边的朋友、网上的博客都告诉我iOS的内存管理是依靠引用计数的,然后说引用计数大于1则对象保存在内存的堆中而引用计数等于0则对象销毁。然后又说在所谓的ARC时代,强指针指向一个对象,则对象不销毁;一个对象没有任何一个强指针指向则销毁....,最后,我想说这些都很有道理的样子,但是,我还是不清楚为什么引用计数器为0为什么会被销毁,为什么一个对象没有强指针指向就会销毁,为什么在@property中一个OC对象要使用strong进行修饰 .... 。所以,在学习 Objective-C高级编程:iOS与OS X多线程和内存管理后,让我明白了很多事情。以下是对于这本书里面知识的总结性内容,如果要详细了解,请阅读该书籍。

注意:下面的内容是适合于已经对于iOS内存管理有一定了解的程序员

内存管理方式

内存管理的思考方式

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

在iOS内存管理中有四个关键字,alloc、new、copy、mutableCopy,自身使用这些关键字产生对象,那么自身就持有了对象

 // 使用了alloc分配了内存,obj指向了对象,该对象本身引用计数为1,不需要retain id obj = [[NSObject alloc] init]; // 使用了new分配了内存,objc指向了对象,该对象本身引用计数为1,不需要retain id obj = [NSObject new]; 
  1. 非自己生成的对象,自己也能持有
 // NSMutableArray通过类方法array产生了对象(并没有使用alloc、new、copy、mutableCopt来产生对象),因此该对象不属于obj自身产生的 // 因此,需要使用retain方法让对象计数器+1,从而obj可以持有该对象(尽管该对象不是他产生的) id obj = [NSMutableArray array]; [obj retain];
  1. 不再需要自己持有对象时释放
 id obj = [NSMutableArray array]; [obj retain]; // 当obj不在需要持有的对象,那么,obj应该发送release消息 [obj release];
  1. 无法释放非自己持有的对象
 // 1. 释放一个已经释放的对象 id obj = [[NSObject alloc] init]; // 已经释放对象 [obj release]; // 释放了对象还进行释放 [obj release]; // 2. 释放一个不属于自己的对象 id obj1 = [obj object]; // obj1没有进行retain操作而进行release操作,使得obj持有对象释放,造成了野指针错误 [obj1 release];

如上为iOS进行内存管理的四种思考方式(记住不论是ARC还是MRC都遵循该思考方式,只是ARC时代这些工作让编译器做了)

首先明确一点,无论在MRC还是ARC情况下,Objective-C采用的是引用计数式的内存管理方式,这一方式的特点:

引用计数器讨论

苹果对于引用计数的管理是通过一张引用计数表进行管理的

图片 1引用计数表.png

我们平常在操作对象的引用计数器时,其实就是对这个引用计数表进行操作,在获取到该表的地址以及相应对象的内存地址,就可以通过对象的内存从该表中进行索引获取到相应的引用计数值,然后根据用户的操作来返回计时器、计时器加1、计时器减1,下面就深入讨论retain、release、alloc、dealloc具体怎么操作该引用计数表

自己生成的对象,自己持有。例如:NSObject * __strong obj = [[NSObject alloc]init];。

alloc

当我们调用alloc函数时我们进一步会调用allocWithZone方法

 id obj = [[NSObject alloc] init]; + alloc { return [self allocWithZone:NSDefaultMallocZone()]; } + allocWithZone:z { return NSAllocateObject; }

调用NSAllocateObject函数对内存进行分配

非自己生成的对象,自己也能持有。例如:NSMutableArray * __strong array = [NSMutableArray array];。

retain、release、retainCount

该书籍对于这三个函数调用先是使用GNUstep(一个Cocoa框架的互换框架,功能类似)进行讲解,后来又讲解了苹果对于引用计数的实现。在这里我们就讨论苹果的实现了。

调用retain、release、retainCount时函数调用顺序:

图片 2retain、retainCount、release函数调用顺序.png

如下所示,调用各个函数时会调用__CFDoExternRefOperation函数,该函数包含于CFRuntime.c中,该函数简化代码如下:

- (NSUInteger)retainCount { return (NSUInteger)__CFDoExternRefOperation(OPERATION_retainCount,self);}- retain { return __CFDoExternRefOperation(OPERATION_retain,self);}- release { return __CFDoExternRefOperation(OPERATION_release,self);}

 int __CFDoExternRefOperation(uintptr_r op,id obj) { CFBasicHashRef table = 取得对象对应的散列表; int count; switch { case OPERATION_retainCount: count = CFBasicHashGetCountOfKey(table,obj); return count; case OPERATION_retain: CFBasicHashAddValue(table,obj); return obj; case OPERATION_release: count = CFBasicHashRemoveValue(table,obj): return 0 == count; } }

代码如上所示,可以想象苹果就是使用类似于上述的引用计数表来管理内存,也就是说我们在调用retain、retainCount、release时首先调用__CFDoExternRefOperation进而获取到引用技术表的内存地址以及本对象的内存地址,然后根据对象的内存地址在表中查询获取到引用计数值。

若是retain就加1若是retainCount就直接返回值,若是release则减1而且在CFBasicHashRemoveValue中将引用计数减少到0时会调用dealloc,从而调用NDDeallocateObject函数、free函数将对象所在内存释放

以上就是在讨论苹果对于引用计数的管理方法,对于GNUStep办法请自行查阅书籍

不再需要自己持有对象时释放。

autorelease

作用:将对象放入自动释放池中,当自从释放池销毁时对自动释放池中的对象都进行一次release操作书写形式:

 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; id obj = [[NSObject alloc] init]; [obj autorelease]; [pool drain]; 

对于autorelease的实现方式,书籍也对比了GNUSetp与苹果实现的方式,现在通过GNUStep源代码来理解苹果的实现

  1. GNUStep实现
 id obj = [[NSObject alloc] init]; [obj autorelease];

 - autorelease { [NSAutoreleasePool addObject:self]; }

 + addObject:anObject { NSAutoreleasePool *pool = 取得正在使用的Pool对象; if (pool != nil) { [pool addObject:anObject]; }else { NSLog(@"NSAutoreleasePool非存在状态下使用Pool对象"); } }

 - addObject:anObject { [array addObject:anObject]; }

从上面可以看出,自动释放池就是通过数组完成的,我们在调用autorelease时最终就是将本对象添加到当前自动释放池的数组而针对于自动释放池销毁时对数组中的进行一次release操作,见下面

 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; ... // 当自动释放池销毁时 [pool drain];

 - drain { [self dealloc]; } - dealloc { [self emptyPool]; [array release]; } - emptyPool { for (id obj in array) { [obj release]; } }
  1. 苹果的实现
 class AutoreleasePoolPage { static inline void *push() { 相当于生成或持有NSAutoreleasePool类对象 } static inline void *pop(void *token) { 相当于废弃NSAutoreleasePool类对象 releaseAll(); } static inline id autorelease { 相当于NSAutoreleasePool类的addObject类方法 AutoreleasePoolPage *autoreleasePoolPage = 取得正在使用的AutoreleasePoolPage实例; autoreleasePoolPage->add; } id *add { 将对象追加到内部数组中 } void releaseAll() { 调用内部数组中对象的release实例方法 } }; void *objc_autoreleasePoolPush { return AutoreleasePoolPage::push(); } void objc_autoreleasePoolPage(void *ctxt) { AutoreleasePoolPage::pop; } id *objc_autorelease { return AutoreleasePoolPage::autorelease; }

如上所示,苹果内部使用了类似于GNUStep中的思想,将对象添加进数组进行管理

无法释放非自己持有的对象。

ARC中内存管理方式

介绍:关于这部分的内存,作者是分了两部分进行讨论,第一部分介绍ARC管理所需要的关键字__strong 、__weak、__unsafe_unretained、__autoreleasing的作用;第二部分介绍了ARC针对于这些关键字的具体内管管理实现方式。下面我们就综合两部分的内容进行一次讨论

苹果官方文档说ARC是有"编译器自行进行管理",但事实上仅仅是编译器是不够,需要满足下面啷个条件

  • clang3.0以上
  • objc4 Objective-C运行时库493.9以上

对象操作Objective-C方法生成并持有对象alloc/new/copy/mutableCopy等方法持有对象retain方法释放对象release方法废弃对象dealloc方法

作用
 id __strong obj = [[NSObject alloc]init];

如上代码,表示obj这个强指针指向NSObject对象,且NSObject对象的引用计数为1

 id __strong obj1 = obj; 

如上代码,表示obj1这个强指针与obj指针指向同一个NSObject对象,且NSObject对象的引用计数为2

 id __strong obj = [NSMutableArray array];

如上代码,表示obj这个强指针指向的NSMutableArray对象的引用计数为1

综上所示,当一个对象被强指针指向则引用计数就加1,否则,该对象没有一个强指针指向则自动释放内存

那么问题来了,为什么一个对象被强指针指向引用计数就加1呢? 为什么分配在堆里面的对象内存能够自动释放内存?

自己生成的对象,自己持有

原理

第一种情况: 对象是通过alloc、new、copy、multyCopy来分配内存的

 id __strong obj = [[NSObject alloc] init];

当使用alloc、new、copy、multyCopt进行对象内存分配时,强指针直接指向一个引用计数为1的对象,在编译器作用下,上述代码会转换成以下代码

 id obj = objc_msgSend(NSObject,@selector; objc_msgSend(obj,@selector; // 当让这个代码会在合适的时候被调用,不是马上调用 objc_release;

第二种情况: 对象不是自身生成,但是自身持有(一般这样的对象是通过除alloc、new、copy、multyCopy外方法产生的)

 id __strong obj = [NSMutableArray array];

在这种情况下,obj也指向一个引用计数为1的对象内存,其在编译器下转换的代码如下:

 id obj = objc_msgSend(NSMutableArray,@selector; // 代替我们调用retain方法,使得obj可以持有该对象 objc_retainAutoreleasedReturnValue; objc_release;

从而使得obj指向了一个引用计数为1的对象, 不过,objc_retainAutoreleaseReturnValue有一个成对的函数objc_autoreleaseReturnValue,这两个函数可以用于最优化程序的运行如下代码:

 + array { return [[NSMutableArray alloc] init]; }

代码转换如下:

 + array { id obj = objc_msgSend(NSMutableArray,@selector; objc_msgSend(obj,@selector; // 代替我们调用了autorelease方法 return objc_autoreleaseReturnValue; }

在转换后的代码,我们可以看见调用了objc_autoreleaseReturnValue函数且这个函数会返回注册到自动释放池的对象,但是,这个函数有个特点,它会查看调用方的命令执行列表,如果发现接下来会调用objc_retainAutoreleasedReturnValue则不会返回注册到自动释放池的对象而仅仅返回一个对象而已。

两者的关系图如下:

图片 3关系图.png

通过这些,我们就可以通知为什么强指针指向一个对象,这个对象的引用计数就加1

在iOS内存管理中有四个关键字,alloc、new、copy、mutableCopy,自身使用这些关键字产生对象,那么自身就持有了对象

作用
 id __weak obj = [[NSObject alloc] init];

根据我们的知识,可以知道NSObject对象在生成之后立马就会被释放,其主要原因是__weak修饰的指针没有引起对象内部的引用计数器的变化因此,__weak修饰的指针常用于打破循环引用或者修饰UI控件,关于__weak修饰的指针引用场景这里不叙述,下面主要介绍其原理

// 使用了alloc分配了内存,obj指向了对象,该对象本身引用计数为1,不需要retain

原理

我们知道弱指针有两个作用:一. 修饰的指针不会引起指向的对象的引用计数器变化 二. 当指向的对象被销毁时,弱指针全部置为nil, 那么除了这些之外,我们还有一个要说的就是,为什么我们在程序中不能频繁的使用weak呢?

  1. 为什么弱指针不会引起指向的对象的引用计数器发生变化
 id __weak obj = [[NSObject alloc] init];

编译器转换后的代码如下:

 id obj; id tmp = objc_msgSend(NSObject,@selector; objc_msgSend(tmp,@selector; objc_initweak(&obj,tmp); objc_release; objc_destroyWeak(&object);

对于__weak内存管理也借助了类似于引用计数表的表,它通过对象的内存地址做为key,而对应的指针作为value进行管理,在上述代码中objc_initweak就是完成这部分操作,而objc_destroyWeak则是销毁该对象对应的value。所以,weak在修饰只是让weak表增加了记录没有引起引用计数表的变化

  1. 当弱指针指向的对象呗销毁时,弱指针怎么才能自动置为nil? 为什么我们在程序中不能频繁使用weak呢

对象通过objc_release释放对象内存的动作如下:

  • objc_release
  • 因为引用计数为0所以执行dealloc
  • _objc_rootDealloc
  • objc_dispose
  • objc_destructInstance
  • objc_clear_deallocating

而在对象被废弃时最后调用了objc_clear_deallocating,该函数的动作如下:

  1. 从weak表中获取已废弃对象内存地址对应的所有记录2)将已废弃对象内存地址对应的记录中所有以weak修饰的变量都置为nil3)从weak表删除已废弃对象内存地址对应的记录4)根据已废弃对象内存地址从引用计数表中找到对应记录删除

据此可以解释为什么对象被销毁时对应的weak指针变量全部都置为nil,同时,也看出来销毁weak步骤较多,如果大量使用weak的话会增加CPU的负荷而不建议大量使用weak,还有一个原因看下面的代码:

 id __weak obj1 = obj; NSLog(@"obj2-%@",obj1);

编译器转换上述代码如下:

 id obj1; objc_initweak(&obj1,obj); // 从weak表中获取附有__weak修饰符变量所引用的对象并retain id tmp = objc_loadWeakRetained(&obj1); // 将对象放入自动释放池 objc_autorelease; NSLog(@"%@",tmp); objc_destroyWeak(&obj1);

据此当我们访问weak修饰指针指向的对象时,实际上是访问注册到自动释放池的对象。因此,如果大量使用weak的话,在我们去访问weak修饰的对象时,会有大量对象注册到自动释放池,这会影响程序的性能。推荐方案 : 要访问weak修饰的变量时,先将其赋给一个strong变量,然后进行访问

最后一个问题: 为什么访问weak修饰的对象就会访问注册到自动释放池的对象呢?

  • 因为weak不会引起对象的引用计数器变化,因此,该对象在运行过程中很有可能会被释放。所以,需要将对象注册到自动释放池中并在自动释放池销毁时释放对象占用的内存。

id obj = [[NSObject alloc] init];

作用

__unsafe_unretained作用需要和weak进行对比,它也不会引起对象的内部引用计数器的变化,但是,当其指向的对象被销毁时__unsafr_unretained修饰的指针不会置为nil。而且一般__unsafe_unretained就和它的名字一样是不安全,它不纳入ARC的内存管理

// 使用了new分配了内存,objc指向了对象,该对象本身引用计数为1,不需要retain

作用

ARC无效

 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; id obj = [[NSObject alloc] init]; [obj autorelease]; [pool drain];

ARC有效*

 id __autoreleasing obj1 = obj;

如上所示,通过__autoreleasing修饰符就完成了ARC无效时一样的功能

当然,在某一些情况下我们不通过显式指定__autoreleasing关键字就可以完成自动注册到自动释放池的功能,例如以下情况

第一种:

 @autoeleasepool { // 如果看了上面__strong的原理,就知道实际上对象已经注册到自动释放池里面了 id __strong obj = [NSMutableArray array]; }

第二种:

访问__weak修饰的对象时,对象就被注册到了自动释放池

第三种:

以下形式的默认修饰符是__autorelease

  • id *obj;
  • NSObject **obj;

同时,也引出一个问题: 为什么在@property中OC对象使用strong而基本数据类型使用assign?

图片 4属性默认修饰符.png

从表中可以推断出,在ARC在OC对象的默认修饰符是__strong,因此,在@property中使用strong而基本数据类型是不纳入到ARC内存管理中的,__unsafe_unretained也不归ARC管,因此,使用assign对基本数据类型进行修饰

id obj = [NSObject new];

原理 ```objc @autoreleasepool {
 id __autoreleasing obj = [[NSObject alloc] init];}

代码转换如下: ```objc id pool = objc_autoreleasePoolPush(); id obj = objc_msgSend(NSObject,@selector; objc_msgSend(obj,@selector; objc_autorelease; objc_autoreleasePoolPop;

 @autoreleasepool { id __autoreleasing obj = [NSMutableArray array]; }

代码转换如下:

 id pool = objc_autoreleasePoolPush(); id obj = objc_msgSend(NSMutableArray,@selector; objc_retainAutoreleasedReturnValue; objc_autorelease; objc_autoreleasePoolPop;

上述代码,代表的就是自身生成并持有对象、自身不生成但也持有对象的两种__autorelease内存管理情况

非自己生成的对象,自己也能持有

ARC规则

  • 不能使用retain、release、retainCount、autorelease方法(如果ARC下使用会出现编译错误)

  • 不能使用NSAllocateObject、NSDeallocateObject函数(如果ARC下使用会出现编译错误)

  • 不要显式调用dealloc(ARC下,显式调用dealloc并在代码中书写[super dealloc]也会出现编译错误)

  • 使用@autoreleasepool块代替NSAutoreleasePool

 @autoreleasepool{}块相比较NSAutoreleasePool而言显得代码更加整洁、层次性强,而且@autoreleasepool代码快哉ARC或者非ARC下都是可以使用的
  • 需遵守内存管理命名规则
 1) alloc、new、copy、mutableCopy等以这些名字开头的方法都应当返回调用方能够持有的对象 2)init开头的方法必须是实例方法并且要返回对象,返回值要是id或者该方法对应类的对象类似或者其超类或者其子类。另外,init开头的方法也仅仅用作对对象进行初始化操作
  • 不能使用区域
 区域是以前为了高效利用内存的使用率而设计的,但是,目前来说ARC下的模式已经能够有效利用内存,区域在ARC下还是非ARC下都已经被单纯的忽略 
  • 对象型变量不能作为C语言结构体的成员
 OC对象型变量如果成为了C语言结构体的成员,那么,ARC不能掌握该对象的生命周期从而有效管理内存,因此,不能这样使用。 
  • 显式转换"id" 和 "void*"
 非ARC下: id obj = [[NSObject alloc] init]; void *p = obj; 这样的代码是可行的,id和void*可以方便得自由转化 ,但是,在ARC下是不一样的 ARC下id和void*有三个转换的关键字 __bridge、__bridge_retained、__bridge_transfer: id obj = [[NSObject alloc] init]; void *p = (__bridge void*)obj; 注意: __bridge不会引起对象的引用计数变化,因此,安全性不太好。相比较,__bridge_retained不仅仅实现了__bridge的功能而且能让p调用retain方法使p持有对象。另外, __bridge_transfer也是和release方法类似,使用__bridge_transfer进行转化,既让对象p调用一次retain方法,而且原来指针obj会调用一次release方法也非常安全 

// NSMutableArray通过类方法array产生了对象(并没有使用alloc、new、copy、mutableCopt来产生对象),因此该对象不属于obj自身产生的

// 因此,需要使用retain方法让对象计数器+1,从而obj可以持有该对象(尽管该对象不是他产生的)

id obj = [NSMutableArray array];

[obj retain];

不再需要自己持有对象时释放

id obj = [NSMutableArray array]; 

[obj retain];

// 当obj不在需要持有的对象,那么,obj应该发送release消息

[obj release];

// 释放了对象还进行释放,会导致奔溃

[obj release];

无法释放非自己持有的对象

// 释放一个不属于自己的对象

id obj = [NSMutableArray array];

// obj没有进行retain操作而进行release操作,然后autoreleasePool也会对其进行一次release操作,导致奔溃。

后面会讲到autorelease。

[obj release];

针对[NSMutableArray array]方法取得的对象存在,自己却不持有对象,底层大致实现:

+ (id)object {

//自己持有对象

id obj = [[NSObject alloc]init];

[obj autorelease];

//取得的对象存在,但自己不持有对象

return obj;

}

使用了autorelease方法,将obj注册到autoreleasePool中,不会立即释放,当pool结束时再自动调用release。这样达到取得的对象存在,自己不持有对象。

autorelease

顾名思义:autorelease就是自动释放,它和C语言的局部变量类似,C语言局部变量在程序执行时,超出其作用域时,会被自动废弃。autorelease会像C语言局部变量那样对待对象实例,当超出作用域时,对象实例的release实例方法会被调用。我们可以设定autorelease的作用域。

autorelease具体使用方法

生成并持有NSAutoreleasePool对象

调用已分配对象的autorelease实例方法

废弃NSAutoreleasePool对象

图片 5

NSAutoreleasePool生命周期

NSAutoreleasePool对象的生命周期就是一个作用域,对于所有调用过autorelease实例方法的对象,在废弃NSAutoreleasePool对象时,都会调用其release实例方法。如上图所示。

用源代码表示如下:

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

id obj = [[NSObject alloc] init];

[obj autorelease];

[pool drain];

在[pool drain]调用时,NSAutoreleasePool被销毁,obj的release方法会被触发。obj被释放。

autorelease苹果实现

可以通过objc4库的runtime/objc-arr.mm来确认苹果中autorelease的实现。

class AutoreleasePoolPage

{

static inline void *push()

{

相当于生成或持有NSAutoreleasePool类对象

}

static inline void *pop(void *token)

{

相当于废弃NSAutoreleasePool类对象

releaseAll();

}

static inline id autorelease(id obj)

{

相当于NSAutoreleasePool类的addObject类方法 

AutoreleasePoolPage *autoreleasePoolPage = 取得正在使用的AutoreleasePoolPage实例;

autoreleasePoolPage->add(obj);

}

id *add(id obj)

{

将对象追加到内部数组中

}

void releaseAll()

{

调用内部数组中对象的release实例方法

}

};

void *objc_autoreleasePoolPush(void)

{

return AutoreleasePoolPage::push();

}

void objc_autoreleasePoolPop(void *ctxt)

{

AutoreleasePoolPage::pop(ctxt);

}

id *objc_autorelease(id obj)

{

return AutoreleasePoolPage::autorelease(obj);

}

我们使用调速器来观察NSAutoreleasePool类方法和autorelease方法的运行过程,如下所示,这些方法调用了关联于objc4库autorelease实现的函数。

//等同于objc_autoreleasePoolPush()

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

id obj = [[NSObject alloc] init];

//等同于objc_autorelease(obj)

[obj autorelease];

//等同于objc_autoreleasePoolPop(pool)

[pool drain];

在iOS程序启动后,主线程会自动创建一个RunLoop,苹果在主线程 RunLoop 里注册了两个 Observer,其回调都是 _wrapRunLoopWithAutoreleasePoolHandler()。

第一个 Observer 监视的事件是 Entry(即将进入Loop),其回调内会调用 _objc_autoreleasePoolPush() 创建自动释放池。其 order 是-2147483647,优先级最高,保证创建释放池发生在其他所有回调之前。

第二个 Observer 监视了两个事件: BeforeWaiting(准备进入休眠) 时调用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 释放旧的池并创建新池;Exit(即将退出Loop) 时调用 _objc_autoreleasePoolPop() 来释放自动释放池。这个 Observer 的 order 是 2147483647,优先级最低,保证其释放池子发生在其他所有回调之后。

在主线程执行的代码,通常是写在诸如事件回调、Timer回调内的。这些回调会被 RunLoop 创建好的 AutoreleasePool 环绕着,所以不会出现内存泄漏,开发者也不一定非得显示创建 Pool 了。runloop相关

尽管如此,在大量产生autorelease的对象时,只要NSAutoreleasePool没有被废弃,那么产生的对象就不能被释放,因此会产生内存不足的现象。例如:

读入大量图像的同时改变其尺寸。图像文件读入到NSData对象,并从中生成UIImage对象,改变该对象尺寸后生成新的UIImage对象。这种情况会产生大量autorelease对象。

for (int i = 0; i < 图片数; ++ i) {

/*

读入图片

大量产生autorelease的对象

由于没有废弃NSAutoreleasePool对象

导致产生内存峰值,可能内存不足而奔溃

*/

}

在这种情况下,应当在适当的位置生成、持有或废弃NSAutoreleasePool对象

for (int i = 0; i < 图片数; ++ i) {

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

/*

读入图片

大量产生autorelease的对象

*/

[pool drain];

/*

通过释放pool

autorelease的对象被release,就不会产生内存峰值

*/

}

使用容器的block版本的枚举器时,内部会自动添加一个AutoreleasePool:

[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {

// 这里被一个局部@autoreleasepool包围着

}];

所有权修饰符及其原理

在 ARC 特性下有 4 种与内存管理息息相关的变量所有权修饰符值得我们关注:

__strong

__weak

__unsafe_unretaied

__autoreleasing

说到变量所有权修饰符,有人可能会跟属性修饰符搞混,这里做一个对照关系小结:

assign 对应的所有权类型是 __unsafe_unretained。

copy 对应的所有权类型是 __strong。

retain 对应的所有权类型是 __strong。

strong 对应的所有权类型是 __strong。

unsafe_unretained对应的所有权类型是__unsafe_unretained。

weak 对应的所有权类型是 __weak。

本文由乐虎游戏发布于计算机资讯,转载请注明出处:iOS内部存款和储蓄器处理(MRC、ARC)深入显出

关键词:

iOS 用RunTime重写KVO&lt;附德姆o&gt;

3.在第二步之后,我点击一个button ,push 到另外一个ViewController(TestViewController)里面,然后在TestViewController里面,点击...

详细>>

乐虎国际登录四十二十四线程与NSTimer

NSInteger转 Byte 数组,长度为2 1.Ios主线程,也称UI线程,在主线程中使用NSTimer,runloop是自动开启的,(如果NSTimer当前所...

详细>>

3D touch

Peek and Pop peek能够让客商在不离开当前内容的前提下预览贰个item和进行有关的actions,不过前提是item要求扶持当响应叁...

详细>>

StatusBarNotification-Swift

如果大家还不错的话,希望点个赞,鼓励一下小弟,项目的地址为:StatusBarNotification;在这里还是要感谢jaydee3,从他...

详细>>