objc--對於Retain和Assign屬性的理解
在寫程式時,對於要定義的變數進了習慣於加上retain屬性,但對其到底起到什麼作用卻一直不是很明白,今天做了一個demo,終於弄清了他們的意思。
比如我要定義一下字串var:
NSString *var;
宣告Property時,@property(nonamtic,assign)NSString *var;
將屬性宣告為Assign,當使用@Synthsize生成Getter和Setter方法後,我們就可以用“.”操作符來為var賦值,
將屬性宣告為Assign時,Setter方法的實現是這樣的:
-(void)setter:(NSString*)str
{
var=str;
}
var=[[NSString alloc]initWithString:@"aaa"];
當我們這樣用時,沒有呼叫Setter方法,只是將var指向目標地址,這時
NSLog(@“%d”,[var retainCount]);的值會是1。
如果我們這樣寫:self.var=[[NSString alloc]initWithString:@"aaa"];
這時會呼叫setter方法,但是NSLog(@“%d”,[var retainCount]);的值仍然是1。
如果我們在設定屬性時這樣來設定:
@property(nonamtic,retain)NSString *var;
這時,自動生成的Setter方法是這樣的:
-(void)Setter:(NSString*)str
{
[str retain];
[var release];
var=str;
}
這樣就一目瞭然了,也就是被設定為retain屬性的方法,在生成Setter方法時,先將要賦的值的引用計數加1,然後將var指向的物件release。再後才是將str賦值給var。這時下面的程式碼的輸出結果會是2,因為,在Setteryyif中[[NSString alloc]initWithString:@"aaa"]所指的記憶體區域的retain被加了1,然後var又指向這個區域,所以var的retainCount自然就為2了:
self.var=[[NSString alloc]initWithString:@"aaa"];
NSLog(@“%d”,[var retainCount]);
這樣做是為了防止記憶體被過渡釋放,比如,[[NSString alloc]initWithString:@"aaa"]這個記憶體區域不光有var指向,還有另外一個變數var2也指向這個區域,如果我們不設定成retain屬性,在執行var=[[NSString alloc]initWithString:@"aaa"];後,var和var1都指向一個共同的記憶體區域,但這個記憶體區域的retainCount為1。如果在另一個地方執行了[var2 release];這樣[[NSString alloc]initWithString:@"aaa"]所指的記憶體區域的retainCount為0,所以這個記憶體區域就被釋放了,var也就成了野指標,這時再引用var將會出現記憶體錯誤。
但是,需要特別注意的是,即使我們設定var的屬性為retain,如果我們在為var賦值時,使用的是如下形式:
var=[[NSString alloc]initWithString:@"aaa"];
而不是:
self.var=[[NSString alloc]initWithString:@"aaa"];
這樣retain屬性是不起作用的,前面不加self.,相當於我們定義的屬性還是assign。retainCount不會加1的。
我個人對這個的理解是,如果不顯示的使用"."操作符,Setter方法是不呼叫的,僅僅是指標的傳遞。
今天又有了點新的想法,補充一下:
"."操作符在OBJC中是方法的呼叫,比如:self.str和[self str]是一樣的。如果我在.h檔案中聲明瞭一個方法:
-(void)method;
那麼,我呼叫這個方法可以用兩種方式:[self method]或self.method。
這樣說的話,我們為什麼可以用諸如self.str這樣的形式來表示一個變數呢,原因就在於OBJC中變數屬性的機制。
我們前面說過,定義一個變數str,加個assign或retain之類的屬性後,再用@synthesize就可以生成相應的setter和getter方法了。這樣,對於一個變數,就有了相應的賦值方法,於是,對於self.str這樣的寫法,實際上就是呼叫了str對應的setter或getter方法。換句話說,也是把setter或getter訊息傳送給和str。str這時就是一個方法名,而不僅僅是變數名了。
所以如果我們沒有對一個變數宣告屬性,也沒有@synthesize來生成setter和getter方法,那麼我們就不能用self.str這種形式,而只能用[email protected]"aaa",或者str1=str這樣的形式來使用變數。
於是也就有了我們前面的結論:用self.str這種形式,相當於呼叫setter或getter方法。也就會執行retain的操作。不用這種形式,就沒用呼叫setter或getter方法,retain的操作也就無從談起。
轉一個另一篇文章,講的比較好:
最近有人問我關於什麼時候用self.賦值的問題, 我總結了一下, 發出來給大家參考. 有什麼問題請大家斧正.
關於什麼時間用self. , 其實是和Obj-c的存取方法有關, 不過網上很多人也都這麼解答的, 那它為什麼和存取方法有關? 怎麼有關的? 並沒有多少人回答出來. 同時關於記憶體管理的內容, 請大家看旺財勇士的Objective-C記憶體管理總結~CC專版 , 有些東西我就不多解釋了.
進入正題, 我們經常會在官方文件裡看到這樣的程式碼:
MyClass.h
@interface MyClass : NSObject {
MyObject *myObject;
}
@property (nonatomic, retain) MyObject *myObject;
@end
MyClass.m
@synthesize myObject;
-(id)init{
if(self = [super init]){
MyObject * aMyObject = [[MyObject alloc] init];
self.myObject = aMyObject;
[aMyObject release];
}
return self;
}
有人就問, 為什麼要這麼複雜的賦值? 為什麼要加self. ? 直接寫成self.myObject = [[MyObject alloc] init];不是也沒有錯麼? 不加self有時好像也是正常的?
現在我們來看看記憶體管理的內容:
先看間接賦值的:
1.加self.
MyObject * aMyObject = [[MyObject alloc] init]; //aMyObject retainCount = 1;
self.myObject = aMyObject; //myObject retainCount = 2;
[aMyObject release];//myObject retainCount = 1;
2. 不加self.
MyObject * aMyObject = [[MyObject alloc] init]; //aMyObject retainCount = 1;
myObject = aMyObject; //myObject retainCount = 1;
[aMyObject release];//物件己經被釋放
再看直接賦值的:
3.加self.
self.myObject = [[MyObject alloc] init]; //myObject retainCount = 2;
4. 不加self.
myObject = [[MyObject alloc] init]; //myObject retainCount = 1;
現在是不是有點暈, 我們先來把程式碼改一下, 官方的一種常見寫法:
MyClass.h
@interface MyClass : NSObject {
MyObject * _myObject;
}
@property (nonatomic, retain) MyObject *myObject;
@end
MyClass.m
@synthesize myObject = _myObject;
OK, 你現在再試下, 如果你用self._myObject = aMyObject; 或者 myObject = aMyObject; 你會得到一個錯誤, 為什麼呢, 這裡就是和Obj-c的存取方法有關了. 說白了很簡單 , 大家都知道, @property (nonatomic, retain) MyObject *myObject; 是為一個屬性設定存取方法, 只是平時我們用的方法名和屬性名是一樣的,現在你把它寫成不同的名字, 就會很清楚了. _myObject是屬性本身, myObject是存取方法名.
現在我們知道self.是訪問屬性的存取方法了, 那存取方法又怎麼工作的? self.myObject = [[MyObject alloc] init]; 為什麼會有記憶體洩露?
關於nonatomic我不多解釋了, 它不是我要講的重點, 而且我也沒完全搞清楚, 不誤導大家. 我只說assign, retain ,copy.
get方法是:
-(MyObject*)myObject{
return _myObject;
}
Set方法是:
// assign
-(void)setMyObject:(id)newValue{
_myObject = newValue;
}
// retain
-(void)setMyObject:(id)newValue{
if (_myObject != newValue) {
[_myObject release];
_myObject = [newValue retain];
}
}
// copy
-(void)setMyObject:(id)newValue{
if (_myObject != newValue) {
[_myObject release];
_myObject = [newValue copy];
}
}
其實這些方法裡還有別的內容, 並不只是這些. 而且這些方法可以被重寫. 比如你寫一個
-(MyObject*)myObject{
return _myObject;
}
放在你的類裡, 你呼叫self.myObject時(不要把它放在等號左邊, 那會呼叫get方法)就會呼叫這個方法.
這裡多說一句, @property 是為你設定存取方法, 和你的屬性無關, 你可以只寫一句
@property (readonly) NSString *name;
在你的類裡實現
-(NSString*)name{
NSLog(@"name");
return @"MyClass";
}
同樣可以用self.name呼叫.
現在回頭說說我們開始的那四個賦值, 當不用self.的時候, 那句話只是一般的賦值, 把一個指標賦給另一個指標, 不會對分配的記憶體有任何影響, 所以2中不要最後[aMyObject release];這句話和4是一回事. 這裡就不多說了.我們看看1和3,
當呼叫setMyObject:方法時, 對newValue 做了一次retain操作, 我們必須把原來的newValue釋放掉, 不然就會記憶體洩露, 在1裡, 我們有個aMyObject可以用來釋放, 在3裡, 我們無法釋放它, 所以, 在3裡, 我們會多出來一個retainCount. 記憶體洩露了.
說了這麼多, 我只想讓大家清楚, 什麼是呼叫屬性本身, 什麼是呼叫存取方法. 怎麼樣才能避免記憶體洩露, 而且, 以上例子裡是在自己類裡的呼叫, 如果這個類被別的類呼叫時, 更要注意一些,
順便說一下, 如果你想在其它類訪問物件屬性, 而不是通過存取方法, 你可以用myClass -> myObject來訪問, 這樣是直接訪問物件本身, 不過你先要把myObject設成@public. 但這個是官方不提倡的,
程式碼比較簡單, 我還是發出來, 高人們可以忽略了.
相關推薦
objc--對於Retain和Assign屬性的理解
在寫程式時,對於要定義的變數進了習慣於加上retain屬性,但對其到底起到什麼作用卻一直不是很明白,今天做了一個demo,終於弄清了他們的意思。 比如我要定義一下字串var: NSString *var; 宣告Property時,@property(nonamtic,assign)NSString
對於PrintStream 和PrintWriter的理解 以及各種編碼格式 以及編碼和解碼的解釋
PrintStream: 是一個位元組列印流,System.out對應的型別就是PrintStream。 它的建構函式函式可以接收三種資料型別的值。 1,字串路徑。 2,File物件。 3,OutputStream。 PrintWriter: 是一
個人對於陣列和緩衝區的理解
參考文章:http://blog.csdn.net/zhangyuan19880606/article/details/51162096 概念上,緩衝區是包在一個物件內的基本資料元素陣列。Buffer類相比一個簡單陣列的優點是它將關於資料的資料內容和
對於softmax和sigmoid的理解
在分類問題中,人們常常會用到sigmoid和softmax這兩個啟用函式。 sigmoid:A logistic function or logistic curve is a common “S” shape (sigmoid curve). 也就是說,s
對於equal和hashcode的理解,何時需要重寫
重寫equal 的同時為什麼必須重寫hashcode? hashCode是編譯器為不同物件產生的不同整數,根據equal方法的定義:如果兩個物件是相等(equal)的,那麼兩個物件呼叫hashCode必須產生相同的整數結果,即:equal為true,hashCode必須
對於Hibernate和JPA的理解
前言 在學習EJB的過程中,碰到了持久化這一概念,前面我一直沒弄清楚JPA和Hibernate的關係,隨著查閱的資料越多,我逐漸弄清了這三者的關係。 概念 持久化:將內容中的物件儲存到磁碟這類永久儲存器中,在需要的時候可以將其調回記憶體還原為物件。 ORM:是一種程式
對於Intent和IntentFilter的理解記錄
來自:http://blog.csdn.net/fhx123fhx123/article/details/7906113 來個通俗的比方: Intent比作一個人 IntentFilter比作一個飯店 1、如果一個人想吃東西(Intent的action),而酒店中
每天一個JS 小demo之原生數組splice方法書寫。主要知識點:鍛煉思維邏輯能力,對於數組方法的理解和各種情況的考量
scrip charset 是否 isn 如果 情況 del 當前 cti <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <titl
java中如何使用空參構造方法自動生成不同名字的對象,使用非靜態的屬性和靜態屬性有什麽區別,原因是什麽?如何理解static關鍵字
區別 關鍵字 內部 方法 屬性 count per setname person 空參構造自動生成對象時,使用非靜態的屬性 代碼: package com.swift; //使用無參構造方法自動生成對象,序號不斷自增 public class Person { p
python私有方法和私有屬性屬性理解
__init__ out code 避免 col 系統 import name sizeof 私有屬性、方法——Python並沒有真正的私有化支持,但可用下劃線得到偽私有盡量避免定義以下劃線開頭的變量 (1)_xxx "單下劃線 " 開始的成員變量叫做保護變量
網上整理的對於Rest和Restful api的理解
gpo 信息 常用 method 安全 什麽 獲取 正常 stat 一、什麽是Rest? REST不是"rest"這個單詞,而是幾個單詞縮寫 -- REpresentational State Transfer 直接翻譯:表現層狀態轉移,但這個翻譯正常人根本看不懂,找到的一
對於分類回歸樹和lightgbm的理解
利用 區分 OS 改進 假設 成了 數據 ima size 在分類回歸樹中之所以要先分類後回歸的原因是, 對於一般的線性回歸是基於全部的數據集。這種全局的數據建模對於一些復雜的數據來說,其建模的難度會很大。所以我們改進為局部加權線性回歸,其只利用數據點周圍的局部數據進行建模
非常易於理解‘類'與'對象’ 間 name 屬性 引用關系,暨《Python 中的引用和類屬性的初步理解》讀後感
我想 就是 spa 發生 來看 初步 img 一個 同名 關鍵字:名稱,名稱空間,引用,指針,指針類型的指針(即指向指針的指針) 我讀完後的理解總結: 1. 我們知道,python中的變量的賦值操作,變量其實就是一個名稱name,賦值就是將name引用到一個objec
筋斗雲案例—理解動畫和offsetLeft屬性
要點: 1.為通過迴圈遍歷的方式,為同組的每一個物件定義相同的事件時,不要每次都通過臨時建立一個匿名事件,來將物件繫結事件,而是通過呼叫命名函式的方式 2.這個例子的lastPosition注意定義一個全域性變數,offsetLeft的距離是所呼叫的物件,距離瀏覽器視窗的左邊的距離,不是固定
【Spring篇02】對於SpringMVC核心原理的理解 & @Controller和@RequestMapping註解
之前在【Web篇08】中提到,SpringMVC的核心就是優化了B/S結構(瀏覽器-伺服器),簡化了Servlet的建立; 瀏覽器可以查詢的路徑:WebContent目錄下的jsp檔案,一般在這個目錄下建立個index.jps作為首頁 伺服器可以查詢的路徑:WEB-INF目錄下的jsp
理解springMVC中的Model和Session屬性
作為一個javaweb應用的開發者,你快速學習了request(HttpRequest)和Sess
java 對於包中的public class和class的理解
學習了mldn的視訊教程 裡面對於class和public class做出了一些說明 如下 我一開始不理解,一個非主方法的java類中只能有一個public class麼,那麼常用的包比如java.util中那麼多的類,我查了下文件發現他們幾乎都是public
對於equals和==的理解
很多時候equals和==大家都分不太清楚怎麼樣來使用,今天小編就來教大家怎麼使用 equals比較的是兩個變數的值是否相等 而==則比較的是這個變數的記憶體地址是否相同 打個比方來說 String a = new String("a"); String b = new String ("a");
對於spring中IOC和AOP的理解及程式碼簡單實現
IoC(Inversion of Control): 在傳統的java程式編寫中如果呼叫方法或類者,必須要對被呼叫者進行例項化,即必須要通過new 被呼叫者類,才能對被呼叫者類中的方法、成員進行操作。在開發某一個專案時必然會出現很多類來完善專案的需求,並且類與類
我對於js中apply()和call()和push()的理解
apply()和call()專門用於修改this。 先舉一個小例子: <script> function test(){ console.log(this); } window.test(); //列印的結果為Window,說明此時的this指的是w