黑马程序员——OC的内存管理——ARC

———-android培训、Java培训、iOS培训,期待与您交流———-

一、基本概念

1.什么是ARC

ARC(自动引用计数)。在ARC模式下,系统会自动追踪对象,并决定判断哪个对象仍会使用哪个对象不会被使用,并在适当的位置插入retain和release。

ARC并非垃圾回收机制,而只是一个编译器特性,即在需要retain和release的地方自动加入该语句。这一工作是在编译时完成的,相比较而言垃圾回收是在程序运行时工作。

2.ARC的判断准则

系统如何判断一个对象是否应该被释放?
当没有强指针指向一个对象时,该对象就会被释放

3.ARC下指针的分类:

  1. 强指针:默认情况下,所有的指针都是强指针,关键字__strong。
  2. 弱指针:__weak关键字修饰的指针。例如声明一个弱指针:__weak Person *p。

4.注意事项

ARC是一种机制,通过是否有强指针指向对象来判断是否需要将该对象销毁。因此在ARC模式下,不能使用release、autorelease、retain以及retainCount。与MRC一样,ARC管理的是NSObject的子类对象,不能管理其他基础数据类型(例如:整型、结构体等)。

二、ARC的基本使用

编译器默认状态下就是开启ARC机制的。使用ARC时,不需要额外的任何操作来管理对象,对象会在没有强指针指向时自动释放。下例中的对象已经重写了dealloc方法:
ARC下重写dealloc方法不需要写[super dealloc]语句

int main(){
    @autoreleasepool{
        Person *p = [Person new];
    }//->对象已经被释放
    return 0;
}

由于在内层的花括号结束时,作为局部变量的指针p会被释放,因此对象Person就没有强指针指向了,于是系统自动调用dealloc方法,销毁该对象。

如果原本指向一个对象的指针指向了其他对象,那么远来的对象也会由于没有强指针指向而被销毁:

int main(){
    @autoreleasepool{
        Person *p = [Person new];
        p = nil;//->对象已经被释放
    }
    return 0;
}

只有有强指针指向的对象才不会被销毁,而若指针不能保留对象:

int main(){
    @autoreleasepool{
        Person *p = [Person new];
        __weak Person *p2 = p;
        p = nil;//->对象已经被释放
    }
    return 0;
}

三、作为成员变量的对象的ARC

在生成成员变量的set/get方法时,使用@property的参数可以设置该成员变量指针类型(__weak__strong)。设置下面两个对象Person和Dog:

#import "Dog.h"

@implementation Dog
- (void)dealloc
{
    NSLog(@"Dog已经被销毁");
}
@end
#import <Foundation/Foundation.h>
#import "Dog.h"
@interface Person : NSObject
@property (nonatomic,weak) Dog *dog;
@end
#import "Person.h"

@implementation Person
- (void)dealloc
{
    NSLog(@"Person已经被销毁");
}
@end

main函数中有如下操作:

#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *p = [Person new];
        Dog *d = [Dog new];
        p.dog = d;
        d = nil;//->Dog已经被销毁
        p = nil;//->Person已经被销毁

    }
    return 0;
}

虽然在d指向nil之后p.dog仍然指向Dog对象,但是由于p.dog是弱指针,所以对象在此时已经被销毁。

如果将@property的参数改为(nonatomic,strong)或者(nonatomic),则main函数中必须如下操作才能销毁Dog对象:

#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *p = [Person new];
        Dog *d = [Dog new];
        p.dog = d;
        p.dog = nil;
        d = nil;//->Dog已经被销毁
        p = nil;//->Person已经被销毁

    }
    return 0;
}

四、循环引用的问题

当两个类相互引用,使用#import编译器就会报错,此时可以使用@class。

使用@class引入一个类时,编译器不会查看该类的具体内容,而只是在引入该类的文件中单纯的知道引入了一个类,因此不会出现两个类相互引用的冲突。例如在Dog类中要引入Person,Person类中要引入Dog,则只需要把本来在.h文件中引入头文件的语句#import “Person.h”和#import “Dog.h”改为@class Person;和@class Dog;即可。

这样使用@class会产生一个副作用。当通过@class引入一个类时,并不能直接调用这个类的方法和成员变量,因为与#import不同,@class只是告诉编译器有一个类,但是并不会将类的具体内容在编译之前进行引入。解决这个问题的方法是在.m文件中通过#import引入类的.h文件。举例如下:

/******Person.h*****/
#import <Foundation/Foundation.h>
@class Dog;
@interface Person : NSObject
@property Dog *dog;
-(void)hitDog;
@end
/******Person.m*****/
#import "Person.h"
#import "Dog.h"
@implementation Person
-(void)hitDog{
    [_dog bark];
}
- (void)dealloc
{
    _dog = nil;
    NSLog(@"Person已经被销毁");
}
@end
/******Dog.h*****/
#import <Foundation/Foundation.h>
@class Person;
@interface Dog : NSObject
@property Person *person;
-(void)bark;
@end
/******Dog.m*****/

#import "Dog.h"

@implementation Dog
-(void)bark{
    NSLog(@"汪汪汪!");
}
- (void)dealloc
{
    NSLog(@"Dog已经被销毁");
}
@end

上述代码通过使用@class解决了循环引用编译器报错的问题,同时在.m文件中用#import引入类的.h文件,使得类的方法可以在引入该类的类中使用。

但是新的问题又出现了,如果在main函数中执行如下代码:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *p = [Person new];
        Dog *d = [Dog new];
        p.dog = d;
        d.owner = p;
        [p.dog bark];
    }
    return 0;
}

程序执行完内层花括号后并没有自动调用Dog和Person的dealloc方法。当程序执行到内层花括号结束部分时,首先会销毁局部变量p和d,此时p.dog依然作为强指针指向Dog对象,d.owner依然作为强指针指向Person对象。因为两个对象都有强指针指向,所以不会被销毁,这样就会造成内存泄漏。

解决方法有两个:

  1. 在两个类相互引用时,将其中一个设置为弱指针,即在@property参数中设置为weak。
  2. 在代码块结束前,手动将相互引用的对象的其中一个成员变量指向nil。

五、ARC项目兼容MRC

要让ARC项目兼容MRC对象,只需要执行如下操作:
在创建target的界面下->Build Phases->Compile Sources->选择MRC文件双击->在Complier Flags中填写-fno-objc-arc

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值