(一). 引用计数
main.m
ARC 自动引用计数,Automatic Reference Counting.
MRC 手动引用计数,Manual Refernce Counting.
内存管理机制:引用计数 retainCount.
每个对象都有引用计数.
引用计数的作用:标记有多少个指针指向这个内存空间.
为什么要标记?
为了避免其他指针访问这段内存时出现问题(野指针*访问*).
野指针不是问题,野指针的访问才是问题.
int main(int argc, const char * argv[]) {
引用计数的加法方法.
1.alloc.引用计数 +1
Person *person = [[Person alloc] init];
NSLog(@“%ld”, person.retainCount);
2. retain.引用计数 +1
Person *newPerson = person; // 把person的地址给newPerson,此时的引用计数依然为1,如果此时释放内存,就会造成其中一个指针为野指针,为了解决这个问题,用retain.
Person *newPerson = [person retain];
NSLog(@"%ld, %ld”, newPerson.retainCount, person.retainCount); // 此时newPerson的引用计数为2,因为我们把person的引用计数变成了2,然后用newPerson去指向这个地址,这个地址背后所对应的引用计数为2,注意与下面的copy区分.
3. release.引用计数 -1
[person release];
[person release]; // 如果内存的引用计数为0,系统会自动调用”.m”文件中的dealloc方法--触发内存销毁的方法(如下,次方法,是默认不写的).
Person.m
#import "Person.h"
@implementation Person
内存销毁法
dealloc(销毁) 和 alloc(开辟) 对应.
当引用计数变为0时,自动调用dealloc方法.
- (void)dealloc {
NSLog(@"对象所在的内存销毁");
[super dealloc];
}
@end
main.m
此时person已经release了两次(此时release两次newPerson和release两次person是等同效果),我们打印newPerson和person的引用计数.
NSLog(@“%ld, %ld”, newPerson.retainCount, person.retainCount);
return 0;
}
其实此时newPerson和person的引用计数已经为0,但是计算机取不到0,它只好打印最接近0之前的状态.
main.m
3.copy.
Person *ppp = [[Person alloc] init];
NSLog(@"ppp的引用计数为:%ld", ppp.retainCount);
Person *newPPP = [ppp copy]; // 复制ppp的内容,然后用一个newPPP来接受ppp的内容,但此时程序会崩溃,所以要在”.m”文件中写入一个拷贝缓冲池来解决拷贝之后:"-[Person copyWithZone:]: unrecognized selector sent to instance 0x10011486"的问题.
NSLog(@"再次打印ppp的引用计数为:%ld", ppp.retainCount); // 此时再打印ppp的引用计数,结果并未改变,仍然为1,说明,copy的作用只是复制内容.
NSLog(@"newPPP的引用计数为:%ld", newPPP.retainCount); // 此时打印newPPP的引用计数,结果为1,说明,copy之后的内容被newPPP接受了.
[ppp retain];
NSLog(@"再次打印ppp的引用计数为:%ld", ppp.retainCount); // 将ppp的引用计数再次retain,然后打印.
Person.m
- (id)copyWithZone:(NSZone *)zone {
// zone 拷贝缓冲池.
// 作用: 确保两个对象的信息除地址之外其它都一样.
Person *p = [[Person allocWithZone:zone] init];
p.name = self.name;
p.age = self.age;
return p;
}
(二). 自动释放
main.m
自动释放池,autoReleasePool
@autorelease {
Person *ppp = [[Person alloc] init];
[ppp retain];
[ppp retain];
相当于给ppp做了一个标记,延迟了它的释放时间.
[ppp autorelease];
NSLog(@"%ld", ppp.retainCount);
[ppp autorelease];
NSLog(@"%ld", ppp.retainCount);
[ppp autorelease];
NSLog(@"%ld", ppp.retainCount);
}
return 0;
}
当程序运行至autoreleasepool时,必须等程序执行到括号外面之后,ppp才释放内存.
(三). 内存管理的原则
main.m
int main(int argc, const char * argv[]) {
1.当看到alloc, retain, copy的时候, 需要对应的内存管理,哪个对象对应+1,就对哪个对象进行release(autorelease);
2.看不到,则不管.
Person *person1 = [[Person alloc] init]; // 创建一个person1对象.
Person *person2 = [person1 retain]; // 使person1的引用计数加一,用person2来接收它.
Person *person3 = [person2 retain]; // 使person2的引用计数加一,用person3来接收它.
Person *person4 = [person3 copy]; // 复制person3的内容,用person4来接收它.
Person *person5 = person4; // 将person4的内容给person5.
NSLog(@"person1的引用计数为:%ld", person1.retainCount);
NSLog(@"person4的引用计数为:%ld", person4.retainCount);
NSLog(@"person5的引用计数为:%ld", person5.retainCount);
[person1 release];
[person2 release];
[person3 release];
[person4 release];
return 0;
}
第一次触发dealloc方法是将person1内存释放.
第二次触发dealloc方法是将person4内存释放.
剩下的person5,变成了野指针.
(四). 如何用自动释放池,管理便利构造器.
在Person.h文件中添加两个属性,自定义初始化,便利构造器.
#import <Foundation/Foundation.h>
@interface Person : NSObject<NSCopying>
@property(nonatomic, retain)NSString *name;
@property(nonatomic, assign)NSInteger age;
- (instancetype)initWithName:(NSString *)name
age:(NSInteger)age;
+ (instancetype)personWithName:(NSString *)name age:(NSInteger)age;
@end
Person.m
#import "Person.h"
@implementation Person
- (instancetype)initWithName:(NSString *)name
age:(NSInteger)age {
self = [super init];
if (self) {
_name = name;
_age = age;
}
return self;
}
+ (instancetype)personWithName:(NSString *)name age:(NSInteger)age {
// 在便利构造器中创建一个person对象,并加入自动释放池(以下是三种释放内存的写法.)
(1).
Person *person = [[Person alloc] initWithName:name age:age];
[person autorelease];
return person;
(2).
Person *person = [[Person alloc] initWithName:name age:age];
return [person autorelease];
(3).
return [[[Person alloc] initWithName:name age:age] autorelease];
}
@end
main.m
@autoreleasepool {
// 在自动释放池中用一个Person类调用便利构造器方法,并打印对象p的name属性.
Person *p = [Person personWithName:@"哈哈" age:100 ];
NSLog(@"%@", p.name);
}
Person.m
#import "Person.h"
@implementation Person
没有在".h"中声明,直接在".m"中写的方法实现,此方法为私有方法,只能在".m"中使用.
- (void)sayHi {
NSLog(@"你好!~");
}
用一个方法A,在A方法中调用该私有方法 - (void)sayHi 方法(此A方法省略在”.h”中的声明过程,只写在”.m”中的实现过程).
-(void)A {
[self sayHi] // 该私有方法为对象方法,所以在便利构造器中调用该私有方法的时候,要先创建一个对象,因为便利构造器是类方法.
}
@end