ios内存管理随笔
以下是我所总结的一些常见内存管理需要注意的方面,并没有严格意义上的数据来源,可能有些部分有歧义,不太正确或遗漏,大家可以和我说说,共同进步。
按照这样方式来使用的话,确实可以大大减少内存方面的问题,错误使用内存导致的崩溃也会少点。
这里记录下,方便以后参考,也为大家提供个借鉴。
1. 保持对象的属性/成员变量和对象本身的引用计数保持一致
a) 成员变量初始化时,不应该用autorelease的,如果是autorelease,则最好要retain一下,保持引用计数为1,然后在dealloc中释放。
b) 对于属性,除自定义的setter方法和dealloc之外,都需要使用self.testProperty=@”testPropert”的方式来进行赋值,这样可以保持自己控制的引用计数始终为1;否则则需要在每次赋值之前都进行release。如
[_testProperty release];
_testProperty = testProperty;
c) 对应于a),所有的成员变量都需要在dealloc中进行释放。
d) 每次成员变量release之后,除非立马进行赋值,否知需要将其置为nil,防止其他引用的地方无法对其进行判断。
dealloc中可以酌情处理,关于成员变量,不设置为nil也无妨。简单的处理方式可以将dealloc中所有的释放的变量都置为nil。
e) 属性中所有的obj-c对象都应该设置为retain(除delegate外),对其引用计数进行+1操作,而不要使用assign。
对于类似于BOOL,int,以及Core**框架等则需要使用assign,不需要更改引用计数。
但是delegate等需要使用assign来进行属性声明,当前对象不应该管理代理对象的引用计数。
如:
@property (nonatomic, copy) NSString*testProperty;
@property (assign) BOOLflag;
@property (assign) id<property>delegate;
f) 基本原则,谁分配,谁释放。在和C框架交互使用时更应该注意。
g) 注意在struct中使用obj-c对象的内存释放问题,确保在free的时候将引用计数变为0,或已经是autorelease。
2. 属性关键字的使用
a) retain和copy的区别
retain的生成的代码类似于如下:
- (void)setTestProperty:(id)testProperty { if(_testProperty != testPropert) { [_testProperty release]; _testProperty = [testPropert retain]; } }
所以retain会释放旧的值,然后设置为新的值,并retain,自身保持其引用计数。从此也可以看出,成员变量_testProperty引用外部变量时,始终会retain一次。
copy会产生一个新的对象指针,例如
一个NSString*的地址为0x0011,内容为@”testProperty”
copy操作之后,产生一个新的NSString *,其地址为0x1122,内容相同,新的对象的retainCount为1,不对旧对象进行操作,故旧对象没有任何变化。
所以可以理解为retain其实是对指针的拷贝,copy是对内容的拷贝。但是由于NSArray,NSDictionay对象等,由于其内容也是指针,故拷贝的其实是数组内部的指针。
也即一般情况下,都可以将NSString 的属性声明为copy的。如@property (nonatomic, copy) NSString *test;
b) assign,nonatomic
assign, Setter方法为直接赋值,不进行retain操作,通常是为了基本类型或像delegate之类的引用(防止出现循环引用问题)。
nonatomic, 非原子性访问,不加同步,多线程并发访问会提升性能,如果不加此属性,默认为原子型事务。锁被加到所属对象实例级。nonatomic使用也较多。
c) @synthesize xxx; 为xxx生成相应的Setter/Getter方法
d) 系统会默认分配一个没有对应变量的属性,如下示例:
@property (nonatomic, copy) NSString *testProperty;
@synthesize testProperty;
示例中并未声明任何变量对应与testProperty属性,但是仍然可以使用self.testProperty来进行赋值和取值。
这是因为系统会默认分配一个_testProperty的变量来与之对应。但建议还是添加一个成员变量,这样更为清晰。
3. 在obj-c编程中使用了CoreGraphics.framework,CoreTelephony.framework等C框架
该类C框架采用CFRelease/CFRetain去控制对象的生命周期。其可以与obj-c对象直接转换。例如:
NSString *str = [NSStrig stringWithFormat:@”%@”, @”abcdefg”];
CFStringRef ref = (NSString *)[str retain];
此时需要使用CFRelease(ref);来对其进行释放;
4. 直接使用C语言,则需要自己去控制内存的分配和释放
malloc和free需要严格对应,如果作为函数参数的话,需要使用指向指针的变量来作为函数参数。外部提供释放。
关于这块,建议看看圣经《深入理解计算机系统》,上面讲得非常透彻与详细。
5. 特殊场景
a) @”123″, 以及initWithString等产生的引用计数都是非常大的。对此执行的release操作只是为了与之前的init对应,并不会真正的释放
b) 对象release之后,不会立刻释放,如:
[obj release];
NSLog(@”obj = %@”, obj); // 这句话不会导致崩溃
NSLog(@”obj = %@”, obj); // 此时的调用会导致崩溃
c)NSAutoreleasePool的性能问题
NSAutoreleasePool也会占用一定的性能,在XCode4.2中默认使用的编译期支持@autoreleasepool{}块来代替NSAutoreleasePool,其性能相对于
NSAutoreleasePool来说,有较大的提升(记得曾经看过一份文档说会提升6倍以上)。
by yytong