内存管理

11.看下面的程序,第一个NSLog会输出什么?这时str的retainCount是多少?第二个和第三个呢? 为什么?

NSMutableArray* ary = [NSMutableArray array];
NSString *str = [NSString stringWithFormat:@"test"];
[str retain];
[ary addObject:str];
NSLog(@"%@%ld",str,(unsigned long)[str retainCount]);
[str retain];
[str release];
[str release];
NSLog(@"%@%ld",str,(unsigned long)[str retainCount]);
[ary removeAllObjects];
NSLog(@"%@%ld",str,(unsigned long)[str retainCount]);

答:
str的retainCount创建+1,retain+1,加入数组自动+1
3
retain+1,release-1,release-1
2
数组删除所有对象,所有数组内的对象自动-1
1

9.内存管理的几条原则时什么?按照默认法则.哪些关键字生成的对象需要手动释放?在和property结合的时候怎样有效的避免内存泄露?

谁创建,谁释放
谁retain,谁release
除alloc、new或copy之外的方法创建的对象都被声明了autorelease
关键字alloc 或new 生成的对象需要手动释放;
设置正确的property属性,对于retain需要在合适的地方释放。

8.Objective-C如何对内存管理的,说说你的看法和解决方法?

1、自动释放池:可以简化内存管理代码,系统会自动释放池中的对象,但系统并不是立即释放池中的对象,而是在一个run loop之后才释放。
2、手动内存计数:当我们对对象进行了alloc、retain、copy或new操作后,我们拥有了对象的控制权,因此需要对其进行release,当计数器到0的时候,系统会自动调用alloc方法来释放内存中的对象。
3、ARC自动内存计数:使用了同样的引用计数机制,不需要手动添加的用来处理内存管理的代码可以自动地由编译器完成。

3、objc如何管理内存

1).MRC(manual retain-release)手动内存管理
2).ARC(automatic reference counting)自动引用计数
3).Garbage collection (垃圾回收)
但是iOS不支持垃圾回收, ARC作为LLVM(编译器的架构系统,用c++编写而成的) 3.0编译器的一项特性, 在iOS5.0 (Xcode4) 版本后推出的自动内存管理, 苹果推荐使用ARC技术来管理内存, 节约时间 , 提高效率 , 减少代码量 , 降低出错几率. 开发者不需要再手动写入retain,release,autorelease三个关键字,手动管理内存, 编译器将自动在代码合适的地方插入retain,release,autorelease进行内存管理.ARC的判断准则, 只要没有强指针指向对象, 对象就会被释放.

10、autorelease的对象是在什么时候被release的?

对象是引用计数为0时被release。
autorelease实际上只是把对release的调用延迟了,对于每一个Autorelease,系统只是把该Object放入了当前的Autorelease pool中,当该pool被释放时,该pool中的所有Object会被调用Release。对于每一个Runloop, 系统会隐式创建一个Autorelease pool,这样所有的release pool会构成一个象CallStack一样的一个栈式结构,在每一个Runloop结束时,当前栈顶的Autorelease pool会被销毁,这样这个pool里的每个Object(就是autorelease的对象)会被release。那什么是一个Runloop呢? 一个UI事件,Timer call, delegate call, 都会是一个新的Runloop。

autorelease和release的区别

1.如果能够真正的理解autorelease,那么才是理解了Objective
c的内存管理。Autorelease实际上只是把对release的调用延迟了,对于每一个Autorelease,系统只是把该Object放入了当前的Autorelease pool中,当该pool被释放时,该pool中的所有Object会被调用Release。
实际上对于 [NSString stringWithFormat:1.0] 这类构造函数返回的对象都是autorelease的。
2. autorelease pool来避免频繁申请/释放内存(就是pool的作用了)。这个应该是相对比较好理解的。

总结:
(1)一定要注意Autorelease pool的生存周期,理解Runloop,避免在对象被释放后使用。
(2)[NSString stringWithFormat]这类函数返回的对象是不需要再自己release的,它已经被autorelease了, 如果你想把它当一个全局对象使用,那必须自己再retain, 释放时再release。

为什么需要Auto release ?
这个auto release有什么好,象C/C++那样,自己申请,自己释放,完全可控不好么,这个auto relase完全不可控,你都不知到它什么时候会被真正的release。我的理解它有一个作用就是可以做到每个函数对自己申请的对象负责,自己申请,自己释放,该函数的调用者不需要关心它内部申请对象的管理。 在下面这个例子中,Func1的调用者不需要再去关心obj的释放。
ClassA *Func1()
{
ClassA *obj = [[[ClassA alloc]init]autorelease];
return obj;
}

