如何避免循环引用造成的内存泄漏

 我来答
huanglenzhi
2015-04-24 · 知道合伙人数码行家
huanglenzhi
知道合伙人数码行家
采纳数:117538 获赞数:517165
长期从事计算机组装,维护,网络组建及管理。对计算机硬件、操作系统安装、典型网络设备具有详细认知。

向TA提问 私信TA
展开全部
  以delegate模式为例(viewcontroller和view之间就是代理模式,viewcontroller有view的使用权,viewcontroller同时也是view的代理(处理view中的事件)):
  

  UserWebService.h

  #import

  //定义一个ws完成的delegate

  @protocol WsCompleteDelegate

  @required

  -(void) finished;//需要实现的方法

  @end

  @interface UserWebService:NSObject

  {

  id delegate;//一个id类型的dategate对象

  }

  @property (assign) id delegate;

  -(id)initWithUserData:(User *)user;

  -(void)connectionDidFinishLoading:(NSURLConnection *)connection;

  @end

  UserWebService.m:

  #import

  @systhesize delegate;//同步这个delegate对象

  @implementation UserWebService

  -(void)connectionDidFinishLoading:(NSURLConnection *)connection

  {

  [delegate finished]

  }

  @end
  复制代码

  LoginViewController.h:
  

  #import "UserWebService.h" //包含含有委托的头文件

  @interface LoginViewController:UIViewController

  -(void)submit;

  @end

  LoginViewController.m:

  @implementation LoginViewController

  -(void) submit

  {

  User *user = [[User alloc]init];

  [user setUserId:@"username"];

  [user setPassword:@"password"];

  ws = [[UserWebService alloc] initWithUserData:user];

  ws.delegate = self;//设置委托的收听对象

  [user release];

  [ws send];

  }

  //实现委托中的方法,

  -(void) finished

  {

  NSAttry *users = [ws users];

  }

  @end
  复制代码

  可以看到,delegate声明为assign:
  

  @property (assign) id delegate;
  复制代码

  如果声明为retain会如何?
  

  LoginViewController alloc了一个UserWebService,UserWebService的代理又是LoginViewController,这样就循环引用了:

  ws = [[UserWebService alloc] initWithUserData:user]; //UserWebService对象引用计数加1

  ws.delegate = self;//LoginViewController对象引用计数加1
  复制代码

  外部框架allocLoginViewController对象后,LoginViewController对象的引用计数为2 ,release后还是无法销毁,产生内存泄漏

  所以用assign而不是retain声明属性可以避免循环引用,ARC下用弱引用也可以解决

  内存检测可以用xcode继承的instrument工具,不过循环引用引起的内存泄露是检测不出来的

  对象release到引用计数为0后,如果对应指针没有赋值为nil,怎出现野指针
  

  ClassA *a = [[ClassA alloc] init];
  复制代码

  a = nil;//alloc的内存没有任何对象可以控制它了,引用计数永远为1,这就造成了内存泄露

  简单的赋值操作并不会改变对象的引用计数:
  

  ClassA *a = [[ClassA alloc] init];

  ClassA *b = a;//a和b指向的对象的引用计数还是1
  复制代码

  @property:

  默认为@property为@property(atomic,assign)

  nonatomic: 没有对应的atomic关键字,即使上面是这么写,但atomic叧是在你没有声明这个特性的时候系统默认,你无法主动去声明这一特性。nonatomic不支持多线程访问,atomic有同步机制,支持多线程访问,如果需要多线程访问,声明为atomic(维持默认),否则声明为nonatomic,因为nonatomic效率比atomic高得多

  关于assign、retain和copy: assign是系统默认的属性特性,它几乎适用亍OC的所有变量类型。对于非对象类型的变量,assign是唯一可选的特性。但是如果你在引用计数下给一个对象类型的变量声明为assign,那么你会在编译的时候收到一条来自编译器的警告。因为assign对于在引用计数下的对象特性,叧创建了一个弱引用(也就是平时说的浅复制)。返样使用变量会很危险。当你release了前一个对象的时候,被赋值的对象指针就成了无头指针了。因此在为对象类型的变量声明属性的时候,尽量少(或者不要)使用assign。

  关于assign合成的setter,看起来是这样的:
  

  -(void)setObjA:(ClassA *)a {

  objA = a;

  }
  复制代码

  在深入retain之前,先把声明为retain特性的setter写出来:
  

  -(void)setObjA:(ClassA *)a

  {

  If(objA != a)

  {

  [objA release];

  objA = a;

  [objA retain]; //对象的retain count 加1

  }

  }
  复制代码

  明显的,在retain的setter中,变量retain了一次,那么,即使你在程序中 self.objA = a; 只写了这么一句,objA仍然需要release,才能保证对象的retain count 是正确的。但是如果你的代码 objA = a; 叧写了这么一句,那么这里只是进行了一次浅复制,对象的retain count 并没有增加,因此这样写的话,你不需要在后面release objA。 这2句话的区别是,第一句使用了编译器生成的setter来设置objA的值,而第二句叧是一个简单的指针赋值
  

  NSString *str = [[NSString alloc] initwithstring @“abc”];

  str = @“abcd”;‘

  [str release];

  NSLog("%@",str);//打印出abcd
  复制代码

  str为什么没有变成野指针呢?因为字符串常量(包括NSString和@“......”)的引用计数很大(100K+),基本上不会释放掉(由OC自己管理),所以字符串常量可以不用release
本回答被提问者和网友采纳
已赞过 已踩过<
你对这个回答的评价是?
评论 收起
推荐律师服务: 若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询

为你推荐:

下载百度知道APP,抢鲜体验
使用百度知道APP,立即抢鲜体验。你的手机镜头里或许有别人想知道的答案。
扫描二维码下载
×

类别

我们会通过消息、邮箱等方式尽快将举报结果通知您。

说明

0/200

提交
取消

辅 助

模 式