觀“編寫高質量iOS與OC 代碼的52個有效方法”有感(四)· 協議與分類

神器.png

23、通過委托與數據源協議進行對象間通信

  • 委托模式(Delegate pattern)
    定義一套接口,某對象若想接受另一個對象的委托,則需遵從此接口,以便成為其“委托對象”。而“另一個對象”則可以給其委托對象回傳一些信息,也可以在發生相關事件時通知委托對象。
  • 委托模式一般用于反向傳值。
    協議定義:
@protocol FirstViewControllerDelegate <NSObject>

//必選
@required
- (void)firstReturnAge:(NSString *)age;

//可選
@optional
- (void)firstReturnName:(NSString *)name;
- (void)firstReturnNPhone:(NSString *)phone;
- (void)firstReturnUSerID:(NSString *)userID;

@end
  • 協議一般分為 required (必選)和 optional (可選)
    例子:UITableView:顧名思義,必選的就是必須實現的方法,不實現程序就會報錯。
@protocol UITableViewDataSource<NSObject>

@required

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;

// Row display. Implementers should *always* try to reuse cells by setting each cell's reuseIdentifier and querying for available reusable cells with dequeueReusableCellWithIdentifier:
// Cell gets various attributes set automatically based on table (separators) and data source (accessory views, editing controls)

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;

@optional

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView;              // Default is 1 if not implemented