(1)在Iphone项目中,大家会看到一个默认的Autorelease pool,程序开始时创建,程序退出时销毁,按照对Autorelease的理解,岂不是所有autorelease pool里的对象在程序退出时才release, 这样跟内存泄露有什么区别?
答案是,对于每一个Runloop, 系统会隐式创建一个Autorelease pool,这样所有的release pool会构成一个象CallStack一样的一个栈式结构,在每一个Runloop结束时,当前栈顶的Autorelease pool会被销毁,这样这个pool里的每个Object会被release。

那什么是一个Runloop呢? 一个UI事件,Timer call, delegate call, 都会是一个新的Runloop。例子如下:
NSString* globalObject;
- (void)applicationDidFinishLaunching:(UIApplication *)application  {
globalObject = [[NSString alloc] initWithFormat:@"Test"];
NSLog(@"Retain count after create: %d", [globalObject retainCount]); // output 1.
[globalObject retain];
NSLog(@"Retain count after retain: %d", [globalObject retainCount]); // output 2.
}

- (void)applicationWillTerminate:(UIApplication *)application  {
NSLog(@"Retain count after Button click runloop finished: %d", [globalObject retainCount]);
// 输出1. Button click loop finished, it's autorelease pool released, globalObject get released once.
}

-(IBAction)onButtonClicked  {
[globalObject autorelease];
NSLog(@"Retain count after autorelease: %d", [globalObject retainCount]);
// 输出2。 Autorelease被call, globalObject被加如当前的AutoreleaePool。
}

Cocoa的内存管理分为 索引计数法(Reference Counting/ Retain Count)和 垃圾收集法(Garbage Collection)。而iPhone上目前只支持前者,所以autorelease就成为很多人的“捷径”。

但是!autorelease其实并不是“自动释放”,不像垃圾收集法,对对象之间的关系侦测后发现垃圾-删除。但是autorelease其实是“延后释放”,在一个运行周期后被标记为autorelease会被释放掉。

切记小心使用autorelease,理解autorelease,防止在你还需要该对象的时候已经被系统释放掉了 误释放对象

问题一:
value = [array objectAtIndex:n]; //得到一个数组中的对象
[arry removeObjectAtIndex:n]; //卸载那个对象因为value得到了那个对象,但是由于另外一个拥有者release了该对象,所以其实value现在成了摇摆指针(无效数据)

问题二:
myArray = [NSArray array];
...
[myArray release];NSArray返回的是一个自动释放对象,不仅myArray不应该在一段时间后release,而应该在适当的时候先retain,以防止该array被系统误释放。

问题三:
rocket = [rocketLauncher aRocket];
[rocketLauncher release];和array这种数据收集类对象一样,如果我们得到了一个类的子对象而不retain它,那么在原父类被释放的时候,这个rocket其实也会失去其意义。

4、以下每行代码执行后,person对象的retainCount分别是多少?请在每行语句右边标出。

1、Person *person = [[Person alloc] init];    1
  [person retain];    2
  [person autorelease];    2
  [person release];    1
2、Person *person = [[Person alloc] init];    1
  [person retain];    2
  [person release];    1
  [person release];    0
3、NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  Person *person = [[Person alloc] init];    1
  [person autorelease];     1
  [pool release];    0

1、内存优化

iOS优化(一)内存优化经验:http://www.jianshu.com/p/ef52250df748
ios-01-内存优化整理:http://www.jianshu.com/p/ba3e159f7743

内存管理的原则:凡是操作他的函数中出现了"create,copy,retaion" 都必须有一个 函数中包含"release"的方法与之对应

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

一、什么是自动释放池
1、Autorelease pool
自动释放池(Autorelease pool)是OC的一种内存自动回收机制,可以将一些临时变量通过自动释放池来回收统一释放
自动释放池本身销毁的时候,池子里面所有的对象都会做一次release操作
2、autorelease
任何OC对象只要调用autorelease方法,就会把该对象放到离自己最近的自动释放池中(栈顶的释放池)。

二:O-C当中的内存释放,并不是像java/.net那样有一个自动的释放池,开发人员不用去关心有关内存释放的问题,O-C里面的自动释放池比c语言的手动内存管理要好一些,但是相对于java/.net来说又弱一些,所以说O-C当中的释放属于半自动的释放池。

