2011-11-20 36 views
23

我有一些關於在工作中使用屬性和實例變量的討論,所以我想找到一個wiki的答案。現在,我知道objective-c中沒有真正的私有成員類型,所有內容都非常公開。但是,我有點擔心我們應該如何設計我們的課程,並遵守面向對象的原則。我想聽聽這三種設計方法的意見:私人和公共成員應該如何在objective-c中實現?

答:根據各種職位甚至到斯坦福大學的一個新的iPhone開發課程,你應該隨時隨地使用屬性。然而,恕我直言,這種方法煞車OOP的設計原則,因爲在這種情況下,所有成員公開。爲什麼我需要將所有內部/本地實例變量發佈到外部?此外,如果您通過屬性使用合成設置器,而不是直接使用本地ivar,則會有一些(但仍然)開銷。這裏的一個示例:

//==== header file =====// 
@interface MyClass : NSObject 

@property (nonatomic, retain) NSString *publicMemberWithProperty; 
@property (nonatomic, retain) NSString *propertyForPrivateMember; 

@end 

B.另一種方法是聲明在頭文件的ivars(不宣相關性質)爲私有成員,並且在相同的標題文件,申報純特性(不宣相對的ivars),用於公衆成員。在這種情況下,Ivars將直接在課堂上使用。這種方法是有道理的,但不會使用屬性的所有好處,因爲我們在設置新值之前手動釋放舊值。這裏的一個示例:

//==== header file =====// 
@interface MyClass : NSObject{ 
    NSString *_privateMember; 
} 

@property (nonatomic, retain) NSString *publicMemberWithProperty; 

@end 

C.要聲明純特性(不宣相對的ivars),用於在頭文件公共成員,並申報純特性(不宣相對的ivars)執行文件在專用接口私有成員。這種方法恕我直言,比第一個更清楚,但同樣的問題仍然存在:爲什麼我們必須擁有內部/本地成員的財產?這裏有一個例子:

//==== header file =====// 
@interface MyClass : NSObject 

@property (nonatomic, retain) NSString *publicMemberWithProperty; 

@end 

//==== implementation file =====// 
@interface MyClass() 

@property (nonatomic, retain) NSString *propertyForPrivateMember; 

@end 

這個決定自由讓我有點困擾,我想從各個來源找到應該如何做的確認。但是,我無法在Apple文檔中找到這樣嚴格的陳述,所以請發佈一個鏈接到蘋果文檔,如果有的話,或任何其他理論,以清除這一點。

+0

這個問題似乎很主觀,不可能回答正確的方法。 – Till

+0

這個問題非常簡單 - 如何在Objective-C中實現私人和公共成員,以及在哪裏可以找到蘋果嚴格的聲音。 – Centurion

+1

我懇求有所不同,但不會深入討論。作爲證明我的主張的一個暗示,你自己記下了例如斯坦福大學的課程建議在各處使用屬性。 – Till

回答

21

通過使用類擴展,您可以擁有私有屬性。

類擴展語法很簡單:

的.M文件,具有類中,創建一個未命名的類別:

.H

@interface OverlayViewController : UIViewController <VSClickWheelViewDelegate> 
- (IBAction)moreButtonClicked:(id)sender; 
- (IBAction)cancelButtonClicked:(id)sender; 
@end 

.M

#import "OverlayViewController.h" 

@interface OverlayViewController() 
@property(nonatomic) NSInteger amount; 
@property(retain,nonatomic)NSArray *colors; 
@end 

@implementation OverlayViewController 
@synthesize amount = amount_; 
@synthesize colors = colors_; 

//… 

@end 

現在,您已經獲得了私人會員的所有屬性,而不會將其暴露給公衆。合成屬性不應該寫入getter/setter,因爲編譯器在編譯時會創建更多或更少的內容。

請注意,此代碼使用合成的ivars。標題中不需要伊娃報關。

關於這種方法,有一個不錯的cocoawithlove article

您還會問爲什麼要使用私人ivars的屬性。有幾個很好的理由:

編輯
由於LLVM 3也有可能,在類擴展

@interface OverlayViewController(){ 
    NSInteger amount; 
    NSArray *colors; 
} 
@end 

或甚至在執行塊

@implementation OverlayViewController{ 
    NSInteger amount; 
    NSArray *colors; 
} 
//… 
@end 

看到聲明的ivars 「WWDC2011:第322課 - Objective-C推進員(〜03:00)

+0

有些學者會指出:那些不是嚴格私有的,因爲如果它願意冒着編譯器警告,getter和setter仍然可以被其他代碼使用,但就我而言,編譯器警告足以警告其他代碼。這是完全的方式(好,給出或帶上你的尾隨下劃線)我需要團隊中的實例變量聲明 - 我應用的規則是頭文件中的任何內容都是按照定義公開的。 – Tommy

+0

@Tommy:在我的工作中,一些人爭辯說,爲什麼我們需要爲私人成員申報和使用屬性。他們說我們應該使用純粹的實例變量,並直接在類代碼中使用它們。這樣做的動機是在使用setter而不是手動釋放舊值並將新val直接分配給ivar時的處理開銷。只是好奇,有沒有蘋果「推薦」像這樣的聲明? – Centurion

