oc中为什么要学手动oc内存管理机制,有什么用

OC中内存管理机制应该就是引用计数机制,retainCount为0时释放该内存。
readwrite此标记说明属性会被当成读写的,这也是默认属性。
readonly此标记说明属性只可以读,也就是不能设置,可以获取。
assign不会使引用计数加1,也就是直接赋值。
retain会使引用计数加1。
copy建立一个索引计数为1的对象,在赋值时使用传入值的一份拷贝。
nonatomic:非原子性访问,多线程并发访问会提高性能。
weak:打开ARC时才会使用,相当于assign,可以把对应的指针变量置为nil。

2.ARC下dealloc方法存在的意义在于什么地方?举例说明一下具体的使用场景。
答:其实在MRC中dealloc方法存在的主要意义是为了释放自身的实例变量,移除观察者,停止timer,移除通知,代理置空等。ARC下,系统会帮助我们释放该对象所包含的实例变量,但是有些对象还是需要们自己去释放的(比如Core Foundation框架下的一些对象),另外通知中观察者的移除,代理置空,停止timer等

4.你如何看待iOS中的拷贝?
答:在我看来,日常生活中,当我们用到”拷贝”这个词语的时候,不管怎样都会产生两份。一份是原来的,一份是新拷贝出来的。但是到目前为止,在iOS中我看到了三种拷贝方式:
(1)伪拷贝:伪拷贝,顾名思义,就是假拷贝,没有拷贝出新的对象。这一点对于NSString这种类簇来说比较常见,NSString本身是不可变字符串,内容不可能被修改,因此我们也没有拷贝的必要,因为拷贝的目的是防止原件被修改,所以才拷贝出来一份,伪拷贝实际上是对象引用计数加了1(相当于retain或者strong的功效)。
(2)浅拷贝:浅拷贝就是确实拷贝出来一份和原来内容一样的新对象。但是对于对象自带的属性是伪拷贝,两个对象的属性指向同一个内存。
(3)深拷贝:深拷贝就是不仅仅拷贝出一份新对象,而且对象的属性也拷贝了一份。
总的来说,如果在开发的过程中需要实现拷贝,那么需要接受NSCopying协议。实现copyWithZone:方法。浅拷贝、深拷贝的区别在于copyWithZone:方法的实现不同。

和copy用于对象类型,二者又有区别,copy是用于一个对象有可能被修改,但不想修改原件,所以拷贝一份出来(新拷贝的对象引用计数为1),这样新拷贝的修改了,原件不会变,原件修改了,新拷贝的不会变。而retain呢,对象引用计数+1,对象只有一个,但引用计数为2,通过任何一个引用修改对象内容,另外一个引用使用的时候,用的就是修改之后的内容。
1、以下代码有什么问题吗?如果没有问题的话,obj、obj2的引用计数分别是多少?如果有问题的话存在什么问题?
答:上面的代码是存在问题的。当执行完第一行代码时,因为执行了alloc,obj的引用计数为1。第二行代码执行完之后,obj2只是和obj指向了同一块内存。第三行代码是执行了hello方法。第四行代码执行release消息之后,obj的引用计数减一,这时retainCount变为0.系统自动调用dealloc方法,对象被销毁。第五行代码执行时,obj2执行的内存已经被系统回收了,但还是调用了hello方法,出现了问题(野指针)。第六行执行时,obj2所指向的内存已经不存在,再次调用release消息,出现过度释放的问题,而且obj2已经变成野指针了。
2、在实际开发的过程中,什么情况下需要创建自动释放池?下面代码中有没有什么问题?

答:其实自动释放池存在的意义是为了延迟释放一些对象,延迟向对象发送release消息。在实际的开发中呢,有两种情况是需要手动创建自动释放池的。第一个就是在多线程中,因为子线程中可能会使用便利构造器等方法来创建对象,那么这些对象的释放只能放在自动释放池中,主线程其实已经添加过自动释放池,在main函数里面。第二个就是如果一段代码里面(比如for循环)大量使用便利构造器创建对象,也需要手动添加自动释放池。
上述代码其实是存在问题的。当执行完第一行代码时,p1的引用计数是1.第二行是创建了一个autoreleasepool。第三行代码向p1发送了autorelease消息,延迟release,即在出池的时候,把p1释放掉。第四行代码又创建了一个autoreleasepool。第五行代码再次向p1发送了autorelease消息。当代码执行到第六行的“}”时,第二个自动释放池结束,这时p1引用技术减1,p1所指向的内存(对象)的retainCount由1变为0,内存被系统回收。代码执行到第七行时,最外层的自动释放池结束,再次向p1发送release消息,出现过度释放。
}//此处,自动释放池2结束,p1引用计数-1
}//此处,自动释放池1结束,但是向已经被释放的对象p1发送了消息

4.父类实现深拷贝时,子类如何实现深拷贝 ?父类没有实现深拷贝时,子类如何实现深度拷贝?
答:父类实现深拷贝之后,子类在重写的copyWithZone方法,先调用父类的copyWithZone方法,之后实现自己属性的拷贝。
如果父类没有实现深拷贝,子类除了需要对自己的属性进行拷贝,还要对父类的属性进行拷贝。

在你打开ARC时,你是不能使用retainrelease autorelease 操作的,原先需要手动添加的用来处理内存管理的引用计数的代码可以自动地由编译器完成了,但是你需要在对象属性上使用weak 和strong, 其中strong就相当于retain属性,而weak相当于assign,基础类型还是使用assign。
strong 用来修饰强引用的属性;对应原来的retain。
该属性值对应 strong 关键字,即该属性所声明的变量将成为对象的持有者。
weak 用来修饰弱引用的属性;对应原来的assign。
但是不同的是当对象被释放以后,对象自动赋值为nil;并且,delegate 和 Outlet 苹果推荐用 weak 属性来声明。同时,如上一回介绍的 iOS 5 之前的版本是没有
weak 关键字的,所以 weak 属性是不能使用的。这种情况我们使用 unsafe_unretained。

IOS的ARC会导致的内存泄露问题和解决方案
iOS提供了ARC功能,很大程度上简化了内存管理的代码。
但使用ARC并不代表了不会发生内存泄露,使用不当照样会发生内存泄露。
下面列举两种ARC导致内存泄露的情况。
A有个属性参照B,B有个属性参照A,如果都是strong参照的话,两个对象都无法释放。
这种问题常发生于把delegate声明为strong属性了。
这种问题常发生于animation处理。

5、内存中的栈和堆的区别是什么?哪些数据在栈上哪些数据在堆上?
(1)管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生 memory leak。
(2)申请大小:能从栈获得的空间较小,堆是向高地址扩展的数据结构,是不连续的内存区域。堆的大小受限于计算机系统中 有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。
(3)碎片问题:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。 对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块 从栈中间弹出
(4)分配方式:堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成 的,比如局部变量的分配。动态分配由 alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器 进行释放,无需我们手工实现。
(5)分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈 都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的。
在函数体中定义的变量通常是在栈上,用malloc, calloc, realloc等分配内存的函数分配得到的就是在堆上。

12.Objective-C如何对内存管理的,说说你的看法和解决方法?
  Objective-C的内存管理主要有三种方式ARC(自动内存计数)、手动内存计数、内存池。
Collection)自动内存计数:这种方式和java类似,在你的程序的执行过程中。始终有一个高人在背后准确地帮你收拾垃圾,你不用考虑它什么时候开始工作,怎样工作。你只需要明白,我申请了一段内存空间,当我不再使用从而这段内存成为垃圾的时候,我就彻底的把它忘记掉,反正那个高人会帮我收拾垃圾。遗憾的是,那个高人需要消耗一定的资源,在携带设备里面,资源是紧俏商品所以iPhone不支持这个功能。所以“Garbage Collection”不是本入门指南的范围,对“Garbage Collection”内部机制感兴趣的同学可以参考一些其他的资料,不过说老实话“Garbage Collection”不大适合适初学者研究。
  解决: 通过alloc – initial方式创建的, 创建后引用计数+1, 此后每retain一次引用计数+1, 那么在程序中做相应次数的release就好了.
Counted)手动内存计数:就是说,从一段内存被申请之后,就存在一个变量用于保存这段内存被使用的次数,我们暂时把它称为计数器,当计数器变为0的时候,那么就是释放这段内存的时候。比如说,当在程序A里面一段内存被成功申请完成之后,那么这个计数器就从0变成1(我们把这个过程叫做alloc),然后程序B也需要使用这个内存,那么计数器就从1变成了2(我们把这个过程叫做retain)。紧接着程序A不再需要这段内存了,那么程序A就把这个计数器减1(我们把这个过程叫做release);程序B也不再需要这段内存的时候,那么也把计数器减1(这个过程还是release)。当系统(也就是Foundation)发现这个计数器变成了0,那么就会调用内存回收程序把这段内存回收(我们把这个过程叫做dealloc)。顺便提一句,如果没有Foundation,那么维护计数器,释放内存等等工作需要你手工来完成。
  3. (NSAutoRealeasePool)内存池:可以通过创建和释放内存池控制内存申请和回收的时机.
其中的对象都会被释放.

8)浅复制和深复制的区别
浅层复制:只复制指向对象的指针,而不复制引用对象本身。
深层复制:复制引用对象本身。
意思就是说我有个A对象,复制一份后得到A_copy对象后,对于浅复制来说,A和A_copy指向的是同一 个内存资源,复制的只不过是是一个指针,对象本身资源 还是只有一份,那如果我们对A_copy执行了修改操作,那么发现A引用的对象同样被修改,这其实违背 了我们复制拷贝的一个思想。深复制就好理解了,内存中存在了

用网上一哥们通俗的话将就是:
浅复制好比你和你的影子,你完蛋,你的影子也完蛋
深复制好比你和你的克隆人,你完蛋,你的克隆人还活着。

浅拷贝只是复制对象本身,对象的属性和包含的对象不做复制。
深拷贝则对对象本身复制,同时对对象的属性也进行复制。
深浅拷贝的本质区别是对象或者对象属性的内存地址是否一样,一样则为浅拷贝,不一样则为深拷贝。
Foundation框架支持复制的类,默认是浅拷贝。其中对Foundation中不可变的对象进行copy时作用相当于retain。
而如果是mutablecopy时,无论对象是否可变,副本是可变的,并且实现了真正意义上的copy。如果对可变对象进行copy,
副本对象是不可变的,同样是真正意义上的copy。

(1)管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生 memory leak。
(2)申请大小:能从栈获得的空间较小,堆是向高地址扩展的数据结构,是不连续的内存区域。堆的大小受限于计算机系统中 有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。
(3)碎片问题:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。 对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块 从栈中间弹出
(4)分配方式:堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成 的,比如局部变量的分配。动态分配由 alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器 进行释放,无需我们手工实现。
(5)分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈 都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的。

众所周知,我们进行iOS开发,在Xcode调试程序时,分为两种方式,Debug和Release,在Target的Setting中相信大家应该看到很多选项都分为Debug和Release,方便我们分别设置,满足调试和发布的不同需求。
Release是发行版本,比Debug版本有一些优化,文件比Debug文件小 Debug是调试版本,Debug和Release调用两个不同的底层库。通俗点讲,我们开发者自己内部真机或模拟器调试时,使用Debug模式就好,等到想要发布时,也就是说需要大众客户使用时,需要build Release版本,具体区别如下:
一、Debug是调试版本,包括的程序信息更多
二、只有Debug版的程序才能设置断点、单步执行、使用TRACE/ASSERT等调试输出语句
三、Release不包含任何调试信息,所以体积小、运行速度快

2、分析内存泄露不能把所有的内存泄露查出来,有的内存泄露是在运行时,用户操作时才产生的。那就需要用到Instruments了。
Leak工具可以很容易的统计所有内存泄漏的点,而且还可以显示在哪个文件,哪行代码有内存泄漏,这样定位问题比较容易,也比较方便;但是Leak在统计内存泄漏的时候会把autorelease方式的内存也统计进来; 所以我们在查找内存泄漏情况的时候,可以autorelease的情况忽略掉;

8.Objective-C如何对内存管理的,说说你的看法和解决方法?
Objective-C的内存管理主要有三种方式ARC(自动内存计数)、手动内存计数、内存池。
①.自动内存计数:这种方式和java类似,在你的程序的执行过程中。始终有一个高人在背后准确地帮你收拾垃圾,你不用考虑它什么时候开始工作,怎样工作。你只需要明白,我申请了一段内存空间,当我不再使用从而这段内存成为垃圾的时候,我就彻底的把它忘记掉,反正那个高人会帮我收拾垃圾。遗憾的是,那个高人需要消耗一定的资源,在携带的移动设备里面,资源是紧俏商品所以iPhone不支持这个功能。
解决: 通过alloc – init方式创建的, 创建后引用计数+1, 此后每retain一次引用计数+1, 那么在程序中做相应次数的release就好了.
Counted)手动内存计数:就是说,从一段内存被申请之后,就存在一个变量用于保存这段内存被使用的次数,我们暂时把它称为计数器,当计数器变为0的时候,那么就是释放这段内存的时候。比如说,当在程序A里面一段内存被成功申请完成之后,那么这个计数器就从0变成1(我们把这个过程叫做alloc),然后程序B也需要使用这个内存,那么计数器就从1变成了2(我们把这个过程叫做retain)。紧接着程序A不再需要这段内存了,那么程序A就把这个计数器减1(我们把这个过程叫做release);程序B也不再需要这段内存的时候,那么也把计数器减1(这个过程还是release)。当系统(也就是Foundation)发现这个计数器变成了0,那么就会调用内存回收程序把这段内存回收(我们把这个过程叫做dealloc)。顺便提一句,如果没有Foundation,那么维护计数器,释放内存等等工作需要你手工来完成。
③. (NSAutoRealeasePool)内存池:可以通过创建和释放内存池控制内存申请和回收的时机.

    ①. atomic提供多线程安全。是防止在写未完成的时候被另外一个线程读取,造成数据错误
    ②. non-atomic:在自己管理内存的环境中,解析的访问器保留并自动释放返回的值,如果指定了 nonatomic ,那么访问器只是简单地返回这个值。
  1. 浅复制和深复制的区别?
    答案:浅层复制:只复制指向对象的指针,而不复制引用对象本身。
    深层复制:复制引用对象本身。
    意思就是说我有个A对象,复制一份后得到A_copy对象后,对于浅复制来说,A和A_copy指向的是同一个内存资源,复制的只不过是是一个指针,对象本身资源
    还是只有一份,那如果我们对A_copy执行了修改操作,那么发现A引用的对象同样被修改,这其实违背了我们复制拷贝的一个思想。深复制就好理解了,内存中存在了
    用网上一哥们通俗的话将就是:
    浅复制好比你和你的影子,你完蛋,你的影子也完蛋
    深复制好比你和你的克隆人,你完蛋,你的克隆人还活着。