三、如何创建一个自动释放池
//ios5.0新方式
@autoreleasepool {
}
//ios5.0之前的老方式
NSAutoreleasePool *pool=[[NSAutoreleasePoolalloc]init];
[pool release];

四、自动释放池如何释放对象内存
黄金法则:如果对一个对象使用了alloc,[mutable]copy,retain,那么必须使用相应的release或者autorelease。

17.下面关于Objective-C内存管理的描述错误的是(A )

A.当使用ARC来管理内存时,对象的retain,dealloc方法不会被调用
B.autoreleasepool在drain的时候会释放在其中分配的对象
C.当使用ARC来管理内存时,在线程中大量分配对象而不用autoreleasepool则可能会造成内存泄露
D.在使用ARC的项目中不能使用NSZone

101.iOS有垃圾回收机制吗?它是以怎样的机制来工作的?

答: OC是支持垃圾回收机制的(Garbage collection简称GC),但是apple的移动终端中,是不支持GC的,Mac桌面系统开发中是支持的。
移动终端开发是支持ARC(Automatic Reference Counting的简称),ARC是在IOS5之后推出的新技术,它与GC的机制是不同的。我们在编写代码时,不需要向对象发送release或者autorelease方法,也不可以调用delloc方法,编译器会在合适的位置自动给用户生成release消息(autorelease),ARC的特点是自动引用技术简化了内存管理的难度.

63.自动释放池底层怎么实现?

答:自动释放池以栈的形式实现:当你创建一个新的自动释放池时,它将被添加到栈顶。当一个对象收到发送autorelease消息时,他被添加到当前线程的处于栈顶的自动释放池中,当自动释放池被回收时,他们从栈中被删除,并且会给池子里面所有的对象都会做一次release操作。

65.简单描述一下客户端的缓存机制?

答案:无法简述,详细了解下,明白了够装逼就好http://www.cnblogs.com/wendingding/p/3950198.html

1、内存中的栈和堆的区别是什么?那些数据在栈上,哪些在堆上?

栈:编译器自动分配释放,存放函数的参数值,局部变量的值。
堆:由程序猿管理释放,存放静态变量,字符常量,资源,编译运行时的数据。

1.内存中的栈和堆得区别是什么?哪些数据在栈上?哪些在堆上?

一、堆栈空间分配区别:
1、栈(操作系统):由操作系统自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈;
2、堆(操作系统): 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表。

二、堆栈缓存方式区别:
1、栈使用的是一级缓存, 他们通常都是被调用时处于存储空间中,调用完毕立即释放;
2、堆是存放在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定(并不是一旦成为孤儿对象就能被回收)。所以调用这些对象的速度要相对来得低一些。

三、堆栈数据结构区别:
堆(数据结构):堆可以被看成是一棵树,如:堆排序;
栈(数据结构):一种先进后出的数据结构。

栈空间中一般存储基本类型,对象的地址
堆空间一般存放对象本身,block的copy等。

182.堆和栈的区别?

答:答:栈完全是由系统管理的,堆是由程序员自己控制管理的,包括内存空间的开辟和释放.栈是先进后出.

315、Objective-C堆和栈的区别?

答:
1、管理方式:
对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak。

2、申请大小:
栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。

堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。

3、碎片问题:
对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出

4、分配方式:
堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。

5、分配效率:
栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的。

9.简单阐述堆和栈的区别

栈:
在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
堆:
就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。

堆和栈主要的区别有以下几点:
1、管理方式不同;
2、空间大小不同;
3、能否产生碎片不同;
4、生长方向不同;
5、分配方式不同;
6、分配效率不同;

1、管理方式:
对于栈来讲,是由编译器自动管理,无需我们手工控制;
对于堆来说,释放工作由程序员控制,容易产生memory leak。

2、空间大小:
一般来讲在32位系统下,堆内存可以达到4G的空间,从这个角度来看堆内存几乎是没有什么限制的。但是对于栈来讲,一般都是有一定的空间大小的,例如,在VC6下面,默认的栈空间大小是1M(好像是,记不清楚了)。
当然,我们可以修改:
打开工程,依次操作菜单如下:Project->Setting->Link,在Category 中选中Output,然后在Reserve中设定堆栈的最大值和commit。
注意:reserve最小值为4Byte;commit是保留在虚拟内存的页文件里面,它设置的较大会使栈开辟较大的值,可能增加内存的开销和启动时间。

3、碎片问题:
对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。
对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出,在他弹出之前,在他上面的后进的栈内容已经被弹出,详细的可以参考数据结构,这里我们就不再一一讨论了。

4、生长方向:
对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向;
对于栈来讲,它的生长方向是向下的,是向着内存地址减小的方向增长。

5、分配方式:
堆都是动态分配的,没有静态分配的堆。
栈有2种分配方式:静态分配和动态分配。
*静态分配是编译器完成的,比如局部变量的分配。
*动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。

6、分配效率:
栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。
堆则是C/C++函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考数据结构/操作系统)在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多。

从这里我们可以看到,堆和栈相比,由于大量new/delete的使用,容易造成大量的内存碎片;由于没有专门的系统支持,效率很低;由于可能引发用户态和核心态的切换,内存的申请,代价变得更加昂贵。所以栈在程序中是应用最广泛的,就算是函数的调用也利用栈去完成,函数调用过程中的参数,返回地址,EBP和局部变量都采用栈的方式存放。所以,我们推荐大家尽量用栈,而不是用堆。

虽然栈有如此众多的好处,但是由于和堆相比不是那么灵活,有时候分配大量的内存空间,还是用堆好一些。

无论是堆还是栈,都要防止越界现象的发生(除非你是故意使其越界),因为越界的结果要么是程序崩溃,要么是摧毁程序的堆、栈结构,产生意想不到的结果,就算是在你的程序运行过程中,没有发生上面的问题,你还是要小心,说不定什么时候就崩掉,那时候debug可是相当困难的。

1、程序在内存中运行时,内存分几个区?各自用途?

答:
栈区:由编译器自动分配释放存放函数的参数值,局部变量的值等。在高级语言中不需要显式的分配和释放
堆区:一般由程序员手动分配释放,如果不释放可有由系统释放。
数据区:存储全局和静态变量。初始化的全局和静态变量在一块区域,未初始化的放在相邻的一块区域,程序结束后由系统释放。
代码区:存放函数体的二进制代码。

1、什么情况下会出现内存的循环引用

1.NSTimer-他的target是self的时候,要手动停止定时器
2.Block-retain circle
3.delegate

320、autorelease和垃圾回收机制(gc)有什么关系?

答:autorelease只是延迟释放,gc是每隔一段时间询问程序,看是否有无指针指向的对象,若有,就将它回收。他们两者没有什么关系。

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

答:当向一个对象发送一个autorelease消息时,Cocoa就会将该对象的一个引用放入到最新的自动释放池。它仍然是个正当的对象,因此自动释放池定义的作用域内的其它对象可以向它发送消息。当程序执行到作用域结束的位置时,自动释放池就会被释放,池中的所有对象也就被释放。

177.队列和栈有什么区别。

栈(Stack):是限定只能在表的一端进行插入和删除操作的线性表。
队列(Queue):是限定只能在表的一段进行插入和在另一端进行删除操作的的线性表。
队列和栈的区别:
1)、队列是先进先出,栈是先进后出
2)、遍历数据速度不同,队列遍历速度要快得多

44.你在开发大型项目时,如何进行内存泄露检测的?

可以通过xcode的自带工具run---start with performance tool里有instruments下有个leaks工具,
启动此工具后,运行项目,工具里可以显示内存泄露的情况,双击可找到源码位置,可以帮助进行内存泄露的处理。

11 、类方法创造的对象要不要用release释放?

答:任何方法创建的对象对遵从内存管理原则,用alloc方法分配的对象就需要释放,如果用类方法创建对象时候,没有用到alloc,那么分配空间的问题,已经在方法内部做了处理,所以我们就不需要去释放了。

17、下面关于Objective-C内存管理的描述错误的是

A. 当使用ARC来管理内存时,代码中不可以出现autorelease
B. autoreleasepool 在 drain 的时候会释放在其中分配的对象
C. 当使用ARC来管理内存时,在线程中大量分配对象而不用autoreleasepool则可能会造成内存泄露
D. 在使用ARC的项目中不能使用NSZone
(A)

10。 内存管理理解不正确的是 B

A 程序A里有一段内存被成功申请完成之后,内存计数器就从0变为1 (这个过程是alloc);
B 程序B里要使用已存在内存,那么内存计数器从1变为2 (这个过程是retain或者copy);
C 紧接着程序A不需要这个内存了,那么程序A就把这个内存计数器减1 (这个过程是release);
D 当系统发现这个内存计数器变为小于等于0,那么就调用垃圾回收程序把这段内存回收(这个过程是dealloc);

313、谈谈Object-C的内存管理方式及过程?

