說明:這個Objective-C專題,是學習iOS開發的前奏,也為了讓有面向對象語言開發經驗的程序員,能夠快速上手Objective-C。如果你還沒有編程經驗,或者對Objective-C、iOS開發不感興趣,請忽略。學習本專題之前,建議先學習C語言專題。
前言
在Java中,我們可以通過"對象名.成員變量名"來訪問對象的公共成員變量,這個就稱為"點語法"。比如:
1.在Student類的第2行定義了一個公共的成員變量age
1 public class Student { 2 public int age; 3 }
2.然后在第5行通過點語法直接給stu的成員變量age賦值
1 public class Test { 2 3 public static void main(String[] args) { 4 Student stu = new Student(); 5 stu.age = 10; 6 } 7 8 }
當然,正規的做法是讓成員變量私有化,讓外界使用公共的get方法和set方法訪問成員變量。
3.很多高級語言中都有這種點語法,為了讓其他行業的程序員快速上手OC,OC中也引入了點語法,只不過它的含義跟Java不太一樣
一、傳統的get方法和set方法
在正式學習OC的點語法之前,先來看一下傳統的get方法和set方法。定義一個Student類,擁有一個成員變量age和對應的get\set方法。
1.Student.h
1 #import <Foundation/Foundation.h> 2 3 @interface Student : NSObject { 4 int age; 5 } 6 7 - (void)setAge:(int)newAge; 8 - (int)age; 9 10 @end
1> 在第4行定義了一個成員變量age,是@protected權限的,所以外界不能直接訪問它
2> 在第7、8行分別聲明了age變量的set方法和get方法
2.Student.m
1 #import "Student.h" 2 3 @implementation Student 4 5 - (void)setAge:(int)newAge { 6 age = newAge; 7 } 8 9 - (int)age { 10 return age; 11 } 12 13 @end
1> 在第5行實現了set方法
2> 在第9行實現了get方法
3.main.m
把定義好的Student類放到main函數中使用
1 #import <Foundation/Foundation.h> 2 #import "Student.h" 3 4 int main(int argc, const char * argv[]) 5 { 6 @autoreleasepool { 7 Student *stu = [[Student alloc] init]; 8 9 // 設置age的值 10 [stu setAge:10]; 11 12 // 取出age的值 13 int age = [stu age]; 14 15 NSLog(@"age is %i", age); 16 17 [stu release]; 18 } 19 return 0; 20 }
1> 在2行包含Student的頭文件
2> 在第7行創建Student對象,在第17行釋放Student對象
3> 在第10行調用set方法設置age的值
4> 在第13行調用get方法獲取age的值
5> 在第15行輸出age的值,輸出結果如下:
2013-04-08 00:26:19.002 點語法[6164:303] age is 10
這就是OC傳統的get方法和set方法的簡單使用,對初學者來說,這個語法比較奇怪,因為它的方法調用是用方括號[ ]完成的。因此,OC最終引入了點語法。
二、使用點語法代替傳統的get方法和set方法
上面演示了OC傳統get\set方法的簡單用法,接下來使用點語法來代替。
前面main.m中main函數的代碼可以改為:
1 int main(int argc, const char * argv[]) 2 { 3 @autoreleasepool { 4 Student *stu = [[Student alloc] init]; 5 6 // 設置age的值 7 stu.age = 10; // 等價於[stu setAge:10]; 8 9 // 取出age的值 10 int age = stu.age; // 等價於int age = [stu age]; 11 12 NSLog(@"age is %i", age); 13 14 [stu release]; 15 } 16 return 0; 17 }
1.注意第7行代碼,把原來的[stu setAge:10]替換成了stu.age = 10。聽清楚了,這兩種寫法是完全等價的。即這里的stu.age並不是代表直接訪問stu對象的成員變量age,而是編譯器遇到stu.age = 10的時候會自動將代碼展開成[stu setAge:10]
再說,如果是直接訪問成員變量的話,OC中應該是這樣的語法:stu->age,而不是stu.age。
2.注意第10行代碼,把原來的int age = [stu age]替換成了int age = stu.age。這兩種寫法又是完全等價的,stu.age並不是直接訪問stu對象的成員變量age,而是編譯器遇到int age = stu.age的時候會自動將代碼展開成int age = [stu age]
3.因此,OC中點語法的含義跟Java是完全不一樣的,OC點語法的本質是方法調用,不是直接訪問成員變量。至於這個點語法代表的是get方法還是set方法,那就取決於你是取值還是設值,取值就是get方法(如第10行代碼),設值就是set方法(如第7行代碼)。
4.如果你想驗證點語法是不是方法調用的話,有很多方法。
比如你可以在Student.m的set方法和get方法內部用NSLog加一些打印信息,如果程序運行后有輸出打印信息,說明的確是調用了get方法或者set方法
1 #import "Student.h" 2 3 @implementation Student 4 5 - (void)setAge:(int)newAge { 6 NSLog(@"調用了setAge方法"); 7 age = newAge; 8 } 9 10 - (int)age { 11 NSLog(@"調用了age方法"); 12 return age; 13 } 14 15 @end
三、點語法和self的陷阱
1.在Java中,this關鍵字代表着方法調用者,也就是說,誰調用了這個方法,this就代表誰。所以一般會這樣寫set方法:
1 public void setAge(int newAge) { 2 this.age = newAge; 3 }
第2行表示將newAge參數的值,賦值給方法調用者的成員變量age
2.OC中有個self關鍵字,作用跟this關鍵字類似。我這么說完,可能有人就會想這樣寫OC的set方法了:
1 - (void)setAge:(int)newAge { 2 self.age = newAge; 3 }
第2行中的self代表着當前調用setAge:方法的對象。但是第2行代碼是絕對錯誤的,會造成死循環。因為我在前面已經說過了,OC點語法的本質是方法調用,所以上面的代碼相當於:
1 - (void)setAge:(int)newAge { 2 [self setAge:newAge]; 3 }
很明顯,會造成循環調用setAge:方法,程序就這樣崩潰了
四、一點小建議
如果是第一次接觸OC的點語法,你可能會真的以為stu.age的意思是直接訪問stu對象的成員變量age。其實,有一部分原因是因為我這里定義的Student類的成員變量名就叫做age。為了更好地區分點語法和成員變量訪問,一般我們定義的成員變量會以下划線 _ 開頭。比如叫做 _age 。
1.Student.h,注意第4行
1 #import <Foundation/Foundation.h> 2 3 @interface Student : NSObject { 4 int _age; 5 } 6 7 - (void)setAge:(int)newAge; 8 - (int)age; 9 10 @end
2.Student.m,注意第6行和第10行
1 #import "Student.h" 2 3 @implementation Student 4 5 - (void)setAge:(int)newAge { 6 _age = newAge; 7 } 8 9 - (int)age { 10 return _age; 11 } 12 13 @end