43.内存中的栈和堆的区别是什么?
管理方式:对于栈来讲,是由编译器自动管理的,无需我们手动控制,对于堆来讲,释放工作有程序猿控制,这样就容易产生memory Leak

申请大小: 栈是向低地址扩展的数据结构,是一块连续的内存区域。这句话的意思是栈顶上的地址和栈的最大容量是系统预先规定好的,在Windows下,栈的大小是2M(也有的说1M,总之是编译器确定的一个常数),如果申请的空间超过了栈的剩余空间时候,就overflow。因此,能获得栈的空间较小。
堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大笑受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大

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

堆都是动态分配的,没有静态分配的堆。栈有两种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配是有alloc函数进行分配的,但是栈的动态分配和堆是不同的,他的动态分配由编译器进行释放,无需我们手工实现。
栈是机器系统提供的数据结构,计算机会在底层堆栈提供支持,分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,他的机制是很复杂的。

3、iOS有没有垃圾回收?
iOS没有垃圾回收机制。只有系统在应用程序退出的时候会在适当的时候回收所有的东西。ios5以后有arc,可以自动添加管理内存的代码,但与垃圾回收不是一个东西。一个需要自己管理,一个不需要。
其实也可以垃圾回收,但是这样的话对性能有影响,而且手机不像电脑,对性能要求严格,所以苹果果断没有用垃圾回收。把内存的烦恼留程序员,但为了照顾程序员,又发明了arc。

  1. 内存池的概念详细解释一下

    内存池:内存池就是用来存放当前创建的多有对象的空间位置,当我们在创建一个实例对象以后系统会自动的把内容放到内存池中,当我们在使用这些对象的时候就会在内存中自动的去执行retain,copy,release操作,当实例对象的引用计数为0时就会自动的执行dealloc方法来销毁实例对象.
    在内存池中也会有runloop作用是来监察每个实例对象的引用计数.

(1). 用ARC管理内存
ARC(Automatic ReferenceCounting, 自动引用计数)和iOS5一起发布,它避免了最常见的也就是经常是由于我们忘记释放内存所造成的内存泄露。它自动为你管理retain和release的过程,除了帮你避免内存泄露,ARC还可以帮你提高性能,它能保证释放掉不再需要的对象的内存。
(3).尽量把views设置为透明
如果你有透明的Views你应该设置它们的opaque属性为YES。
原因是这会使系统用一个最优的方式渲染这些views。如果设为YES,渲染系统就认为这个view是完全不透明的,这使得渲染系统优化一些渲染过程和提高性能。如果设置为NO,渲染系统正常地和其它内容组成这个View。默认值是YES。
(4).避免过于庞大的XIB
当你加载一个XIB的时候所有内容都被放在了内存里,包括任何图片。如果有一个不会即刻用到的view,你这就是在浪费宝贵的内存资源了。
(5).不要阻塞主线程
永远不要使主线程承担过多。因为UIKit在主线程上做所有工作,渲染,管理触摸反应,回应输入等都需要在它上面完成。
一直使用主线程的风险就是如果你的代码真的block了主线程,你的app会失去反应。
大部分阻碍主进程的情形是你的app在做一些牵涉到读写外部资源的I/O操作,比如存储或者网络。
如果要在`UIImageView`中显示一个来自bundle的图片,你应保证图片的大小和UIImageView的大小相同。在运行中缩放图片是很耗费资源的,特别是`UIImageView`嵌套在`UIScrollView`中的情况下。
如果图片是从远端服务加载的你不能控制图片大小,比如在下载前调整到合适大小的话,你可以在下载完成后,最好是用background thread,缩放一次,然后在UIImageView中使用缩放后的图片。
学会选择对业务场景最合适的类或者对象是写出能效高的代码的基础。当处理collections时这句话尤其正确。
· Sets: 无序的一组值。用值来查找很快,插入/删除很快。
大量app依赖于远端资源和第三方API,你可能会开发一个需要从远端下载XML, JSON, HTML或者其它格式的app。
问题是我们的目标是移动设备,因此你就不能指望网络状况有多好。一个用户现在还在edge网络,下一分钟可能就切换到了3G。不论什么场景,你肯定不想让你的用户等太长时间。