从一段内存被申请之后,就存在一个变量用于保存这段内存被使用的次数,我们暂时把它称为计数器,当计数器变为0的时候,那么就是释放这段内存的时候,比如说,当在程序A里面一段内存被成功申请完成之后,那么这个计数器就从0变成了1(我们把这个过程叫做alloc),然后程序B也需要使用这个内存,那么计数器就从1变成了2(我们把这个过程叫做retain),紧接着程序A不再需要这段内存了,那么程序A就把这个计数器减1(我们把这个过程叫做release),程序B也不再需要这段内存的时候,那么也把计数器减1(这个过程还是release),当系统(也就是Foundation)发现这个计数器变成了0,那么就会调用内存回收程序把这段内存回收(我们把这个过程叫做dealloc)。

201.内存管理的几条原则是什么?按照默认法则,哪些关键字生成的对象需要手动释放,在和property结合的时候怎样有效的避免内存泄露?

答:当使用new、alloc或copy方法创建一个对象时,该对象引用计数器为1。如果不需要使用该对象,可以向其发送release或autorelease消息,在其使用完毕时被销毁。
如果通过其他方法获取一个对象,则可以假设这个对象引用计数为1,并且被设置为autorelease,不需要对该对象进行清理,如果确实需要retain这个对象,则需要使用完毕后release。
如果retain了某个对象,需要release或autorelease该对象,保持retain方法和release方法使用次数相等。
使用new、alloc、copy关键字生成的对象和retain了的对象需要手动释放。设置为autorelease的对象不需要手动释放,会直接进入自动释放池。

171.什么是ARC?请简述一下ARC的原理。

1)ARC是iOS 5推出的新功能,全称叫ARC(Automatic Reference Counting)。简单地说,就是代码中自动加入了retain/release,原先需要手动添加的用来处理内存管理的引用计数的代码可以自动地由编译器完成了。
2)ARC的规则就是只要对象没有强指针引用,就会被释放掉,换而言之只要还有一个强引用指针变量指向对象,那么这个对象就会存在内存中。弱指针指向的对象,会被自动变成空指针(nil指针),从而不会引发野指针错误。

7.说说assign vs weak,_block vs _weak的区别

assign适用于基本数据类型,weak是适用于NSObject对象,并且是一个弱引用。

assign其实页可以用来修饰对象,那么为什么不用它呢?因为被assign修饰的对象在释放之后,指针的地址还是存在的,也就是说指针并没有被置为nil。如果在后续内存分配中,刚才分到了这块地址,程序就会崩溃掉。而weak修饰的对象在释放之后,指针地址会被置为nil。

_block是用来修饰一个变量,这个变量就可以在block中被修改。

_block:使用_block修饰的变量在block代码块中会被retain(ARC下,MRC下不会retain)

_weak:使用_weak修饰的变量不会在block代码块中被retain

8.请说出下面代码是否有问题,如果有问题请修改?

@autoreleasepool {
    for (int i=0; i<largeNumber; i++) {
        Person *per = [[Person alloc] init];
        [per autorelease];
    }
}
内存管理的原则:如果对一个对象使用了alloc、copy、retain,那么你必须使用相应的release或者autorelease。咋一看,这道题目有alloc,也有autorelease,两者对应起来,应该没问题。但autorelease虽然会使引用计数减一,但是它并不是立即减一,它的本质功能只是把对象放到离他最近的自动释放池里。当自动释放池销毁了,才会向自动释放池中的每一个对象发送release消息。这道题的问题就在autorelease。因为largeNumber是一个很大的数,autorelease又不能使引用计数立即减一,所以在循环结束前会造成内存溢出的问题。

解决方案如下:
@autoreleasepool {
    for (int i=0; i<100000; i++) {
        @autoreleasepool {
            Person *per = [[Person alloc] init];
            [per autorelease];
        }
    }
}
在循环内部再加一个自动释放池,这样就能保证每创建一个对象就能及时释放。

9.请问下面代码是否有问题,如有问题请修改?

@autoreleasepool {
    NSString *str = [[NSString alloc] init];
    [str retain];
    [str retain];
    str = @"jxl";
    [str release];
    [str release];
    [str release];
}
这道题跟第8题一样存在内存泄露问题,1.内存泄露 2.指向常量区的对象不能release。

指针变量str原本指向一块开辟的堆区空间,但是经过重新给str赋值,str的指向发生了变化,由原来指向堆区空间,到指向常量区。常量区的变量根本不需要释放,这就导致了原来开辟的堆区空间没有释放,造成内存泄露。

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

