C#, Java, Objective-C (iOS 5以前) 三種語言的 Property與Field 介紹
C#, Java, Objective-C (iOS 5以前)的 Property與Field 介紹
很久以前就想寫這篇文章了,在我得知我都亂用Objective-C的Property之後Orz
下面是我的心得,不一定正確
在物件導向的世界有兩個Term,field跟property。定義不知道是不是field只能給自己人使用,而property則是此物件要讓外部的Class存取的屬性。原本property的用意就是封裝,不讓外部的元件亂設定自己的屬性,或是你要設定之前,我再幫你檢查一次,ex: Id (身分證) 這個欄位,你可能就只能設定某些值~
首先是C#
private string _Msg; //這是field
public string Msg //這是property
{
get //getter
{
return _Msg;
}
set //setter
{
_Msg = value;
}
}
上面可以看到 property的寫法很累綴,所以C#又有另一種寫法
public string Msg
{ get; set; }
這樣子你也不用另外定義一個 field了。由Compiler自動幫你產生。而外部使用的時候則是
classA.Msg = "Hello";
return classA.Msg;
這裡我很推薦的就是C#是使用 "=" 來呼叫 setter的,這樣直覺多了。也因為這個原因,我有時候會不使用property,而直接使用field。
public string Msg;
自己的想法是,外部元件使用ClassA時是一樣的寫法(IntelliSense會看到不一樣),然後我也不需要filter,所以不必為了property而property。不知道這樣設計會有甚麼問題?
接下來是Java
public class ClassA{
string Msg; //這是field
public string getMsg() //這是property
{
return Msg;
}
public void setMsg(string _Msg)
{
Msg = _Msg;
}
}
Java 很嚴謹Orz,所以要使用的時候,一定要使用function的呼叫
classA.getMsg();
classA.setMsg("Hello");
我自己是覺得這種寫法很麻煩啦,每次都要多打一個get / set,然後某些屬性又沒有get, ex: Array.size() or xxx.length() 這裡我不太確定他的函式命名方式,因為readonly property就不用加上get嗎?
所以我有時候還是偷懶直接用field
public String Msg;
第三個是 Objective-C
新的iOS 5 不適合下面的寫法了,因為有新的記憶體管理方式
要先介紹的是在iOS 5以前,iOS是使用 Reference Count的記憶體管理方式,所以如果你要用這個object的話,你就要記得幫他refCount+1,不用時 -1,當他的refCount=0時就會自動釋放。
classA.h
@interface ClassA: NSObject {
NSString *Msg; //這是field
}
- (NSString*)getMsg; //這是getter property
- (void)setMsg:(NSString*)_Msg; //這是setter property
乍看之下跟Java很像,當然如果你都是用這種手工的方式就沒有甚麼問題。自己實作的Property Function記得要 add refCount
呼叫的寫法就是
[classA setMsg:@"Hello"];
NSString *Tmp = [classA getMsg];
但後來Compiler提供了快速宣告的做法
ClassA.h
@interface ClassA: NSObject {
NSString *Msg; //這是field
}
@property (nonatomic, retain) NSString *Msg //這一行就是幫你產生上面的例子的那兩個Property Function 的宣告
ClassA.m
@synthesize Msg
@synthesize則是幫你產生 Property Function的實作。
Objective-C 也提供了"."的語法
self.A = @"Hello"; Compiler 會轉為 [self setA:@"Hello"];
return self.A 則是轉為return [self A];
到這裡用法應該沒甚麼問題,但該死的就是 Reference Count 的設計,他的理念是,如果你想繼續使用某個物件的話,你就要記得幫他+1,不再用了之後就要-1。所以在自動產生的setter第一行就會作這件事情。
所以如果你直接用 Msg = anotherMsg; 跟 self.Msg = anotherMsg; 是不一樣的,一個有呼叫setter,一個沒有,如果anotherMsg被釋放之後你再用他就會掛掉了。
第二種情況則是釋放不完全,浪費記憶體空間
self.classB = [ClassB new];
new的時候剛產生出來的物件 refCount 是1,丟到 setter 之後,會變成2,將來不再使用的時候,呼叫 self.classB = nil,會將其 refCount-1 變成1,但永遠不會變成0被釋放了Orz,因為沒有指標指到ClassB剛new出來的那塊記憶體了。
所以我後來的寫法都改成
self.classB = [[ClassB new] autorelease];
目前是習慣大都加上 self. 來使用property,幾乎不用 field了。但還是要提一下另一種寫法,好像是從C++傳過來的
ClassA.h
@interface ClassA: NSObject {
NSString *_Msg; //這是field
}
@property (nonatomic, retain) NSString *Msg //這一行就是幫你產生上面的例子的那兩個Property Function 的宣告
ClassA.m
@synthesize Msg = _Msg;
就是field跟property不要用相同的名字,這樣如果你忘了寫"self." ,Compiler就會通知你。但我是都沒用這樣啦,覺得有點麻煩~
最後我發現沒宣告field也可以。所以下面是我的最終版:
ClassA.h
@interface ClassA: NSObject {
}
@property (nonatomic, retain) NSString *Msg //這一行就是幫你產生上面的例子的那兩個Property Function 的宣告
ClassA.m
@synthesize Msg;
直接宣告 Property就好~
題外話:
不知道有沒有人跟我一樣之前覺得Objective-C的function 定義很奇怪的,我後來終於想到有一個唯一的好處
如果我有一個function,需要接收兩個 string 的參數,然後第二個string argument有可能是不同的意義,在C#是做不到的,因為它只用parameter type去作判斷。
- (void)GetWebContent:(NSString*)Url RefererUrl:(NSString*)RefererUrl
- (void)GetWebContent:(NSString*)Url LoginUrl:(NSString*)LoginUrl
留言