内存管理的困境
1、当我们要释放一个堆空间时,要确保这个堆空间的所有指针都使用完毕,这时候堆空间才能释放,避免提前释放
2、释放指针指向的堆空间时,要确保哪些指针指向这个堆空间,这些指针只能释放一次,避免再次释放
3、使用ARC,可能因为代码的不规范,导致内存提前释放
Objective-C如何对内存管理的,说说你的看法和解决方法?
答:Objective-C的内存管理主要有三种方式ARC(自动内存计数)、手动内存计数、内存池。
1). (Garbage Collection)自动内存计数:这种方式和java类似,在你的程序的执行过程中。始终有一个高人在背后准确地帮你收拾垃圾,你不用考虑它什么时候开始工作,怎样工作。你只需要明白,我申请了一段内存空间,当我不再使用从而这段内存成为垃圾的时候,我就彻底的把它忘记掉,反正那个高人会帮我收拾垃圾。遗憾的是,那个高人需要消耗一定的资源,在携带设备里面,资源是紧俏商品所以iPhone不支持这个功能。所以“Garbage Collection”不是本入门指南的范围,对“Garbage Collection”内部机制感兴趣的同学可以参考一些其他的资料,不过说老实话“Garbage Collection”不大适合适初学者研究。
解决方法: 通过alloc – initial方式创建的, 创建后引用计数+1, 此后每retain一次引用计数+1, 那么在程序中做相应次数的release就好了.
2). (Reference Counted)手动内存计数:就是说,从一段内存被申请之后,就存在一个变量用于保存这段内存被使用的次数,我们暂时把它称为计数器,当计数器变为0的时候,那么就是释放这段内存的时候。比如说,当在程序A里面一段内存被成功申请完成之后,那么这个计数器就从0变成1(我们把这个过程叫做alloc),然后程序B也需要使用这个内存,那么计数器就从1变成了2(我们把这个过程叫做retain)。紧接着程序A不再需要这段内存了,那么程序A就把这个计数器减1(我们把这个过程叫做release);程序B也不再需要这段内存的时候,那么也把计数器减1(这个过程还是release)。当系统(也就是Foundation)发现这个计数器变 成员了0,那么就会调用内存回收程序把这段内存回收(我们把这个过程叫做dealloc)。顺便提一句,如果没有Foundation,那么维护计数器,释放内存等等工作需要你手工来完成。
解决:一般是由类的静态方法创建的, 函数名中不会出现alloc或init字样, 如[NSString string]和[NSArray arrayWithObject:], 创建后引用计数+0, 在函数出栈后释放, 即相当于一个栈上的局部变量. 当然也可以通过retain延长对象的生存期.
3). (NSAutoRealeasePool)内存池:可以通过创建和释放内存池控制内存申请和回收的时机.
解决:是由autorelease加入系统内存池, 内存池是可以嵌套的, 每个内存池都需要有一个创建释放对, 就像main函数中写的一样. 使用也很简单, 比如[[[NSString alloc]initialWithFormat:@”Hey you!”] autorelease], 即将一个NSString对象加入到最内层的系统内存池, 当我们释放这个内存池时, 其中的对象都会被释放.
内存管理的几条原则是什么?按照默认法则.那些关键字生成的对象需要手动释放?在和property结合的时候怎样有效的避免内存泄露?
答:1、谁申请,谁释放 。谁写alloc,谁就写release;哪个类创建,哪个类释放
2、凡是使用了alloc/retain/new/copy,这些方法创建和引用对象,都需要调用release/autorelease进行释放
内存管理主要要避免“过早释放”和“内存泄漏”,对于“过早释放”需要注意@property设置特性时,一定要用对特性关键字,对于“内存泄漏”,一定要申请了要负责释放,要细心。关键字alloc 或new 生成的对象需要手动释放;设置正确的property属性,对于retain需要在合适的地方释放
MRC、ARC
规则:谁创建,谁释放,谁引用,谁管理的原则。
alloc, init,new,copy-------->release
当一个对象的retainCount = 0的时候,就会调用dealloc方式,将对象释放掉。释放内存的并不是release,而是dealloc。
retain是保持的意思,给一个对象发送retian消息,就意味着保持这个对象。它的retainCount+1。具体我用一张流程图说明:
什么情况下使用weak关键字,相比assgin有什么不同?
1、delegate.因为在arc中,有可能会出现循环引用。
2、自身已经对它进行一次强引用,没有必要再强引用一次,比如IBOutlet控件属性。
不同点
1、weak为属性设置新值的时候,设置方法既不保留新值,也不释放旧值。在遭到摧毁时,属性值也会被清空。assgin的设置方法只针对基本数据类型等简单的赋值操作。
2、assgin可以用非OC对象,weak必须用于oc对象。
@property中有哪些属性关键字?/@property后面可以有哪些修饰符?
1). readwrite 是可读可写特性;需要生成getter方法和setter方法时
2). readonly 是只读特性 只会生成getter方法 不会生成setter方法 ;不希望属性在类外改变
3). assign 是赋值特性,setter方法将传入参数赋值给实例变量;仅设置变量时;
4). retain 表示持有特性,setter方法将传入参数先保留,再赋值,传入参数的retaincount会+1;
5). copy 表示赋值特性,setter方法将传入对象复制一份;需要完全一份新的变量时。
6).nonatomic 非原子操作,决定编译器生成的setter getter是否是原子操作,atomic表示多线程安全,一般使用nonatomic
weak属性在dealloc中置nil么?
不需要。
在ARC中环境无论是强指针还是弱指针都无需在dealloc设置为Nil,ARC会自动帮我们处理。
在属性所指的对象遭到摧毁时,属性值也会清空。
autorelease
实现、本质、RunLoop相关
·App启动后,苹果在主线程 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 了。
也就是说AutoreleasePool创建是在一个RunLoop事件开始之前(push),AutoreleasePool释放是在一个RunLoop事件即将结束之前(pop)。
AutoreleasePool里的Autorelease对象的加入是在RunLoop事件中,AutoreleasePool里的Autorelease对象的释放是在AutoreleasePool释放时。