在ARC中,在有可能出现循环引用的时候,往往要通过让其中一端使用weak来解决。比如delegate代理。

自身已经对它进行一次强引用,没有必要再强引用一次,此时也会使用weak,自定义控件属性一般也使用weak。

不同点:
weak此特质表明该属性定义了一种“非拥有关系”。为这种属性设置新值时,设置方法既不保留新值,也不释放旧值。此特性与assign一样,然而在属性所指的对象遭到推毁时,属性值也会清空。而assign的“设置方法”只会执行针对“纯量类型” (scalar type,例如 CGFloat 或 NSlnteger 等)的简单赋值操作。

assign可以用非OC对象,而weak必须用于OC对象。

11.内存管理语义(assign、strong、weak等的区别)

assign:“设置方法” 只会执行针对“纯量”的简单赋值操作。

strong:此特质表明该属性定义了一种“拥有关系”。为这种属性设置新值时,设置方法会先保留新值,并释放旧值,然后再将新值设置上去。

weak:此特质表明该属性定义了一种“非拥有关系”。为这种属性设置新值时,设置方法既不保留新值,也不释放旧值。此特质同assign类似,然而在属性所指的对象遭到推毁时,属性值也会清空。

unsafe_unretained:此特质的语义和assign相同,但是它适用于“对象类型”,该特质表达一种“非拥有关系”,当目标对象遭到推毁时,属性值不会自动清空,这一点与weak有区别。

copy:此特质所表达的所属关系与strong类似。然而设置方法并不保留新值,而是设置方法并不保留新值,而是将其“拷贝”。当属性类型为NSString*时,经常用此特质来保护其封装性,因为传递给设置方法的新值有可能指向一个NSMutableString类的实例。这个类是NSString的子类,表示一种可以修改其值的字符串,此时若是不拷贝字符串,那么设置完属性之后,字符串的值就可能会在对象不知情的情况下遭人更改。所以,这时就要拷贝一份“不可变”的字符串,确保对象中的字符串值不会无意间变动。只要实现属性所用的对象是“可变的”,就应该在设置新属性值时拷贝一份。

1.什么是ARC?

ARC是automatic reference counting自动引用计数,在程序编译时自动加入retain/release。在对象被创建时retain count+1,在对象被release时count-1,当count=0时,销毁对象。程序中加入autoreleasepool对象会由系统自动加上autorelease方法,如果该对象引用计数为0,则销毁。那么ARC是为了解决MRC手动管理内存存在的一些而诞生的。

MRC下内存管理的缺点:
释放一个堆内存时,首先要确定指向这个堆空间的指针都被release了。(避免提前释放)
释放指针指向的堆空间,首先要确定哪些指向同一个堆,这些指针只能释放一次。(避免释放多次,造成内存泄露)
模块化操作时,对象可能被多个模块创建和使用,不能确定最后由谁释放。
多线程操作时,不确定哪个线程最后使用完毕。

虽然ARC给我们编程带来的很多好处,但也可能出现内存泄露。如下面两种情况:
循环参照:A有个属性参照B,B有个属性参照A,如果都是strong参照的话,两个对象都无法释放。
死循环:如果有个ViewController中有无限循环,也会导致即使ViewController对应的view消失了,ViewController也不能释放。

2.block一般用哪个关键字修饰,为什么?

block一般使用copy关键字进行修饰,block使用copy是从MRC遗留下来的“传统”,在MRC中,方法内容的block是在栈区的,使用copy可以把它放到堆区。但在ARC中写不写都行:编译器自动对block进行了copy操作。

3.用@property声明的NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?

答:用@property声明 NSString、NSArray、NSDictionary 经常使用copy关键字,是因为他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary,他们之间可能进行赋值操作,为确保对象中的字符串值不会无意间变动,应该在设置新属性值时拷贝一份。

如果我们使用是strong,那么这个属性就有可能指向一个可变对象,如果这个可变对象在外部被修改了,那么会影响该属性。

copy此特质所表达的所属关系与strong类似。然而设置方法并不保留新值,而是将其“拷贝” (copy)。 当属性类型为NSString时,经常用此特质来保护其封装性,因为传递给设置方法的新值有可能指向一个NSMutableString类的实例。这个类是NSString的子类,表示一种可修改其值的字符串,此时若是不拷贝字符串,那么设置完属性之后,字符串的值就可能会在对象不知情的情况下遭人更改。所以,这时就要拷贝一份“不可变” (immutable)的字符串,确保对象中的字符串值不会无意间变动。只要实现属性所用的对象是“可变的” (mutable),就应该在设置新属性值时拷贝一份。

