如何避免循環引用造成的內存泄漏呢:
以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