>

因为调用了对象的release方法是指将对象的引用计

- 编辑:澳门博发娱乐官网 -

因为调用了对象的release方法是指将对象的引用计

当前的主流iPhone实际物理内存一般都不超过1G,要高效利用的这1G空间,达到高性能,ios是怎么做的嗯?iOS的操作系统更是抛弃了不必要的复杂——系统层面不支持App内存页换出,当内存吃紧时,对于可以重新载入的只读数据来说,直接清理掉,而对于可写的数据,只能通过App自己去管理维护。内存紧张时,iOS会向App发起memory warning,不配合释放足够内存者,kill掉!

基础知识

iOS提供了两种管理内存的方式,分别为MRC(手动)和ARC(自动)。

1.ARC

ARC是OC编译器的特性,而不是运行时特性或者垃圾回收机制,ARC所做的只不过是在代码编译时为你自动在合适的位置插入release或autorelease.只要没有强指针指向对象,对象就会被释放

  • 前端编译器
    前端编译器会为"拥有的"每一个对象插入相应的release语句.如果对象的所有权修饰符是_strong,那么它就是被拥有的.如果在某个方法内创建了一个对象,前端编译器会在末端自动插入release语句用以销毁它.而类拥有的对象(实例变量/属性)会在dealloc方法内被释放..事实上,你并不需要写dealloc方法或调用父类的dealloc方法.ARCUI自动帮你完成一切.此外,由编译器生成的代码甚至会比你自己写的release语句的性能还要好,这是因为编译器可以做出一些假设.在ARC中.没有类可以覆盖release方法,也没有调用它的必要.ARC会通过直接使用objc_release来优化调用过程.而对于retain也是同样的方法.ARC会调用objc_retain来取代保留消息.
  • ARC优化器
    虽然前端编译器听起来很厉害的样子,但是在代码中有时仍会出现几个对retain和release的重复调用.ARC优化器负责移除多余的retain和release语句,确保生成的代码运行速度高于手动引用计数的代码.
> 下面关于Objective-C内存管理的描述错误的是 
A:当视同ARC来管理内存时.代码中不可以出现autorelease
B:autoreleasepool 在drain的时候会释放在其中分配的对象arc
C:当使用ARC来管理内存时,在线程中大量分配对象而不用autoreleasepool则可能造成内存泄露
D:在使用ARC的项目中不能使用NSZone

- 参考答案:A
- 理由:ARC只是在大多数时候,编译器自动为我们添加上内存管理的代码,只是我们的源代码看不到而已,但是在编译时,编译器会添加上相关内存管理代码.对于自动释放池,在drain时会将自动释放池中的所有对象的引用计数减一.若引用计数为0.则会自动释放掉内存,如果在线程中需要大量分配内存,我们理应天健上自动释放池,以防止内存泄露.比如在for循环中要分配大量的内存处理数据,那么我们应该在for循环内添加自动释放池,在每个循环后就讲内存释放掉.防止内存泄漏.在ARC项目中,自然不能手动使用NSZone,也就不能调用父类的dealloc.

MRC文件在ARC工程混合编译时,需要在文件的Compiler Flags上添加什么参数
A:-shared
B:-fno-objc-arc
C:-fobjc-arc
D:-dynamic



参考答案: B

类目的优缺点:优点:不需要通过增加子类而增加现有类的方法,而且类中的方法与原始类方法基本没有区别。缺点:无法向类目添加实例变量,覆盖原始类方法后,原始的类方法没办法调用。

最简单来说分为两大部分:指令+数据。再细分一点,五部分:代码,初始化数据区,未初始化数据区,堆,栈。** 主要的布局如下图所示:**

MRC(人工引用计数),手动管理内存。

MRC模式下,所有的对象都需要手动的添加retain、release代码来管理内存。使用MRC,需要遵守谁创建,谁回收的原则。也就是谁alloc,谁release;谁retain,谁release。
当引用计数为0的时候,必须回收,引用计数不为0,不能回收,如果引用计数为0,但是没有回收,会造成内存泄露。如果引用计数为0,继续释放,会造成野指针。为了避免出现野指针,我们在释放的时候,会先让指针=nil。

2.在什么情况下使用weak关键字.相比assign有什么不同?

  • 什么情况下使用weak
  • 在ARC中,在有肯能出现循环引用的时候,往往要通过让其中一端使用weak来解决.比如;delegate代理属性.自身已经对它进行了一次强引用,没有必要再强引用一次.此时也会使用weak,自定义IBOutlet控件属性一般也是用weak;当然,也可使用strong
  • 两者有什么不同
    • weak此特质表明该属性定义了一种"非拥有关系".为这种属性设置新值时.设置方法即不保留新值,也不释放旧值.此特质同assign类似,然而此属性所指的对象遭到摧毁时,属性值也会清空.而assign的"设置方法"只会执行针对"纯量类型"(例如CGFloat或NSInteger等)的简单赋值操作.
    • assign可以用非OC对象,而weak必须用于OC对象,

iOS是否支持垃圾回收机制:iOS开发只支持手动内存管理(MRC)和自动内存管理(ARC),Mac开发支持GC垃圾回收机制,10.8之后弃用了GC,推荐使用ARC。

澳门博发娱乐官网 1内存结构.png

ARC(自动引用计数),自动管理内存。

在ARC模式下,只要没有强指针(强引用)指向对象,对象就会被释放。在ARC模式下,不允许使用retain、release、retainCount等方法。并且,如果使用dealloc方法时,不允许调用[super dealloc]方法。
ARC模式下的property变量修饰词为strong、weak,相当于MRC模式下的retain、assign。strong :代替retain,缺省关键词,代表强引用。weak:代替assign,声明了一个可以自动设置nil的弱引用,但是比assign多一个功能,指针指向的地址被释放之后,指针本身也会自动被释放。

3.调用了对象的release方法惠晓辉对象吗?

答案是不会,因为调用了对象的release方法是指将对象的引用计数器-1,当对象的引用计数器为0的时候回调用了对象的dealloc方法才能进行释放对象的内存.

内存中堆区和栈区的区别:栈区由编译器自动分配释放,存放方法的参数值,局部变量的值等。堆区一般由程序员分配与释放,若程序员不释放,则内存溢出。

** 从代码上来分析:**

@property修饰的内存管理

MRC : retain、assign、copy
ARC : strong、weak、copy

自动释放池常见面试代码?

for (int i = 0;i < someLargeNumber; i++)
{
     NSString *string = @"Abc";
     string = [ string lowercaseString];
     string = [string strinByAppendingString:@"xyz"];
    NSLog(@"%@",string);
}

问;以上代码存在什么样的问题;如果循环次数非常大时,应该如何修改?

    存在问题;问题出在每一次循环的时候,就会有一个string加到当前runloop中的自动释放池中,只有当自动释放池被release的时候.自动释放池中的表示了autorelease的这些数据所占用的内存空间才能被释放掉.假设,当someLargeNumber达到一定程度时,内存空间将被耗尽,与此同时这些数据有有没有被释放掉.所以会造成内存泄漏的情况发生.

解决方法;1,如果i比较大,可以用@autoreleasepool{}解决,放在for循环外,循环结束后,销毁创建的对象.解决占据栈区内存的问题.
2.如果i玩命大,一次循环都会造成自动释放池被填满,自动释放池放在for循环内,每次循环都将上一次创建的对象release.

经过修改之后.
for(int i=0;i<1000;i++){
NSAutoreleasePool *pool = [[NSAuroreleasePool alloc] init];
NSString *string = @"Abc";
string = [string lowercaseString];
string = [string stringByAppendingString:@"xyz"];
NSLog(@"%@",string);
//释放池
[pool drain];
}

定义属性的时候,什么时候使用assign、retain、copy、nonatomic?

//main.cpp int a = 0; 全局初始化区 char *p1; 全局未初始化区 main() { int b; 栈 char s[] = "abc"; 栈 char *p2; 栈 char *p3 = "123456"; 123456在常量区,p3在栈上。 static int c =0; 全局初始化区 p1 = malloc; p2 = malloc; 分配得来得10和20字节的区域就在堆区。 strcpy(p1, "123456"); 123456放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。 } 

关于栈和堆

栈(stack): 由编译器自动分配释放,存放函数的参数值,局部变量的值等
堆(heap): 一般由程序员分配释放,若程序员不释放,程序结束时可能由系统回收

OC对象存放于堆里面(堆内存要程序员手动回收)
非OC对象一般放在栈里面(栈内存会被系统自动回收)
我们创建一个oc对象,定义一个变量,调用一个函数或者方法都会提高App的内存占用。

Objective-C对象的内存布局是怎样的?

由于Objective-C中没有多继承,因此其内存布局还是很简单的,就是最前面的有个isa指针,然后父类的实例变量存放在子类成员变量之前.

(1)assign,普通赋值,一般使用于基本的数据类型,常见委托设计模式,防止循环引用。

  • 栈区:由编译器自动分配和释放,存放 函数的参数值、局部变量值等,栈是向低地址扩展的数据结构,是一块连续的内存区域。即栈顶的地址和栈的最大容量是系统预先设定好的。

  • 堆:一般由程序员分配和释放、若程序员不释放,程序结束后由OS回收。堆是不连续的内存区域,向高地址扩展的数据结构。

  • 区别:堆只有动态分配的,栈有静态分配(编译器完成,比如局部变量)和动态分配的(alloc函数,编译器来释放);栈的分配效率比堆高,但堆灵活和内存占用大,由于堆空间的不连续性,会存在碎片问题。

  • 全局区和静态区:全局变量和静态变量是放在一块的,内部划分初始化和未初始化两个块区。程序结束后由系统释放。

  • 文字常量区:常量字符串放在这里的,程序结束后由系统释放

  • 程序代码区:存放函数体的二进制代码

内存泄露和过度释放

iOS开发过程中,使用Objective-C分配的堆内存都是通过引用计数来做保留和释放的。一块内存初始分配,引用计数为1,此后每新增一个强引用,引用计数增加1;释放正好相反,每一次release,引用计数减1,直到为0,对象所用内存被真正free掉,以被再次复用。然而,实际开发当中,总有一些原因导致引用计数无法按正常逻辑减少到0,或者减少到0之后仍然被调用release,前者是内存泄露,后者则是过度释放。

当内存泄露发生时,运行的App不会直接第发生明显问题,但废弃内存得不到回收,在长时间持续运行后,App进程会由于可用内存不断变低而被kill或带来其它隐患。(内存泄露的隐患)
过度释放,是对同一个对象释放了过多的次数,其实当引用计数降到0时,对象占用的内存已经被释放掉,此时指向原对象的指针就成了“悬垂指针”,如若再对其进行任何方法的调用,(原则上)都会直接crash(然而由于某些特殊的情况,不会马上crash)。(过度释放的隐患)

什么时候需要在程序中创建内存池?

当用户子自己创建的数据线程,则主要创建该线程的内存池

(2)retain,保留计数,获取对象的所有权,引用计数在原先的基础之上+1.

** MRC(iOS 5.0 之前)**

内存工具(Instruments)

如果我们不创建内存池,是否有内存池提供给我们?

界面线程维护着自己的内存池,用户自己创建的数据线程,则需要闯将该线程的内存池.

(3)copy,用来复制对象,一般字符串使用copy,Foundation中的不可变对象使用copy效果相当于retain,只是引用计数+1.

MRC,手动引用计数, 手动管理内存的三个原则

内存泄漏

为了更好地处理内存泄露的问题,平时在写代码的时候要注意循环引用,学会善用weak,良好的编程习惯,检查一些显而易见的语法问题。
对于运行时出现的问题,我们可用 Instruments 中的 AllocationLeaks 来不断重复操作App,发现和定位内存泄露点。当运行时发生显示内存泄露时, Leaks 会在时间轴上标出红色指示线,同时在 Instruments 的下方会列出调用细节,结合系统提供的 malloc 历史,其中包含引用计数变化情况,以及调用栈可以很直接地找到泄露原因。
同时对于一些“隐式”的情况,需要反复操作,同时观察 Allocation 中只增不减,一直创建新对象而不释放老对象的情况。

苹果是如何实现autoreleasepool的?

autoreleasepool以一个队列数组的形式实现,主要通过下列三个函数完成.
 - objc_autoreleasepoolPush
 - objc_autoreleasepoolPop
 - objc_autorelease
看函数名就可以知道,瑞autorelease分别执行push,pop操作.销毁对象时执行release操作.

