1. 程式人生 > >第20條:為私有方法名加前綴

第20條:為私有方法名加前綴

avi 機制 warning 習慣 動態 tina 小寫 blog 特性

  本條要點:(作者總結)

  • 給私有方法的名稱加上前綴,這樣可以很容易地將其同公共方法區分開。
  • 不要單用一個下劃線做私有方法的前綴,因為這樣做法是預留給蘋果公司用的。

  一個類所做的事情通常都要比從外面看到的更多。編寫類的實現代碼時,經常要寫一些只在內部使用的方法。筆者建議,應該為這種方法的名稱加上某些前綴,這有助於調試,因為據此很容易就能把公共方法和私有方法區別開。

  為私有方法名加前綴還有個原因,就是便於修改方法名或方法簽名。對於公共方法來說,修改其名稱或簽名之前要三思,因為類的公共 API 不便隨意改動。如果改了,那麽使用這個類的所有開發者都必須更新其代碼才行。而對於內部方法來說,若要修改其名稱或簽名,則只需要同時修改本類內部的相關代碼即可,不會影響到面向外界的那些 API。用前綴把私有方法標出來,這樣很容易就能看出哪些方法可以隨意修改,哪些不應輕易改動。

  具體使用何種前綴可根據個人喜好來定,其中最好包含下劃線與字母p。筆者喜歡用 p_ 作為前綴,p 表示 “private”(私有的),而下劃線則可以把這個字母和真正的方法名區隔開。下劃線後面的部分按照常用的駝峰法來命名即可,其首字母要小寫。例如,包含私有方法的 EOCObject 類可以這樣寫:

 1 #import <Foundation/Foundation.h>
 2 
 3 @interface EOCObject : NSObject
 4 
 5 - (void)publicMethod;
 6 
 7 @end
 8 
 9 #import "EOCObject.h
" 10 11 @implementation EOCObject 12 13 - (void)publicMethod { 14 /* ... */ 15 } 16 17 - (void)p_privateMethod { 18 /* ... */ 19 } 20 21 @end

  與公共方法不同,私有方法不出現在接口定義中。有時可能要在 “class-continuation 分類”裏聲明私有方法,然而最近修訂的編譯器已經不要求在使用方法前必須先行聲明了。所以說,私有方法一般只在實現的時候聲明。

  如果寫過 C++ 或 Java 代碼,你可能就會問了:為什麽要這樣做呢?直接把方法聲明成私有的不就好了嗎?Objective-C 語言沒有辦法將方法標為私有。每個對象都可以響應任意消息,而且可在運行期檢視某個對象所能直接響應的消息。根據給定的消息查出其對應的方法,這一工作要在運行期才能完成,所以 Objective-C 中沒有那種約束方法調用的機制用以限定誰能調用此方法、能在哪個對象上調用此方法以及何時能調用此方法。開發者會在命名慣例中體現出 “私有方法”等語義。新手也許不適應這一點,但是必須用心領悟 Objective-C 語言這種強大的動態特性。想掌握其動態特性,確實得花大功夫,不過培養良好的命名習慣也是一條成功之道。

  蘋果公司喜歡單用一個下劃線作為私有方法的前綴。你或許也想照著蘋果公司的辦法只拿一個下劃線作前綴,這樣做可能會惹來大麻煩:如果從蘋果公司提供的某個類中繼承了一個子類,那麽你在子類裏可能會無意間覆寫了父類的同名方法。鑒於此,蘋果公司在文檔中說,開發者不應該單用一個下劃線做前綴。不能將方法限定於某個範圍內,這也許是 Objective-C 的缺點,然而作為 “動態方法派發系統”(dynamic method dispatch system)這個強大組件的一部分,此特性也帶來了諸多好處。

  你或許覺得剛才提到的那種情況不太常見,其實未必。例如,要在 iOS 應用程序中創建一個視圖控制器,就得編寫 UIViewController 的子類。自定義的視圖控制器裏可能保存著許多狀態消息。你可能想編寫一個方法,當視圖出現在屏幕上時,可經由此方法把控制器裏的所有狀態都重置一遍。於是,該方法的實現代碼也許會寫成這樣:

 1 #import <UIKit/UIKit.h>
 2 
 3 @interface EOCViewController : UIViewController
 4 
 5 @end
 6 
 7 
 8 #import "EOCViewController.h"
 9 
10 @interface EOCViewController ()
11 
12 @end
13 
14 @implementation EOCViewController
15 
16 - (void)_resetViewController {
17     // Reset state and views
18     
19 }
20 
21 - (void)viewDidLoad {
22     [super viewDidLoad];
23     // Do any additional setup after loading the view.
24 }
25 
26 - (void)didReceiveMemoryWarning {
27     [super didReceiveMemoryWarning];
28     // Dispose of any resources that can be recreated.
29 }
30 
31 /*
32 #pragma mark - Navigation
33 
34 // In a storyboard-based application, you will often want to do a little preparation before navigation
35 - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
36     // Get the new view controller using [segue destinationViewController].
37     // Pass the selected object to the new view controller.
38 }
39 */
40 
41 @end

  可問題是,UIViewController 類本身其實已經實現了一個名叫 _resetViewController 的方法了!如果這樣寫的話,那麽所有調用都將執行子類中的這個方法,本來該調用超類方法的地方現在調用的卻是 EOCViewController 中覆寫過的這個版本。由於超類中的同名方法並未對外公布,所以除非深入研究這個庫,否則你根本不會察覺到自己在無意間覆寫了這個方法。這畢竟是個用下劃線開頭的私有方法,所以沒有對外公布也是合理的。由於超類方法永遠不可能執行,所以這個視圖控制器的行為會很奇怪,到時你可能會納悶:為什麽子類的這個方法調用得這個頻繁呢,按道理不應該執行這麽多次呀?

  總之,在確定使用了前綴的情況下,如果子類所繼承的那個類既不在蘋果公司的框架中,也不在你自己的項目中,而是來自別的框架,那麽除非該框架在文檔中明示,否則你無法知道其私有方法所加的前綴是什麽。此時可以把自己一貫使用的類名前綴用作子類私有方法的前綴,這樣能有效避免重名問題。同時還應該考慮到其他人會如何從你所寫的類中繼承子類,這也是私有方法應該加前綴的原因。除非使用一些相當復雜的工具,否則,在沒有源代碼的情況下,無法知道某個類在其公共接口之外還 定義並實現了哪些方法。

  END

  

  

  

第20條:為私有方法名加前綴