- (nullable NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section;    // fixed font style. use custom view (UILabel) if you want something different
- (nullable NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section;
  • 有了協議之后,類就可以用一個屬性存放其委托對象了。
    @property (nonatomic, weak) id <FirstViewControllerDelegate> delegate;
    這個屬性定義為weak,而非strong,是因為兩者之間必須為“非擁有關系”。
  • 委托方法的實現
if ([self.delegate respondsToSelector:@selector(firstReturnName:)]) {
        [self.delegate firstReturnName:@"哈哈"];
        [self.delegate firstReturnAge:@"18"];
        [self.navigationController popViewControllerAnimated:YES];
    }

可以發現:

  • 可選實現的方法,首先需要通過 respondsToSelector 來判斷委托對象是否實現了相關方法。如果實現了就調用,沒有實現就不執行任何操作。
  • 必選方法,上面我就和可選寫在一起了,其實就是默認委托對象一定實現了相關方法。如果沒實現就報錯。
  • 如果相關方法多次調用,要考慮的優化。
    多次調用,那么每次都會去檢測委托對象是否實現了方法。那么我們是不是可以把委托對象是否能夠響應相關協議方法這一信息緩存起來?加快執行速度!
    我們可以用 位段
    位段,C語言允許在一個結構體中以位為單位來指定其成員所占內存長度,這種以位為單位的成員稱為“位段”或稱“位域”( bit field) 。利用位段能夠用較少的位數存儲數據。
struct {
        unsigned int didFirstReturnName         :1;
        unsigned int didFirstReturnNPhone       :1;
        unsigned int didFirstReturnUSerID       :1;
    } _delegateFlags;

這個結構體用來緩存委托對象是否能夠響應方法。實現緩存功能所用的代碼可以寫在 delegate 屬性所對應的設置方法里:

- (void)setDelegate:(id<FirstViewControllerDelegate>)delegate {
  _delegate = delegate;
  _delegateFlags.didFirstReturnName = [delegate respondsToSelector:@selector(firstReturnName:)];
  _delegateFlags.didFirstReturnNPhone = [delegate respondsToSelector:@selector(firstReturnNPhone:)];
  _delegateFlags.didFirstReturnUSerID = [delegate respondsToSelector:@selector(firstReturnUSerID:)];
}

每次調用 delegate 的相關方法之前,就不用檢測委托對象是否實現了相關方法,而是直接查詢結構體里的標志:

if (_delegateFlags.didFirstReturnName) {
        [self.delegate firstReturnName:@"哈哈"];
        [self.navigationController popViewControllerAnimated:YES];
    }

24、將類的實現代碼分散到便于管理的數個分類之中

  • 類中經常容易填滿各種方法,而這些方法的代碼則全部堆在一個巨大的實現文件里,可以通過“Objective-C”的“分類”機制,把類代碼按邏輯劃入幾個分區中,對開發與調試都有好處。
Xcode創建分類.png

25、總是為第三方類的分類名稱加前綴

  • 因為分類機制通常用于向無源碼的既有類中新增功能。這個特性極為強大,使用時很容易忽視其中可能產生的問題:
    如果分類中的方法覆蓋原來的那一份實現代碼,那么會以最后一個分類為準。
  • 所以我們一般的做法:以命名空間來區別各個分類的名稱和其所定義的方法。添加前綴。

26、勿在分類中聲明屬性

  • 盡管在技術上說,分類也可以聲明屬性,但是這種做法還是盡量避免。原因:
    除了“class-continuation分類(參看27條)”之外,其他分類都無法向類中新增實例變量,因此,它們無法把實現屬性所需的實例變量合成出來。
  • 把封裝數據所用的全部屬性都定義在主接口里。
  • 在除了“class-continuation分類”之外的其他分類中,可以定義存取方法,但盡量不要定義屬性。

27、使用“class-contunuation分類”隱藏實現細節

  • 類中經常會包含一些無須對外公布的方法及實例變量。怎么寫呢?,這個特殊的“class-continuation分類”就派上用場了。
    “class-continuation分類”和普通分類不同,它必須定義在其所接續的那個類的實現文件里。其重要之處:這是唯一能聲明實例變量的分類,而且此分類沒有特定的實現文件,其中的方法都應該定義在類的主實現文件里。
@interface ZSCManager () {
    NSString *_name;
}
@end

這樣做有什么好處呢?
公共接口里本來就能定義實例變量。不過,把它們定義在“class-continuation分類”或者“實現塊”中可以將其隱藏起來,只供本類使用。

28、通過協議提供匿名對象

  • 協議定義了一系列方法,遵從此協議的對象應該實現它們(如果這些方法不是可選的,那么就必須實現)。
    我們可以用協議把自己所寫的API之中的實現細節隱藏起來,將返回的對象設計為遵從此協議的純 id 類型。這樣,想要隱藏的類名就不會出現在API之中了。若是接口背后有很多個不同的實現類,而你又不想指明具體使用哪個類,就可以考慮用這個方法。
    因為有時候這些類可能會變,有時候它們無法容納于標準的類繼承體系中,因而不能以某個公共基類來統一表示。
    例子:
#import <Foundation/Foundation.h>

@interface ZSCShare : NSObject

+ (instancetype)shareInstance;
/**通過協議提供匿名對象,返回的具體不知道是什么類型,我們不關注,只要是分享的就行**/
- (id)createShareWithName:(NSString *)name;

@end

//.m的實現
#import "ZSCShare.h"

@implementation ZSCShare

static id instance;
+ (instancetype)shareInstance {
    static dispatch_once_t predicate;
    dispatch_once(&predicate, ^{
        instance = [[ZSCShare alloc] init];
    });
    return instance;
}
- (id)createShareWithName:(NSString *)name {

    return [NSString stringWithFormat:@"分享了 %@",name];
}

@end
  • 調用時候就會顯示下面這樣,達到了我們的目的。
協議匿名返回.png

接下來也將會繼續整理。如果覺得有用請點個喜歡!

您的支持將是我繼續寫作的動力!謝謝。

觀“編寫高質量iOS與OC X代碼的52個有效方法”有感(一)· 熟悉Objective-C
觀“編寫高質量iOS與OC X代碼的52個有效方法”有感(二)· 對象、消息、運行時
觀“編寫高質量iOS與OC X代碼的52個有效方法”有感(三)· 接口與API設計
觀“編寫高質量iOS與OC X代碼的52個有效方法”有感(四)· 協議與分類

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容