减小文档的一个方式就是在服务端和你的app中打开gzip。这对于文字这种能有更高压缩率的数据来说会有更显著的效用。

更多的view意味着更多的渲染,也就是更多的CPU和内存消耗,对于那种嵌套了很多view在 UIScrollView里边的app更是如此。 这里我们用到的技巧就是模仿`UITableView``UICollectionView`的操作:不要一次创建所有的subview,而是当需要时才创建,当它们完成了使命,把他们放进一个可重用的队列中。 这样的话你就只需要在滚动发生时创建你的views,避免了不划算的内存分配。

创建views的能效问题也适用于你app的其它方面。想象一下一个用户点击一个按钮的时候需要呈现 一个view的场景。有两种实现方法:

1. 创建并隐藏这个view当这个screen加载的时候,当需要时显示它;
2. 当需要时才创建并展示。
每个方案都有其优缺点。用第一种方案的话因为你需要一开始就创建一个view并保持它直到不再使用,这就会更加消耗内存。然而这也会使你的app操作更敏感因为当用户点击按钮的时候它只需要改变一下这个view的可见性。
第二种方案则相反-消耗更少内存,但是会在点击按钮的时候比第一种稍显卡顿。
一个极好的原则就是,缓存所需要的,也就是那些不大可能改变但是需要经常读取的东西。

我们能缓存些什么呢?一些选项是,远端服务器的响应,图片,甚至计算结果,比如UITableView的行高。
NSURLConnection默认会缓存资源在内存或者存储中根据它所加载的HTTP Headers。你甚至可以手动创建一个NSURLRequest然后使它只加载缓存的值。

11).权衡渲染方法
在iOS中可以有很多方法做出漂亮的按钮。你可以用整幅的图片,可调大小的图片,uozhe可以用CALayer, CoreGraphics甚至OpenGL来画它们。
当然每个不同的解决方法都有不同的复杂程度和相应的性能。
简单来说,就是用事先渲染好的图片更快一些,因为如此一来iOS就免去了创建一个图片再画东西上去然后显示在屏幕上的程序。问题是你需要把所有你需要用到的图片放到app的bundle里面,这样就增加了体积–这就是使用可变大小的图片更好的地方了:你可以省去一些不必要的空间,也不需要再为不同的元素(比如按钮)来做不同的图。
然而,使用图片也意味着你失去了使用代码调整图片的机动性,你需要一遍又一遍不断地重做他们,这样就很浪费时间了,而且你如果要做一个动画效果,虽然每幅图只是一些细节的变化你就需要很多的图片造成bundle大小的不断增大。
总得来说,你需要权衡一下利弊,到底是要性能能还是要bundle保持合适的大小。
(12).处理内存警告
一旦系统内存过低,iOS会通知所有运行中app。如果你的app收到了内存警告,它就需要尽可能释 放更多的内存。最佳方式是移除对缓存,图片object和其他一些可以重创建的objects的strong references.
幸运的是,UIKit提供了几种收集低内存警告的方法:
一旦收到这类通知,你就需要释放任何不必要的内存使用。
例如,UIViewController的默认行为是移除一些不可见的view,它的一些子类则可以补充这个方法,删掉一些额外的数据结构。一个有图片缓存的app可以移除不在屏幕上显示的图片。
(13).重用大开销对象
一些objects的初始化很慢,比如NSDateFormatterNSCalendar。然而,你又不可避免地需要使用它们,比如从JSON或者XML中解析数据。
想要避免使用这个对象的瓶颈你就需要重用他们,可以通过添加属性到你的class里或者创建静态变量来实现。
注意如果你要选择第二种方法,对象会在你的app运行时一直存在于内存中,和单例(singleton)很相似。
还需要注意的是,其实设置一个NSDateFormatter的速度差不多是和创建新的一样慢的!所以如果你的app需要经常进行日期格式处理的话,你会从这个方法中得到不小的性能提升。
(14). 减少使用Web特性
UIWebView很有用,用它来展示网页内容或者创建UIKit很难做到的动画效果是很简单的一件事。
所以想要更高的性能你就要调整下你的HTML了。第一件要做的事就是尽可能移除不必要的 javascript,避免使用过大的框架。能只用原生js就更好了。
另外,尽可能异步加载例如用户行为统计script这种不影响页面表达的javascript。
最后,永远要注意你使用的图片,保证图片的符合你使用的大小。使用Sprite sheet提高加载速度和节约内存。
Table view需要有很好的滚动性能,不然用户会在滚动过程中发现动画的瑕疵。
为了保证table view平滑滚动,确保你采取了以下的措施:
· 避免渐变,图片缩放,后台选人
· 如果cell内现实的内容来自web,使用异步加载,缓存请求结果
· 使用正确的数据结构来存储数据
假如你创建很多临时对象,你会发现内存一直在减少直到这些对象被release的时候。这是因为只有当UIKit用光了autorelease pool的时候memory才会被释放。好消息是你可以在你自己的 @autoreleasepool里创建临时的对象来避免这个行为:

这段代码在每次遍历后释放所有autorelease对象

17). 选择是否缓存图片
既然有两种类似的方法来实现相同的目的,那么他们之间的差别是什么呢?
`imageNamed`的优点是当加载时会缓存图片。`imageNamed`的文档中这么说:这个方法用一个指定的名字在系统缓存中查找并返回一个图片对象如果它存在的话。如果缓存中没有找到相应 的图片,这个方法从指定的文档中加载然后缓存并返回这个对象。
如果你要加载一个大图片而且是一次性使用,那么就没必要缓存这个图片,用 `imageWithContentsOfFile`足矣,这样不会浪费内存来缓存它。
 然而,在图片反复重用的情况下`imageNamed`是一个好得多的选择

执行过程中tracker的引用计数分别为:

arc的程序出现内存泄露怎办
用arc和非arc混编,非arc的类在arc里实例化并且使用,在arc里居然出现内存泄露,而且应为是arc,所以无法使用release,autorelease和dealloc去管理内存。正常情况下应该是不会出现这种情况的,某一个类若是ARC,则在这个类里面都应该遵循ARC的用法,而无需关心用到的类是否是ARC的,同样,在非ARC类里面,就需要遵循内存管理原则。

用ARC,只是编译器帮你管理了何时去release,retain,不用ARC就需要你自己去管理,说到底只是谁去管理的问题,所以你再好好看看,可能问题与ARC无关。
如果实在找不到问题,建议你找到泄露的那个对象,将其赋值为nil,因为ARC里面,一旦对象没有指针指向,就会马上被释放。

IOS的ARC会导致的内存泄露问题和解决方案
iOS提供了ARC功能,很大程度上简化了内存管理的代码。
但使用ARC并不代表了不会发生内存泄露,使用不当照样会发生内存泄露。
下面列举两种ARC导致内存泄露的情况。
A有个属性参照B,B有个属性参照A,如果都是strong参照的话,两个对象都无法释放。
这种问题常发生于把delegate声明为strong属性了。
这种问题常发生于animation处理。

众所周知,我们进行iOS开发,在Xcode调试程序时,分为两种方式,Debug和Release,在Target的Setting中相信大家应该看到很多选项都分为Debug和Release,方便我们分别设置,满足调试和发布的不同需求。
Release是发行版本,比Debug版本有一些优化,文件比Debug文件小 Debug是调试版本,Debug和Release调用两个不同的底层库。通俗点讲,我们开发者自己内部真机或模拟器调试时,使用Debug模式就好,等到想要发布时,也就是说需要大众客户使用时,需要build Release版本,具体区别如下:
一、Debug是调试版本,包括的程序信息更多
二、只有Debug版的程序才能设置断点、单步执行、使用TRACE/ASSERT等调试输出语句
三、Release不包含任何调试信息,所以体积小、运行速度快

2、分析内存泄露不能把所有的内存泄露查出来,有的内存泄露是在运行时,用户操作时才产生的。那就需要用到Instruments了。
Leak工具可以很容易的统计所有内存泄漏的点,而且还可以显示在哪个文件,哪行代码有内存泄漏,这样定位问题比较容易,也比较方便;但是Leak在统计内存泄漏的时候会把autorelease方式的内存也统计进来; 所以我们在查找内存泄漏情况的时候,可以autorelease的情况忽略掉;

5.调用一个类的静态方法需不需要release?

调用一个类的静态方法不需要release,其实静态成员方法也是有对象的,叫做类对象,但这个是在第一次访问类成员时触发系统将其加载到内存的,而且该类对象只在程序立闭时,才会释放,并不由程序本身控制。

关于静态方法与实例方法