+0

@Centurion您可以使用「私有」屬性[讓內存管理更簡單](http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmPractical.html#//apple_ref/doc/uid/TP40004447-SW4):「有時它可能看起來很乏味或迂腐,但如果你持續使用訪問器方法,那麼存儲管理問題的機會會大大減少。如果你在實例變量中使用保留和釋放代碼,你幾乎肯定會做錯事。「 – albertamg

2

與C++類似,Objective C提供公共,私有和受保護的作用域。它還提供了一個與Java中定義的包範圍類似的包範圍。 類的公共變量可以是程序中任何地方的引用。 私有變量只能在聲明它的類的消息中引用。它可以在屬於同一類的任何實例的消息中使用。 包的範圍與公共範圍相似,即可執行文件或庫。根據Apple的文檔,在64位體系結構中,不同映像中定義的軟件包範圍變量將被視爲私有變量。 變量作用域由@public,@ private,@protected和@package修飾符定義。這些修飾符可以以類似於C++或Java的方式使用。在範圍聲明下列出的所有變量都屬於相同的範圍。此外,變量可以在聲明範圍的同一行中列出。

@interface VariableScope : NSObject { 
     @public 
     int iVar0; 
     @protected 
     int iVar1; 
     @private 
     int iVar2; 
     @package 
     int iVar3; 

@public int iVar01, iVar02; 
@protected int iVar11, iVar12; 
@private int iVar21, iVar22; 
@package int iVar31, iVar32; 
} 
    @end 

欲瞭解更多信息使用下面的鏈接

http://cocoacast.com/?q=node/100

+4

是的,有這樣的關鍵字是公共的,私人的和受保護的,但是它們在運行時不會限制範圍。我已經進行了一些測試,並且我可以說在Objective-C中所有成員都非常公開。你可以在這裏找到關於我的實驗的文章:http://stackoverflow.com/questions/6122398/objective-c-why-private-ivars-are-not-hidden-from-the-outside-access-when-using – Centurion

3

真的沒有一個乾淨,安全,零開銷,解決了這個它是直接由語言支持。許多人對目前的可見度特徵滿意,而許多人則認爲他們缺乏。

運行時可能(但沒有)與ivars和方法作出此區別。一流的支持將是最好的,國際海事組織。在那之前,我們有一些抽象的成語:

選項A

是壞的 - 一切都可見。我不同意這是一個好方法,那不是OOD(海事組織)。如果一切是可見的,那麼你的類應該:

  • 支持(DOC所有情況下,如何在客戶端可以使用你的類(通常是不合理的或不希望)
  • 或者您通過文檔爲他們提供一噸的規則更新很可能會被忽視)
  • 或訪問器應該沒有副作用(不OOD,經常翻譯爲「不覆蓋存取」)

選項B

有選項A的缺陷,和選項A一樣,成員可以通過鍵來訪問。

選項C

這是稍微更安全。像所有其他人一樣,您仍然可以使用鍵控訪問,並且子類可以覆蓋您的訪問者(即使不知情)。

選項d

一來這樣的做法是在實現類型上寫你的類的包裝。您可以爲此使用ObjC類型或C++類型。在速度很重要的地方你可能會喜歡C++(在OP中提到過)。

的簡單方法,這將採取的形式之一:

// inner ObjC type 
@class MONObjectImp; 

@interface MONObject : NSObject 
{ 
@private 
MONObjectImp * imp; 
} 
@end 


// Inner C++ type - Variant A 
class MONObjectImp { ... }; 

@interface MONObject : NSObject 
{ 
@private 
MONObjectImp imp; 
} 
@end 


// Inner C++ type - Variant B 
class MONObjectImp; 

@interface MONObject : NSObject 
{ 
@private 
MON::t_auto_pointer<MONObjectImp> imp; 
} 
@end 

(注:由於這是最初寫,已經出臺的@implementation塊聲明實例變量的能力,您應該聲明你如果沒有必要支持較老的工具鏈或'易碎'的32位OS X ABI),那麼就有C++類型。

C++變體A不像其他人那樣「安全」,因爲它需要客戶端可見的類聲明。在其他情況下,您可以在實現文件中聲明和定義Imp類 - 從客戶端隱藏它。

然後你可以暴露你選擇的接口。當然,如果客戶真的想通過運行時訪問,他們仍然可以訪問您的成員。這對於他們安全地使用ObjC Imp類型來說是最容易的 - objc運行時不支持成員的C++語義,所以客戶端會詢問UB(IOW它是運行時的所有POD)。

ObjC實現的運行時成本是編寫一個新類型,爲每個實例創建一個新的Imp實例,並且消息量增加了一倍。

除分配外(變式B),C++類型幾乎沒有任何成本。

選項Ë

其他的方法往往是從界面分離的ivars。雖然這是一件好事,但它也是ObjC類型非常不尋常的。 ObjC類型/設計通常與他們的ivars和訪問者保持緊密的關係 - 所以你將面臨來自其他一些開發者的阻力。