4.runloop、autorelease pool以及线程之间的关系。

每个线程(包含主线程)都有一个Runloop。对于每一个Runloop,系统会隐式创建一个Autorelease pool,这样所有的release pool会构成一个像callstack一样的一个栈式结构,在每一个Runloop结束时,当前栈顶的Autorelease pool会被销毁,这样这个pool里的每个Object会被release。

5.@property 的本质是什么?ivar、getter、setter 是如何生成并添加到这个类中的。

“属性”(property)有两大概念:ivar(实例变量)、存取方法(access method=getter),即@property = ivar + getter + setter。

例如下面的这个类:
    @interface WBTextView :UITextView
    @property (nonatomic,copy)NSString *placehold;
    @property (nonatomic,copy)UIColor *placeholdColor;
    @end
类完成属性的定以后,编译器会自动编写访问这些属性的方法(自动合成autosynthesis),上述代码写出来的类等效与下面的代码:
    @interface WBTextView :UITextView
    - (NSString *)placehold;
    -(void)setPlacehold:(NSString *)placehold;
    -(UIColor *)placeholdColor;
    -(void)setPlaceholdColor:(UIColor *)placeholdColor;
    @end
详细介绍见:http://blog.csdn.net/jasonjwl/article/details/49427377

6.分别写一个setter方法用于完成

@property (nonatomic,retain)NSString *name
和
@property (nonatomic,copy) NSString *name

retain属性的setter方法是保留新值并释放旧值,然后更新实例变量,令其指向新值。顺序很重要。假如还未保留新值就先把旧值释放了,而且两个值又指向同一个对象,先执行的release操作就可能导致系统将此对象永久回收。
// retain
-(void)setName:(NSString *)name {
    [name retain];
    [_name release];
    _name = name;
}
// copy
-(void)setName:(NSString *)name
{
    [_name release];
    _name = [name copy];
}

1、浅谈iOS内存管理机制

iOS内存管理机制的原理是引用计数,引用计数简单来说就是统计一块内存的所有权,当这块内存被创建出来的时候,它的引用计数从0增加到1,表示有一个对象或指针持有这块内存,拥有这块内存的所有权,如果这时候有另外一个对象或指针指向这块内存,那么为了表示这个后来的对象或指针对这块内存的所有权,引用计数加1变为2,之后若有一个对象或指针不再指向这块内存时,引用计数减1,表示这个对象或指针不再拥有这块内存的所有权,当一块内存的引用计数变为0,表示没有任何对象或指针持有这块内存,系统便会立刻释放掉这块内存。

其中在开发时引用计数又分为ARC(自动内存管理)和MRC(手动内存管理)。ARC的本质其实就是MRC,只不过是系统帮助开发者管理已创建的对象或内存空间,自动在系统认为合适的时间和地点释放掉已经失去作用的内存空间,原理是一样的。虽然ARC操作起来很方便,不但减少了代码量,而且降低了内存出错的概率,但因为ARC不一定会及时释放,所以程序有时候可能会占用内存较大。而MRC若做得好,通过手动管理,及时释放掉不需要的内存空间,便可保证程序长时间运行在良好状态上。

在MRC中会引起引用计数变化的关键字有:alloc,retain,copy,release,autorelease。(strong关键字只用于ARC,作用等同于retain)

alloc:
    当一个类的对象创建,需要开辟内存空间的时候,会使用alloc,alloc是一个类方法,只能用类调用,它的作用是开辟一块新的内存空间,并使这块内存的引用计数从0增加到1,注意,是新的内存空间,每次用类alloc出来的都是一块新的内存空间,与上一次alloc出来的内存空间没有必然联系,而且上一次alloc出来的内存空间仍然存在,不会被释放。

retain:
    retain是一个实例方法,只能由对象调用,它的作用是使这个对象的内存空间的引用计数加1,并不会新开辟一块内存空间,通常于赋值是调用。
    如:对象2=[对象1 retain];
        表示对象2同样拥有这块内存的所有权。若只是简单地赋值;
    如:对象2=对象1;
        那么当对象1的内存空间被释放的时候,对象2便会成为野指针,再对对象2进行操作便会造成内存错误。

