iOS 循環引用 委托 (實例說明)


如何避免循環引用造成的內存泄漏呢:

  以delegate模式為例(viewcontroller和view之間就是代理模式,viewcontroller有view的使用權,viewcontroller同時也是view的代理(處理view中的事件)):
  

  1. UserWebService.h
  2.   #import
  3.   //定義一個ws完成的delegate
  4.   @protocol WsCompleteDelegate
  5.   @required
  6.   -(void) finished;//需要實現的方法
  7.   @end
  8.   @interface UserWebService:NSObject
  9.   {
  10.   id delegate;//一個id類型的dategate對象
  11.   }
  12.   @property (assign) id delegate;
  13.   -(id)initWithUserData:(User *)user;
  14.   -(void)connectionDidFinishLoading:(NSURLConnection *)connection;
  15.   @end
  16.   UserWebService.m:
  17.   #import
  18.   @systhesize delegate;//同步這個delegate對象
  19.   @implementation UserWebService
  20.   -(void)connectionDidFinishLoading:(NSURLConnection *)connection
  21.   {
  22.   [delegate finished]
  23.   }
  24.   @end


  LoginViewController.h:
  

  1. #import "UserWebService.h" //包含含有委托的頭文件
  2.   @interface LoginViewController:UIViewController
  3.   -(void)submit;
  4.   @end
  5.   LoginViewController.m:
  6.   @implementation LoginViewController
  7.   -(void) submit
  8.   {
  9.   User *user = [[User alloc]init];
  10.   [user setUserId:@"username"];
  11.   [user setPassword:@"password"];
  12.   ws = [[UserWebService alloc] initWithUserData:user];
  13.   ws.delegate = self;//設置委托的收聽對象
  14.   [user release];
  15.   [ws send];
  16.   }
  17.   //實現委托中的方法,
  18.   -(void) finished
  19.   {
  20.   NSAttry *users = [ws users];
  21.   }
  22.   @end


  可以看到,delegate聲明為assign:
  

  1. @property (assign) id delegate;
復制代碼


  如果聲明為retain會如何?
  

  1. LoginViewController alloc了一個UserWebService,UserWebService的代理又是LoginViewController,這樣就循環引用了:
  2.   ws = [[UserWebService alloc] initWithUserData:user]; //UserWebService對象引用計數加1
  3.   ws.delegate = self;//LoginViewController對象引用計數加1


  外部框架allocLoginViewController對象后,LoginViewController對象的引用計數為2 ,release后還是無法銷毀,產生內存泄漏

  所以用assign而不是retain聲明屬性可以避免循環引用,ARC下用弱引用也可以解決

  內存檢測可以用xcode繼承的instrument工具,不過循環引用引起的內存泄露是檢測不出來的

  對象release到引用計數為0后,如果對應指針沒有賦值為nil,怎出現野指針
  

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


  a = nil;//alloc的內存沒有任何對象可以控制它了,引用計數永遠為1,這就造成了內存泄露

  簡單的賦值操作並不會改變對象的引用計數:
  

  1. ClassA *a = [[ClassA alloc] init];
  2.   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,看起來是這樣的:
  

  1. -(void)setObjA:(ClassA *)a {
  2.   objA = a;
  3.   }


  在深入retain之前,先把聲明為retain特性的setter寫出來:
  

  1. -(void)setObjA:(ClassA *)a
  2.   {
  3.   If(objA != a)
  4.   {
  5.   [objA release];
  6.   objA = a;
  7.   [objA retain]; //對象的retain count 加1
  8.   }
  9.   }


  明顯的,在retain的setter中,變量retain了一次,那么,即使你在程序中 self.objA = a; 只寫了這么一句,objA仍然需要release,才能保證對象的retain count 是正確的。但是如果你的代碼 objA = a; 叧寫了這么一句,那么這里只是進行了一次淺復制,對象的retain count 並沒有增加,因此這樣寫的話,你不需要在后面release objA。 這2句話的區別是,第一句使用了編譯器生成的setter來設置objA的值,而第二句叧是一個簡單的指針賦值
  

  1. NSString *str = [[NSString alloc] initwithstring @“abc”];
  2.   str = @“abcd”;‘
  3.   [str release];
  4.   NSLog("%@",str);//打印出abcd


  str為什么沒有變成野指針呢?因為字符串常量(包括NSString和@“......”)的引用計數很大(100K+),基本上不會釋放掉(由OC自己管理),所以字符串常量可以不用release


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM