Objective - C基础: 第五天 - 6.循环引用

来源:互联网 发布:杭州学淘宝哪家比较好 编辑:程序博客网 时间:2024/06/15 03:29

在前面, 我们知道了OC的内存管理以及@property的参数, 其实还有一个叫做循环引用, 其实所谓的循环引用很简单, 就是两个类中, 你包含我, 我包含你, 这个就叫循环引用, 比如下面这个例子:


#import <Foundation/Foundation.h>#import "Car.h"@interface Person : NSObject@property (nonatomic, retain) Car *car;@end

#import <Foundation/Foundation.h>#import "Person.h"@interface Car : NSObject@property (nonatomic, retain)Person *person;@end






这样子就是循环引用, 但这个例子里的循环引用是有性能问题的, 需要改进, 我们来看看改进之后的例子:

#import <Foundation/Foundation.h>@class Car;@interface Person : NSObject@property (nonatomic, retain) Car *car;@end

#import <Foundation/Foundation.h>@class Person;@interface Car : NSObject@property (nonatomic, retain)Person *person;@end

为什么这么做呢? 我们来回想一下, #import的作用就是引用头文件, 如果在我们平常的简单编程里面, 直接这么做是没问题的, 但如果在几十个几百个类中循环引用, 一旦某个被循环引用的类修改了一点点, 那么全部引用它的类就要重新再引用, 就会造成性能问题, 所以我们这里使用@class.



@class的作用仅仅只是告诉编译器XXX是一个类, 我们可以使用, 不像#import那样直接引用, 但@class也并不是完美的, 如果我们使用手动引用计数, 需要在.m文件里release对象的话, 那么就需要使用到#import来辅助一下, 比如:

#import "Person.h"#import "Car.h"@implementation Person- (void)dealloc{    [_car release];    [super dealloc];}@end

#import "Car.h"#import "Person.h"@implementation Car- (void)dealloc{    [_person release];    [super dealloc];}@end


那么@class#import的区别在哪里? 我们来看看区别:

#improt 方法会包含被引用类的所有信息, 包括被引用类的变量和方法;
@class 方式只是告诉编译器在X.h文件中X *x只是类的声明, 具体这个类里面有什么信息, 这里不需要知道, 等实现文件中真正要用到时, 才会真正去查看X类中信息




总结
1. 如果有上百个头文件都#import了同一个文件, 或者这些文件一次被#import, 那么一旦最开始的文件稍有改动, 后面引用到这个文件的所有类都需要重新编译一边, 这样的效率可想而知, 而相对来讲, 使用@class方式就不会出现这种问题了.


2. 在.m实现文件中, 如果需要引用到被引用类的实例变量或者方法时, 还需要使用#import方法引入被引用类.





再回头看看我们的例子, 我们会发现, 该例子会有循环retain, 什么是循环retain呢? 其实循环retain一般只会出现在循环引用上, 当两个类相互引用并且使用retain的时候, 那么谁也不会被释放, 这样子就会造成内存泄漏, 比如:

#import <Foundation/Foundation.h>#import "Person.h"#import "Car.h"int main(int argc, const char * argv[]){    Person *p = [[Person alloc] init];    Car *c = [[Car alloc] init];        p.car = c;    c.person = p;        NSLog(@"p = %ld", [p retainCount]);    NSLog(@"c = %ld", [c retainCount]);        [c release];    [p release];        return 0;}

打印出来的结果:

2015-01-27 12:23:23.885 07.循环引用[6274:661345] p = 22015-01-27 12:23:23.886 07.循环引用[6274:661345] c = 2



有人会想到, 简单啦, 直接写两次release就可以啦, 没错是可以这么做, 但这么做不符合apple的内存管理原则, 我们之前说过内存管理的原则是一次alloc一次release, 所以该做法不可取.


解决办法其实也很简单, 只要把其中一个类改成assign就可以解决了, 比如:

#import <Foundation/Foundation.h>@class Car;@interface Person : NSObject@property (nonatomic, assign) Car *car;@end

#import <Foundation/Foundation.h>@class Person;@interface Car : NSObject@property (nonatomic, retain)Person *person;@end



在看看main()函数打印出来的结果:

2015-01-27 12:27:19.038 07.循环引用[6316:663154] p = 22015-01-27 12:27:19.039 07.循环引用[6316:663154] c = 12015-01-27 12:27:19.039 07.循环引用[6316:663154] Car被释放了2015-01-27 12:27:19.039 07.循环引用[6316:663154] Person被释放了



两端互相引用造成内存泄漏的解决方案: 把其中一端的retain改成assign.





好了, 这次我们就讲到这里, 下次我们继续~~

0 0
原创粉丝点击