——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-
Autorelease
Autorelease需要配合autoreleasepool才能使用,没有autoreleasepool,其内部的autorelease是不起release作用的。
其实autorelease就是针对对象一创建alloc就要进行release这样僵死的内存管理原则而进行的改进,因为在手动情况下,关于对象的使用要非常小心的写在release前,一旦错误,便造成僵尸对象,针对这种情况,autorelease利用autoreleasepool将所有的对象的创建和使用抱包在一个括号里,程序员可以在里面尽情的使用而不用估计最后的release
适用场合:
Int main ( )
{
Person *p = [[Person alloc] init];
p.age = 10;
[p release];
//[p release]要防止僵尸对象产生,要恰到位置(对象p的使用要在release之前),而且,一旦重复写,就导致了僵尸对象,而且每个对象创建,都必须加上release,这样的程序灵活性不强。这就引出了Autorelease。
}
Autorelease方法会返回对象本身,它会将对象放到一个自动释放池(它也是对象),当自动释放池被销毁时,会对池子里面的所有对象做一次release操作。
自动创建池创建:
IOS 5.0后的自动创建池创建:
@autoreleasepool
{ //{开始代表了创建了释放池;
Person *p = [[Person alloc] init] autorelease] ; //释放池销毁时,调用[p release];
p.age = 10;
} //}结束代表了销毁释放池
Person *p = [[Person alloc] init] autorelease autorelease]; //错误,池子销毁时,会对对象做两次release操作,导致僵尸对象。
IOS 5.0前老版本的自动创建池创建:
NSAutoreleasepool *pool = [NSAutoreleasePool alloc] init];
Person *p = [Person alloc] init autorelease];
[pool release];
Autorelease好处:
不用关心对象释放的时间;
不用再关心什么时候调用release.
使用注意:
占用内存大的对象不要随便用autorelease,用release精确控制;
占用内存较小的对象使用autorelease,没太大影响(未精确控制);
Alloc之后调用了autorelease又调用release,会在autorelease时访问僵尸对象:Person *p = [[Person alloc] init] autorelease] ,[p release];
连续调用多次autorelease:Person *p = [[Person alloc] init] autorelease autorelease];
将内存进行封装管理时,需要alloc进行创建的要调用该封装方法,但如字符串这类对象,不需要调用该方法;
系统自带的方法里没有包含alloc,new,copy,说明返回的对象都是autorelease的,也就是说系统中有老方法(创建时alloc,autorelease)和新方法(封装管理alloc,autorelease的,调用封装方法),不论新旧方法,在main函数中新方法直接调用类方法就可以创建autorelease对象,旧方法在创建对象之后,要手动在之后进行autorelease或release操作,才是完整的创建一个autorelease对象。
NSNumber *num2 = [NSNumber numberWithInt:10]; 系统内部封装
NSNumber *num = [NSNumber alloc] initWithInit:10 autorelease]; 系统未封装,需手动
在非ARC中,这些方法的准则是需要遵守的,但是在非ARC中,不需要去autorelease管理内存,那在ARC中这些系统类方法一旦使用,其内部的封装不就要出错了,其实不然,autorelease需要配合autoreleasepool才能使用,没有autoreleasepool,其内部的autorelease是不起release作用的,但是怎么不会报错????[A1]
- 类方法都是以类名作为开头:
NSString(类名):StringWithFormat;
NSNumber(类名): numberWithInit;
- 开发中经常提供一些类方法,快速创建一个已经autorelease的对象,
创建对象时不要直接用类名,一般用self;
Person *p =[[self alloc] init] autorelease];
内存分析:
Autoreleasepool存放在在栈中,系统中有无数个池子,对象一创建,将其放入置于栈顶的池子,观察对象所在池子,通过栈的存放和代码块的作用域进行判断。
在ios程序运行过程中,会创建无数个池子,这些池子都是以栈结构而存在的;
当一个对象调用autorelease方法时,会将这个对象放到栈顶的释放池;
实例1:旧方法
Int main ( )
{
@aotoreleasepool //池子1为栈顶
{
Person *p1 = [[Person alloc] init autorelease];
P1.age = 10;
@autoreleasepool //池子2为栈顶,p2进;p2在池2的代码块
{
Person *p2 = [[Person alloc] init autorelease];
P2.age = 15;
}
Person *p3 = [[Person alloc] init autorelease]; //池子2销毁,池子2为栈顶,p3存放在池1;p3也在池1代码块中
}
实例2:新方法:Autorelease开发中应用
快速创建一个autorelease对象,对内存管理代码进行封装(alloc,autorelease在方法中),一般是调用一个与类名相同的类方法:
例1:单类:一个方法创建一个类型对象
@interface Person:NSObject
@property (nonatomic, assign) int age;
@end
- (id) personWithAge:(int) age;
@implementation Person
- (id) personWithAge:(int) age //内存管理方法
{
Person *p = [[Person alloc] init autorelease]; [A2]
//[Person alloc]中Person类太局限,继承类无法使用,[self alloc]
p.age = age; //在不重写构造方法的基础上初始化,还不如直接构造方法中重写!!!!!
return p;
}
@end
Int main ( )
{
@autoreleasepool
{
Person *p = [Person personWithAge:10];
NSString *str = @”jack”; //系统内部已封装
NSString1 *str = [NSString stringWithFormat:@”age =%d”,12]; //系统内部已封装
NSNumber *num2 = [NSNumber numberWithInt:10]; //新方法,系统内部已封装
NSNumber *num = [NSNumber alloc] initWithInit:10]; //旧方法,内部无内存管理封装
[num release];
//或者NSNumber *num = [NSNumber alloc] initWithInit:10 autorelease];
}
}
简化了两行代码,能迅速创建一个autorelease对象:
Person *p = [[Person alloc] init autorelease];
p.age = 10;
例2:继承类:一个方法创建不同类型对象
Person类如例1:
@interface GoodPerson:Person
@property (nonatomic ,assign) money;
@end
@implementation GoodPerson
@end
Int main ( )
{
@autoreleasepool
{
GoodPerson *p = [GoodPerson personWithAge:10];
是否应该:GoodPerson p =(GoodPerson )[GoodPerson personWithAge:10]; [A3]
p.money = 100; //该行报错;
}
其实p指针是GoodPerson类型,但指向Person,子类类型指向父类,在父类Person对象中是没有setMoney方法的。
}
改进:
将父类autorelease封装方法person进行改进
- (id)personWithAge:(int) age
{
Person *p =[[self alloc] init] autorelease]; [A4] [A5]
将固定放类Person位置换成self,可以灵活的将此处换成调用类,这样,同一个方法可以实现继承类中不同对象的创建,一个父类方法可创建无数父类和子类对象。同时Person类为多态。
p.age = age;
}
内存总结:
计数器的基本操作;
Set方法的内存管理:
@property内存管理;
Autorelease;
ARC
Automatic reference counting为编译器特性;
ARC判断原则:只要没有强指针指向对象,就会释放对象;对指针未提前指空或指其他时,一定要等程序结束销毁指针才销毁对象???main函数可是无限循环的!!
指针两种:
强指针:默认情况下都是强指针,
弱指针:__weak Person *p2
强指针:
Int main ( )
{
Person *p = [[Person alloc] init]; //该对象在第二行程序被释放,指针转向指向
P=[[Person alloc] init]; //该对象在程序结束被释放,指针变量被回收
}
程序结束,指针变量回收
Int main ( )
{
Person *p = [[Person alloc] init]; //可以画内存图分析
Person *p2 = p;
P=[[Person alloc] init];
}
弱指针:
Int main( )
{
Person *p = [[Person alloc] init];
__weak Person *p2 = p;
P = nill; //弱指针指向不管用,只要强指针不在,即使弱指针还指向,一样销毁
P2 = nill;
}
系统发现对象所有强指针释放了,会立即把所有弱指针=nill,防止野指针存在
__weak Person *p = [[Person alloc] init] //对象一创建就释放
NSLog(@”@”,p) //弱指针,已被p=nill
所以p的输出不会报错。
ARC特点:
不允许调用release、retain、retainCount;
允许重写dealloc,但是不允许调用[super dealloc]
@property参数: @property(nonatomic,strong) Dog *dog;
Strong: 成员变量是强指针,相当于原来的retain(适用于OC对象,弱指针的话,等于没加+1);
Weak: 成员变量是弱指针,相当于原来的assign
Assign: 适用于非OC对象类型;
- 以前的retain改strong,assign还是assign
实例1:
@interface Dog:NSObject
@end
@implementation Dog
@end
@interface Student
@property (nonatomic,strong) Dog * dog;
@end
Int main ( )
{
Dog *d = [[Dog alloc] init];
Person *p = [[Person alloc] init];
P.dog = d;
d = nill; //d指针释放,但_dog强指针还指向,dog对象还存在
}
情景2:@property (nonatomic,weak) Dog *d;
同样的main函数,到该程序行时
d = nill; //当指针d清空,只剩_dog弱指针指向,此时dog对象被销毁;
实例2:
个人思路
其实很明显,强指针就相当于一指向它,随即对象做了一次+1的retain操作(在set方法中retain),而弱指针即使指向,也是枉然,因为它在指向同时,是没做内存管理的,也即没对对象做+1的计数器操作(在set方法中就是assign)。
ARC的原理就是根据对象有没有被指针指向操作来判断对象是否被销毁,所以在这样智能的情况下,程序员可以抛开很多,但在ARC再怎么无所顾忌,你在使用对象的时候最起码要清楚所指向的指针都做了些什么操作,指空,转指向?如果这个都不知道,系统再智能你也会犯错,你对指针都转指向了,还利用他们进行方法调用,这不就是野指针了么!!
非ARC项目重构成ARC项目:
转化
查看项目是否为ARC项目
项目中ARC与非ARC共存:
告诉编译器该文件不需ARC: -fno-objc-arc
告诉编译器该文件需要ARC: -f-objc-arc
ARC循环引用:
当两端循环引用的时候,解决方案:
ARC:
1端用strong,另一端用weak;
非ARC:
1端用retain,一端用assign;
[A1]ARC项目中这些是报错的么?还需要去验证numberWithInt该方法的内部封装
[A2]结合之前的init构造方法,我们可以在这里对构造方法也进行实现,重写构造方法,默认初始化设置
[A3]需不需要父类指针强转??在方法内部就需要转
[A4]父类指针这样返回子类可好??虽然需要的只是地址:
Animal *a = [Dog new];
Dog *d = a //警告
Dog d = (Dog ) a //正确