copy:
    copy同样是一个实例方法,只能由对象调用,返回一个新的对象,它的作用是复制一个对象到一块新的内存空间上,旧内存空间的引用计数不会变化,新的内存空间的引用计数从0增加到1,也就是说,虽然内容一样,但实质上是两块内存,相当于克隆,一个变成两个。
    其中copy又分为浅拷贝、深拷贝和真正的深拷贝:
        浅拷贝只是拷贝地址与retain等同;
        深拷贝是拷贝内容,会新开辟新内存,与retain不一样;
        真正的深拷贝是对于容器类来说的,如数组类、字典类和集合类(包括可变和不可变)。
        假设有一个数组类对象,普通的深拷贝会开辟一块新内存存放这个对象,但这个数组对象里面的各个元素的地址却没有改变也就是说数组元素只是进行了retain或者浅拷贝而已,并没有创建新的内存空间。
        而真正的深拷贝,不但数组对象本身进行了深拷贝,连数组元素都进行了深拷贝,即为各个数组元素开辟了新的内存空间。

release:
    release是一个实例方法,同样只能由对象调用,它的作用是使对象的内存空间的引用计数减1,若引用计数变为0则系统会立刻释放掉这块内存。如果引用计数为0的基础上再调用release,便会造成过度释放,使内存崩溃;

autorelease:
    autorelease是一个实例方法,同样只能由对象调用,它的作用于release类似,但不是立刻减1,相当于一个延迟的release,通常用于方法返回值的释放,如便利构造器。autorelease会在程序走出自动释放池时执行,通常系统会自动生成自动释放池(即使是MRC下),也可以自己设定自动释放池,如:
    @autoreleasepool{
        obj= [[NSObject alloc]init];
        [obj autorelease];
    }
    当程序走出“}”时obj的引用计数就会减1.

除了以上所述的关键字,还有一些方法会引起引用计数的变化,如UI中父视图添加、移除子视图,导航控制器或视图控制器推出新的视图控制器以及返回,容器类(数组、字典和集合)添加和移除元素。

当子视图添加到父视图上时,子视图的引用计数加1,移除时引用计数减1,若父视图引用计数变为0内存被释放,其所有的子视图都会被release一次,即引用计数减1,原则上只有这三种情况子视图的引用计数会发生变化,其他如父视图引用计数的加减都不会影响到子视图。

容器类的情况与视图类似,添加元素,该元素引用计数加1,移除元素,该元素引用计数减1,容器引用计数变为0所占用内存被释放,容器所有元素release,引用计数减1,其他情况下容器本身的引用计数变化不会影响到容器内元素的引用计数变化。

导航控制器或视图控制器推出新的视图控制器会使被推出的视图控制器的引用计数加1,该视图控制器返回的时候引用计数减1,具体方法如下:
    导航控制器推出视图控制器调用方法:
        - (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated;
    返回时同样用导航控制器调用方法:
        - (UIViewController *)popViewControllerAnimated:(BOOL)animated;
    视图控制器推出视图控制器调用方法:
        - (void)presentViewController:(UIViewController *)viewControllerToPresent animated: (BOOL)flag completion:(void (^)(void))completion
    返回时被推出的视图控制器调用方法:
        - (void)dismissViewControllerAnimated: (BOOL)flag completion: (void (^)(void))completion

应注意:当一个对象的引用计数变为0占用内存被释放时,会调用 - (void)dealloc方法,所以如果在MRC下自定义类,必须在该方法里将该类中属性关键字设置为retain或copy的属性release一次,以免造成内存泄露,重写方法不要忘记在第一行添加[super dealloc];。

推荐阅读更多精彩内容

  • 内存管理 简述OC中内存管理机制。与retain配对使用的方法是dealloc还是release,为什么?需要与a...
    丶逐渐阅读 1,099评论 1 15
  • iOS内存管理 概述 什么是内存管理 应用程序内存管理是在程序运行时分配内存(比如创建一个对象,会增加内存占用)与...
    蚊香酱阅读 4,048评论 8 115
  • 内存管理概述 内存管理内存的作用:存储数据. 如何将数据存储到内存之中.声明1个变量.然后将数据存储进去. 当数据...
    指尖书法阅读 523评论 2 7
  • 29.理解引用计数 Objective-C语言使用引用计数来管理内存,也就是说,每个对象都有个可以递增或递减的计数...
    Code_Ninja阅读 533评论 0 2
  • 为什么进行内存管理? 由于移动设备的内存极其有限,所以每个APP所占的内存也是有限制的,当app所占用的内存较多时...
    天天想念阅读 308评论 1 7