iOS 应用开发(五)OC 面向对象之内存管理
(一)概述
任何继承了 NSObject 的对象,都需要进行内存管理。
每个 OC 对象都有自己的引用计数器(4个字节),是一个整数,表示“对象被引用的次数”。对象刚创建的时候,引用计数器默认为1;当引用计数器为0时,对象占用的内存就会被系统回收。
- 引用计数器(refer_counter)操作:
retain:refer_counter + 1;// retain 方法返回对象本身
release:refer_counter -1;
retainCount:获取当前 refer_counter 的值。
注意:当对象被销毁时,系统会自动向对象发送一条 dealloc 消息。
// 打开“Build Setting”,找到“Objective-C Automatic Reference Counting”项,将它的值设置成“NO”
// 避免 ARC forbids explicit message* 编译错误
#import <Foundation/Foundation.h>
@interface Person : NSObject
@property int age;
- (id)initWith:(int)age;
@end
-------------------------------------------------------
#import "Person.h"
@implementation Person
+ (void)load {
NSLog(@"Person----load");
}
#pragma mark - init
- (id)initWith:(int)age {
if (self = [super init]) {
self->_age = age;
}
return self;
}
#pragma mark - toString
- (NSString *)description {
return [NSString stringWithFormat:@"<Person: age=%d>", self->_age];
}
#pragma mark - recycle
- (void)dealloc {
NSLog(@"对象被回收");
[super dealloc];
}
@end
-------------------------------------------------------
#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
// 创建对象后,refer_counter 默认为1
Person *p = [[Person alloc] initWith:20];
NSLog(@"%@",p);
// retain:refer_counter + 1; 并返回对象本身
// Person *p1 = [p retain];
// NSLog(@"age=%d",p1.age);
// 获取当前 refer_counter 的值
NSUInteger count = [p retainCount];
NSLog(@"count=%ld",count);// count=1
// release:refer_counter -1
// 此时引用计数器为0,对象占用的内存就会被系统回收。
// 触发 dealloc 方法调用(对应 alloc)
[p release];
// 报"message sent to deallocated instance 0xxxxxx"
p.age = 10;
// 指针p变成空指针
p = nil;
// EXC_BAD_ACCESS: 访问了一块已经被回收的内存
// 野指针错误:指向僵尸对象(不可用内存)的指针
// 注意:OC 不存在空指针错误
[p release];
[p release];
[p release];
[p release];
NSLog(@"%@",p);// (null)
return 0;
}
一般来说,有 alloc/retain,就必须有 release。
#import <Foundation/Foundation.h>
@interface Student : NSObject {
int _no;
NSString *_name;
}
- (void)setNo:(int)no;
- (int)no;
- (void)setName:(NSString *)name;
- (NSString *)name;
@end
-------------------------------------------------------
#import "Student.h"
@implementation Student
- (void)setNo:(int)no {
_no = no;
}
- (int)no {
return _no;
}
- (void)setName:(NSString *)name {
if (name !=_name) {
// 先释放原来的,然后引用计数器+1并赋值。
[name release];
_name = [name retain];
}
}
- (NSString *)name {
return _name;
}
- (void)dealloc {
[_name release];
NSLog(@"Student 对象被释放");
[super dealloc];
}
@end
(二)@property 参数
#import <Foundation/Foundation.h>
#import "Dog.h"
@interface Student : NSObject
// 【读写属性】
// (readonly):只读,只会生成getter;
// (readwrite): 可读可写,setter getter同时生成(默认)
@property (readonly,nonatomic) int no;
// 【setter 内存管理相关参数】
// assign: 生成的setter方法(默认参数,直接赋值,适用于非oc对象)
// retain: 生成的setter方法(先判断新旧对象是否为同一个对象,若不是则release旧值,再retain新值。)
// copy: release 旧值,copy 新值(一般用于 NSString ,block)。
@property (copy,nonatomic) NSString *name;
//(可自定义 setter(注意要带冒号)/getter 方法名,但不推荐)
@property (nonatomic,retain,setter=setDog2:,getter=getDog2) Dog *dog;
// 【多线程管理】
// nonatomic:性能高(推荐)
// atomic:性能低(默认)
@property (nonatomic,getter=isRich) BOOL rich;
@end
-------------------------------------------------------
#import "Student.h"
@implementation Student
- (void)dealloc {
[_name release];
[_dog release];
NSLog(@"Student 对象被释放");
[super dealloc];
}
@end
-------------------------------------------------------
// test...
Student *student = [[Student alloc] init];
student.rich = YES;
NSLog(@"student isRich=%d",student.isRich);
Dog *dog = [[Dog alloc] init];
student.dog2 = dog;
NSLog(@"%@",student.getDog2);
[dog release];
[student release];
@class 仅仅是告诉编译器,XXX 是一个类,一般用于相互依赖(为避免对象无法释放,方法1(非arc):一方用 retain,另一方用 assign; 方法2(arc) :一方用strong,一方用weak)。
#import <Foundation/Foundation.h>
@class Car;// 使用@class,不用#import
@interface Person : NSObject
@property (nonatomic,retain) Car *car;
@end
-------------------------------------------------------
#import "Person.h"
#import "Car.h"
@implementation Person
- (void)dealloc {
[_car release];
NSLog(@"Person对象被释放");
[super dealloc];
}
@end
-------------------------------------------------------
#import <Foundation/Foundation.h>
@class Person;
@interface Car : NSObject
@property (nonatomic,assign) Person *person;
@end
-------------------------------------------------------
#import "Car.h"
#import "Person.h"
@implementation Car
- (void)dealloc {
// [_person release];
NSLog(@"Car对象被释放");
[super dealloc];
}
@end
(三)autorelease 自动释放
autorelease 会将对象方法一个自动释放池中并返回对象本身( refer_count不变,仍然是1)。当自动释放池被销毁时,会对池子中所有对象做一次 release 操作,一般适用于小对象。
#import <Foundation/Foundation.h>
@interface Person : NSObject
@property (nonatomic,assign) int age;
- (id)initWith:(int)age;
@end
-------------------------------------------------------
#import "Person.h"
@implementation Person
- (id)initWith:(int)age {
if (self = [super init]) {
_age = age;
}
return self;
}
- (NSString *)description {
return [NSString stringWithFormat:@"<Person: age=%d>", _age];
}
- (void)dealloc {
NSLog(@"Person 对象被销毁");
[super dealloc];
}
@end
-------------------------------------------------------
#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
// 自动释放池(延迟执行 release,以栈结构存在)
@autoreleasepool {// {表示创建了释放池
// autorelease 会将对象方法一个自动释放池中并返回对象本身,
Person *p = [[[Person alloc] initWith:20] autorelease];
NSLog(@"%@",p);
// [p retain];
@autoreleasepool {
Person *p1 = [[[Person alloc] initWith:30] autorelease];
NSLog(@"%@",p1);
}// 由于是“栈顶”,所以释放时先执行(dealloc)
}// }表示销毁释放池
return 0;
}
一般系统自带的方法里面没有包含使用 alloc/new/copy/ 说明返回的对象都是 autorelease 的。类方法里面,尽量使用self。
(四)ARC
ARC:automatic reference counting, Xcode 编译器自动引用计数。判断标准:只要没有强指针(局部引用出栈 / 弱指针)指向对象,就会释放对象。
指针分2种:
1 强指针:默认都是强指针。
2 弱指针:__weak Person *p =[Person personWith:20];// 对象一开始被创建,就马上被释放了。
ARC 特点:
1 不允许调用release、retain、retainCount,但可以重写dealloc(注意不能调用[super dealloc]) 。
2 @property 参数
strong 成员变量是强指针,相当于原来的retain(适用于oc 对象);
weak 成员变量是弱指针 (适用于oc 对象)。
不使用 ARC.png