黑马程序员---oc ARC的演变

——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好处:

  1. 不用关心对象释放的时间;

  2. 不用再关心什么时候调用release.

使用注意:

  1. 占用内存大的对象不要随便用autorelease,用release精确控制;

  2. 占用内存较小的对象使用autorelease,没太大影响(未精确控制);

  3. Alloc之后调用了autorelease又调用release,会在autorelease时访问僵尸对象:Person *p = [[Person alloc] init] autorelease] ,[p release];

  4. 连续调用多次autorelease:Person *p = [[Person alloc] init] autorelease autorelease];

  5. 将内存进行封装管理时,需要alloc进行创建的要调用该封装方法,但如字符串这类对象,不需要调用该方法;

  6. 系统自带的方法里没有包含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]

  1. 类方法都是以类名作为开头:

NSString(类名):StringWithFormat;

NSNumber(类名): numberWithInit;

  1. 开发中经常提供一些类方法,快速创建一个已经autorelease的对象,

创建对象时不要直接用类名,一般用self;

Person *p =[[self alloc] init] autorelease];

内存分析:

Autoreleasepool存放在在栈中,系统中有无数个池子,对象一创建,将其放入置于栈顶的池子,观察对象所在池子,通过栈的存放和代码块的作用域进行判断。

  1. 在ios程序运行过程中,会创建无数个池子,这些池子都是以栈结构而存在的;

  2. 当一个对象调用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;

}

内存总结:

  1. 计数器的基本操作;

  2. Set方法的内存管理:

  3. @property内存管理;

  4. 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特点:

  1. 不允许调用release、retain、retainCount;

  2. 允许重写dealloc,但是不允许调用[super dealloc]

  3. @property参数: @property(nonatomic,strong) Dog *dog;

Strong: 成员变量是强指针,相当于原来的retain(适用于OC对象,弱指针的话,等于没加+1);

Weak: 成员变量是弱指针,相当于原来的assign

Assign: 适用于非OC对象类型;

  1. 以前的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 //正确

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值