(4)nonatomic,非原子性操作,不加同步,多线程并发访问会提高性能。

  1. 谁创建,谁释放,如果你通过alloc new copy来创建一个对象,那么你必须调用release或者这autorrelease。换句话说,,不是你创建的,你就不用去释放。

  2. 除了alloc、new、copy之外的方法创建的对象都被声明了autorelease。

  3. 谁retain,谁release。只要你调用了retain,无论这个对象是如何生成的,你都要调用release

野指针与僵尸对象

野指针:指针指向的对象已经被回收掉了,这个指针就叫做野指针
僵尸对象:一个已经被释放的对象,就叫做僵尸对象
详情点这里
我们有两种方式找到产生的原因:
1.使用 InstrumentsZombies 工具 详情点这里
2.手动开启 Product->Scheme->Edit Scheme->Run xxx.app,在右边框中选中 Enable Zombie Objects(手动开启僵尸对象,会导致内存增长,也会影响 Leaks 工具的调试)

iOS应用性能调优的25个建议和技巧

objc使用什么机制管理对象内存?

objc管理内存时通过引用计数器的机制来决定对象是否需要释放.每次runloop完成一个循环的时候,都会检查对象的retainCount.如果retainCount为0,说明该对象没有地方需要继续使用了.可以释放掉了.

ARC(自动内存管理)会造成内存泄漏吗?

** ARC(iOS 5.0 之后)**

为什么要进行内存管理?

因为移动设备的内存极其有限,当一个程序所占内存达到一定值时,系统会发出内存警告.当程序达到更大的值时,程序会出现闪退,影响用户体验,为了保证程序的运行流畅,所以必须进行内存管理

ARC中如果内存管理不当的话,照样会造成内存泄漏,比如:ARC中也会循环引用导致内存管理,OC对象与CoreFoundation类之间桥接时,管理不当的话也会产生内存泄漏。

自 iOS5.0 之后,苹果开始支持ARC(Automatic Reference Counting)自动引用计数,是一个编译器特性,编译器自动的添加完成了内存管理的引用计数代码。arc只针对OC对象类型,OC类型:任何继承于NSObject对象都属于OC的类型。它对基本数据类型不管用。

内存管理的范围

管理所有继承自NSObject的对象,对基本数据类型无效.是因为对象和其他数据类型在系统中存储额空间不一样,其它局部变量主要存储在栈区(因为基本数据类型占用的存储空间是固定的,一般存放于栈区),而对象存储于堆中,当代码块结束时,这个代码块所涉及到的所有局部变量会自动将栈区清空,只想对象的指针也会被回收,这时对象就没有指针指向,但依然存在于堆内存中,造成内存泄露.

当释放对象的时候,为什么需要调用[super dealloc]方法?

** @property属性 **

* objc使用什么机制管理对象内存(或者内存管理方式有哪些)?

MRC手动内存管理
ARC自动引用计数.
Garbage collection(垃圾回收),但是iOS不支持垃圾回收,ARC作为LLVM3.0编译器的一项特性,在iOS5.0之后推出的.
ARC的判断准则,只要没有强指针指向对象,对象就会被释放.

这是因为子类继承于父类,那么子类中的一些实例变量也是继承于父类的,因此我们需要调用父类的方法将父类所拥有的实例进行释放掉。

OC中@property属性关键字的使用(assign/weak/strong/copy) ,property的属性默认是:readwrite、strong、 atomic

内存管理的原则.

  • 只要还有人使用这个对象,那么这个对象就不会被回收.
  • 只要你想使用这个对象,那么就应该让这个对象的引用计数器加1;
  • 当你不想使用这个对象时,应该让对象的引用计数器减1;
  • 谁创建,就有谁来release.
    • 如果通过alloc,new,copy来创建一个对象,当不想使用这个对象的时候就必须调用release或者是autorelease让引用计数器减1;
    • 谁创建,谁负责release
    • 只要你调用了retain,无论这个对象是如何生成,都需要调用release.
      总结:
      有加就有减,曾让某个计数器加1,就应该让其在最后减1;

自动释放池是什么?如何工作?

本文由胜博发-编程发布,转载请注明来源:因为调用了对象的release方法是指将对象的引用计