Objective-C基础2:内存管理基础
1.内存存储区域
C、C++里面。栈区:存储临时变量和对象。堆区域:存储动态分配对象。静态变量存储区:存储静态变量和常量对象。
OC里面的内存存储区域跟C、C++一样。
2.为什么要进行内存管理
写过C、C++程序的都知道,内存管理永远是C++程序的一大痛点,项目当中崩溃全部来自于内存相关的操作,尤其是指针操作和内存操作,稍不注意就会产生内存访问违规造成程序崩溃。那么如何进行内存管理呢,个人认为有以下几点原则:尽量用系统提供给我们的封装对象,不要用原生的,比如用string而不要用char*,用string能避免项目中大部分的字符串违规操作;能在栈上分配就不在堆上分配,利用栈的自动释放功能帮我们释放;对于复杂的内存操作比如多个类或者多个线程引用同一块内存,那就给这块内存加引用计数;内存操作一定要判空操作。
我们知道OC中的所有对象都是动态分配的,那么OC中是如何管理这些对象的呢?答案是利用引用计数来管理。
3.OC中对象内存管理
OC中通过引用计数来管理对象的生命周期。
- (instancetype)retain OBJC_ARC_UNAVAILABLE; //引用加1
- (oneway void)release OBJC_ARC_UNAVAILABLE; //引用减1
- (instancetype)autorelease OBJC_ARC_UNAVAILABLE; //自动释放对象
- (NSUInteger)retainCount OBJC_ARC_UNAVAILABLE; //返回当前的引用技术
当对象B拥有对象A时,设置对象A时,最好掉用Retain提升引用计数,在dealloc方法里面减少引用技术。
注意下面代码的中(void) setA : (A*) pAIn实现
-(void) setA : (A*) pAIn
{
[pAIn retain];
[pA release];
pA = pAIn;
}
先保留PAIn,再释放PA,然后赋值,这种写法能有效地避免PAIn和PA指向同一对象这种情况,强烈建议一定要这样写。
代码如下:
@interface A : NSObject - (void) disInfo; @end @implementation A - (void) disInfo { NSLog(@"I'm A haha!"); } @end @interface B : NSObject { @private A* pA; } -(void) setA : (A*) pAIn; -(void) disA; @end @implementation B - (void)dealloc { [pA release]; [super dealloc]; } -(void) setA : (A*) pAIn { [pAIn retain]; [pA release]; pA = pAIn; } -(void) disA { [pA disInfo]; } @end int main(int argc, const char * argv[]) { A* pA = [A new]; B* pB = [B new]; [pB setA:pA]; [pB disA]; [pB release]; [pA release]; return 0; }
上面的例子中我们需要手动地掉用retain和release方法,也就是说我们需要知道何时要retain,何时需要release,很显然在业务逻辑很复杂的情况下需要追踪引用计数造成的问题需要花费比较多的时间。那么,如何让对象自动retain和release呢?写过Windows Com程序的人知道CComPtr这个智能指针类,OC又是如何做的呢?下面揭晓。
4.OC中自动释放内存
Cocoa中的自动释放池@autoreleasepool和NSAutoreleasepool配合对象的autorelease方法来实现自动释放内存。
1)利用autoreleasepool和autorelease。代码如下:
@autoreleasepool { A* pA = [A new]; [pA autorelease]; B* pB = [B new]; [pB autorelease]; [pB setA:pA]; [pB disA]; }
上面的代码跟3中的代码相比优势在:在pA和pB创建完成之后直接调用autorelease方法将对象加入自动释放吃,代码紧凑,后面再也不用担心在什么时候release了。
2)利用NSAutoreleasePool和autorelease。代码如下:
NSAutoreleasePool* pool = [NSAutoreleasePool new]; A* pA = [A new]; [pA autorelease]; B* pB = [B new]; [pB autorelease]; [pB setA:pA]; [pB disA]; [pool release];
代码跟跟利用关键字@autoreleasepool的代码差不多。
那么面对着两种方式,我们应该选择哪种呢?个人认为有一下几种原则:
1)对于对象操作紧凑的代码用@autoreleasepool,因为他的效率比NSAutoreleasePool高
2)对象A拥有一些例对象并且这些对象保留在NSArray等数组中,用 NSAutoreleasePool,因为只需要在A的dealloc对象当中调用NSAutoreleasePool的release方法,保存在NSArray的对象就会自动释放,很显然@autoreleasepool不具备这样的能力。