程序运行过程中要创建大量的对象,OC中对象是存储在堆中的,系统不会自动释放堆中的内存。如果一个对象创建并使用后没有得到及时释放,那么就会占用大量的内存。

OC中基本类型是由系统进行管理,放在栈上

在Xcode4.2之后的版本中?引入了ARC,程序编译时,Xcode可以自动为你的代码添加内存释放代码,如果编写手动释放代码Xcode会报错,因此如果使用的Xcode4.2之后的版本,必须手动关闭ARC,这样才有助于我们理解OC的内存管理机制。


在C#中都有GC在自动管理内存,但是在OC中没有垃圾回收机制,那么OC中内存又是如何管理呢?其实在OC中内存的管理是依赖对象引用计数器(reference counting)来进行的。

OC中每个对象都有一个与之对应的整数,叫“引用计数器”,当一个对象在创建之后它的引用计数器值加1,当调用这个对象的alloc、retain、new、copy方法之后引用计数器值自动在原来的基础上加1,当调用这个对象的release方法之后它的引用计数器值减1,如果一个对象的引用计数器值为0,则系统会自动调用这个对象的dealloc方法来销毁这个对象。

下面通过代码看一下引用计数器是如何工作的。

/*最后一定要调用父类的dealloc方法; 目的:一是父类可能有其他引用对象需要释放;二是当前对象真正的释放操作是在super的dealloc中完成的; //如果不设置personTest=nil,则personTest就是一个野指针,它指向的内存不属于这个程序,非常危险 //如果设置了personTest=nil,此时personTest已经是空指针了,则oc中给空指针发送消息是不会报错的

在上述代码中,可以通过dealloc方法来查看是否一个对象已经被回收,如果没有回收,则有可能造成内存泄漏。
如果一个对象被释放后,那么最后引用它的变量需要手动设置为nil,否则可能造成野指针错误。

注意:OC中给空对象发送消息是不会引起错误

在OC中存在着一种内存自动释放机制叫做自动释放池(或自动引用计数),但是与C#不同的是,这仅仅是一种半自动的机制,有些操作还是需要进行手动设置。

自动内存释放使用@autoreleasepool关键字声明一个代码块,如果一个对象在初始化时调用了autorelease方法,那么当代码块执行完之后,在块中调用过autorelease方法的对象都会自动调用一次release方法。

下面通过代码来了解一下自动释放池。

//OC类库中的类方法一般都不需要手动释放,内部已经调用了autorelease方法; //由于autorelease是延迟释放(延迟到自动释放池销毁), //所以这里仍然可以使用person1对象 //内部已经调用了autorelease,所以不需要手动释放 //另外由于内存管理原则,在外部不使用alloc、new、copy操作, //就不需要调用release或autorelease,所以这个操作是放到类方法内部进行完成

下面我们对自动内存释放稍作总结:

  1. autorelease方法不会改变对象的引用计数器,只是将这个对象放到自动释放池中;
  2. 自动释放池实质是当自动释放池销毁之后,调用release方法,但是不一定能够销毁对象,例如:当对象引用计数器值大于1时,该对象就无法销毁;
  3. 由于自动释放池最后统一销毁对象,因此如果一个操作比较占用内存,例如:对象较多或者对象占用资源较多,最好不要放到自动释放池或者放到多个自动释放池;
  4. OC中类库的类方法一般都不需要手动释放,因为内部已经调用了autorelease方法;

关于内存管理,总结起来可以用三条原则概括:

  1. 使用new、alloc、copy方法创建一个对象时,该对象的保留计数器值为1。当不再使用该对象时,应该向该对象发送一条release或autorelease消息。这样该对象在其使用寿命结束时被销毁;
  2. 当你获得一个对象时,假设该对象的保留计数器值为1,而且已经被设置为自动释放,那么你不需要执行任何操作来确保该对象得到清理。如果你打算在一段时间内拥有该对象,则需要保留它并确保在操作完成时释放它。
  3. 如果你保留了某个对象,就需要(最终)释放或自动释放该对象。必须保持retain方法和release方法的使用次数相等。

注:对象之间可能交叉引用,此时需要遵循一个法则:谁创建,谁释放

参考文档:来自apple开发者官网的

collection(垃圾回收)。iOS不支持垃圾回收;ARC作为苹果新提供的技术,苹果推荐开发者使用ARC技术来管理内存;这篇笔记主要讲的是手动管理。 内存管理的目的是: 1.不要释放或者覆盖还在使用的内存,这会引起程序崩溃; 2.释放不再使用的内存,防止内存泄露。iOS程序的内存资源是宝贵的。 MRR手动管理内存也是基于引用计数的,只是需要开发者发消息给某块内存(或者说是对象)来改变这块内存的引用计数以实现内存管理(ARC技术则是编译器代替开发者完成相应的工作)。一块内存如果计数是零,也就是没有使用者(owner),那么objective-C的运行环境会自动回收这块内存。 objective-C的内存管理遵守下面这个简单的策略: 注:文档中把引用计数加1的操作称为“拥有”(own,或者take ownership of)某块对象/内存;把引用计数减1的操作称为放弃(relinquish)这块对象/内存。拥有对象时,你可以放心地读写或者返回对象;当对象被所有人放弃时,objective-C的运行环境会回收这个对象。 1.你拥有你创建的对象 也就是说创建的对象(使用alloc,new,copy或者mutalbeCopy等方法)的初始引用计数是1。 2.给对象发送retain消息后,你拥有了这个对象 3.当你不需要使用该对象时,发送release或者autorelease消息放弃这个对象 上面这个赋值操作不会拥有这个对象(这仅仅是个指针赋值操作);这和C++语言里的某些基于引用计数的类的行为是有区别的。想拥有一个objective-C对象,必须发送“创建”或者retain消息给该对象。 dealloc方法 dealloc方法用来释放这个对象所占的内存(包括成员变量)和其它资源。 不要使用dealloc方法来管理稀缺资源,比如文件,网络链接等。因为由于bug或者程序意外退出,dealloc方法不能保证一定会被调用。 Accessor Methods和内存管理 Accessor methods的原因可能是(原文档中没有说明):在初始化方法里,成员变量处于最初的状态,并没有任何值。考虑到一个成员变量的setter方法一般会对成员变量的旧值发送release消息。这种行为在初始化方法里没有意义。 如果需要在Initializer里给成员变量赋值,可参见一开始提到的原始文档里给出的示例代码。 使用weak reference(弱引用)来避免retain cycle 对一个对象发送retain消息会创建对这个对象的强引用(strong reference)。如果两个对象都有一个强引用指向对方,那么就形成了一个环(retain cycle)。这个环使得这两个对象都不可能被release。 弱引用(weak reference)指的是一种non-owning(非拥有)的关系,比如简单指针赋值关系。使用弱引用避免了retain cycle。但是需要注意的是,弱引用不能保证弱引用指向的对象是否存在,所以发消息给这个对象时一定要小心。如果弱引用指向的对象已经释放,那么发送消息给它会导致程序崩溃。所以,需要一点点额外的操作来使用弱引用所指的对象。比如,当向notification center注册一个对象时,notification center保存了一个指向这个对象的弱引用。当这个对象被回收时,需要通知下notification center。 当你使用对象时,要确保这个对象不会被回收。主要要注意以下两种情形: 1.当一个对象从collection对象(collection指的数组之类的集合)移除时,如果这个仅被collection对象拥有,那么移除操作了会被即可回收。所以如果要使用这个将要移除的对象,要先retain。 2.当“父”对象回收时。这和情形1类似。 Autorelease Pool pool是以桟(stack)的形式组织的。新创建的pool位于桟的最顶端。当发送autorelease消息给一个对象时,这个对象被加到栈顶的那个pool中。发送drain给一个pool时,这个pool里所有对象都会受到release消息,而且如果这个pool不是位于栈顶,那么位于这个pool“上端”的所有pool也会受到drain消息。 -一个对象被加到一个pool很多次,只要多次发送autorelease消息给这个对象就可以;同时,当这个pool被回收时,这个对象也会收到同样多次release消息。简单地可以认为接收autorelease消息等同于:接收一个retain消息,同时加入到一个pool里;这个pool用来存放这些暂缓回收的对象;一旦这个pool被回收(drain),那么pool里面的对象会收到同样次数的release消息。 -UIKit框架已经帮你自动创建一个autorelease pool。大部分时候,你可以直接使用这个pool,不必自己创建;所以你给一个对象发送autorelease消息,那么这个对象会加到这个UIKit自动创建的pool里。某些时候,可能需要创建一个pool: 1.没有使用UIKit框架或者其它内含autorelease pool的框架,那么要使用pool,就要自己创建。 2.如果一个循环体要创建大量的临时变量,那么创建自己的pool可以减少程序占用的内存峰值。(如果使用UIKit的pool,那么这些临时变量可能一直在这个pool里,只要这个pool受到drain消息;完全不使用autorelease pool应该也是可以的,可能只是要发一些release消息给这些临时变量,所以使用autorelease pool还是方便一些) 3.创建线程时必须创建这个线程自己的autorelease pool。 -使用alloc和init消息来创建pool,发送drain消息则表示这个pool不再使用。pool的创建和drain要在同一上下文中,比如循环体内。

我要回帖

更多关于 oc内存管理机制 的文章

 

随机推荐