99热99这里只有精品6国产,亚洲中文字幕在线天天更新,在线观看亚洲精品国产福利片 ,久久久久综合网

歡迎加入QQ討論群258996829
麥子學(xué)院 頭像
蘋(píng)果6袋
6
麥子學(xué)院

iOS中有必要使用攔截器來(lái)取代基類(lèi)嗎?

發(fā)布時(shí)間:2016-11-18 16:12  回復(fù):0  查看:2352   最后回復(fù):2016-11-18 16:12  

以前在ios開(kāi)發(fā)中,習(xí)慣了把 ViewController 里面的一些通用邏輯寫(xiě)在一個(gè)基類(lèi),然后其它 ViewController 再繼承這個(gè)基類(lèi),一直都認(rèn)為這是一個(gè)不錯(cuò)的做法,但今天看了篇關(guān)于 View 層的架構(gòu)文章,完全顛覆了我以前的想法,派生基類(lèi)并不是最好的選擇。

  簡(jiǎn)單的分析下原因

 ?。?span>派生的基類(lèi)會(huì)增加業(yè)務(wù)使用的成本

  1、增加集成成本,在百度實(shí)習(xí)的時(shí)候,開(kāi)發(fā)的 App 依賴(lài)于百度地圖和百度導(dǎo)航,而且都是直接源碼依賴(lài)進(jìn)來(lái)的,每次編譯一次都好幾分鐘,在添加新的頁(yè)面和調(diào)試頁(yè)面時(shí),需要經(jīng)常運(yùn)行查看,單是編譯的時(shí)間都讓人無(wú)法接受了。想新建一個(gè)基于我們開(kāi)發(fā)的 App 環(huán)境的 Demo,但我們所有的 ViewController 都繼承于一個(gè)基類(lèi),而基類(lèi)又依賴(lài)于各種樣的基礎(chǔ)庫(kù),折騰半天也搞不出這么一個(gè) Demo.

  2、增加學(xué)習(xí)成本,使用派生的基類(lèi)時(shí)還需要我們?nèi)W(xué)習(xí)派生基類(lèi)的使用

  既然這種方式不是最好的選擇,那當(dāng)然有更好的方式去取代這種方式來(lái)實(shí)現(xiàn)相同的效果,下面說(shuō)下通過(guò)攔截器來(lái)實(shí)現(xiàn)和派生基類(lèi)一樣的功能。

  這里我使用已經(jīng)造好的輪子 Aspects 來(lái)進(jìn)行方法的攔截,我們來(lái)創(chuàng)建一個(gè)繼承 NSObject 的 ViewController 的攔截器:

iOS中有必要使用攔截器來(lái)取代基類(lèi)嗎?

.m 文件:

 @implementation ViewControllerInterceptor

  // 會(huì)在應(yīng)用啟動(dòng)的時(shí)候自動(dòng)被runtime調(diào)用,通過(guò)這個(gè)方法可以實(shí)現(xiàn)代碼的注入

  + (void)load {

  [super load];

  [ViewControllerInterceptor sharedInstance];

  }

  // 單例

  + (instancetype)sharedInstance {

  static dispatch_once_t onceToken;

  static ViewControllerInterceptor *sharedInstance;

  dispatch_once(&onceToken, ^{

  sharedInstance = [[ViewControllerInterceptor alloc] init];

  });

  return sharedInstance;

  }

  - (instancetype)init {

  if ([super init]) {

  }

  return self;

  }

  @end

  實(shí)現(xiàn)一個(gè)單例來(lái)確保只初始化一次。因?yàn)槔^承 NSObjectload() 方法就會(huì)在啟動(dòng)時(shí)被runtime調(diào)用,通過(guò)這個(gè)方法可以實(shí)現(xiàn)代碼的注入。所以我們把 Aspects 的攔截方法實(shí)現(xiàn)在 init() 方法里面:

 - (instancetype)init {

  if ([super init]) {

  // 使用 Aspects 進(jìn)行方法的攔截

  // AspectOptions 三種方式選擇:在原本方法前執(zhí)行、在原本方法后執(zhí)行、替換原本方法

  [UIViewController aspect_hookSelector:@selector(viewWillAppear:) withOptions:AspectPositionAfter usingBlock:^(idaspectInfo, BOOL animated){

  UIViewController * vc = [aspectInfo instance];

  [self viewWillAppear:animated viewController:vc];

  } error:NULL];

  }

  return self;

  }

  這里會(huì)監(jiān)聽(tīng) UIViewController 的 viewWillAppear: 方法,當(dāng) UIViewController 執(zhí)行 viewWillAppear: 方法后,就會(huì)攔截到,然后執(zhí)行攔截器的模擬 viewWillAppear: 方法:

 // 通過(guò)這種方式可以代替原來(lái)框架中的基類(lèi),不必每個(gè) ViewController 再去繼續(xù)原框架的基類(lèi)

  #pragma mark - fake methods

  - (void)viewWillAppear:(BOOL)animated viewController:(UIViewController *)viewController

  {

  // 去做基礎(chǔ)業(yè)務(wù)相關(guān)的內(nèi)容

  if (!viewController.isInitTheme) {

  [self ThemeDidNeedUpdateStyle];

  viewController.isInitTheme = YES;

  }

  // 其他操作......

  }

  - (void)ThemeDidNeedUpdateStyle {

  NSLog(@"Theme did need update style");

  }

  在這里,我想當(dāng)?shù)?/span> ViewController 執(zhí)行 viewWillAppear: 方法后判斷是否需要初始化主題,如果已經(jīng)初始化成功后就會(huì)再次執(zhí)行,所有我們需要在 ViewController 添加一個(gè)標(biāo)志屬性,但 ViewController 是不確定的,我們并不知道當(dāng)前 ViewController 是哪一個(gè)類(lèi),如果我每個(gè) ViewController 都添加一個(gè) isInitTheme 的標(biāo)志,那就又回到派生基類(lèi)上去了,這時(shí)候,就由神奇的 Category 來(lái)處理了。

  我們對(duì) UIViewControler Category 添加一個(gè) isInitTheme 的屬性:

   @interface UIViewController (Addition)

  @property(nonatomic, assign) BOOL isInitTheme;

  @end

  然后再通過(guò) runtime 來(lái)動(dòng)態(tài)添加一個(gè) isInitTheme 的實(shí)例變量:

  #define KeyIsInitTheme @"KeyIsInitTheme"

  @implementation UIViewController (Addition)

  #pragma mark - inline property

  - (BOOL)isInitTheme {

  return objc_getAssociatedObject(self, KeyIsInitTheme);

  }

  - (void)setIsInitTheme:(BOOL)isInitTheme {

  objc_setAssociatedObject(self, KeyIsInitTheme, @(isInitTheme), OBJC_ASSOCIATION_RETAIN_NONATOMIC);

  }

  @end

  這里我們就成功在 UIViewController 的 Category 中添加一個(gè)實(shí)例變量,然后我們就可以使用這個(gè)屬性來(lái)進(jìn)行判斷了。

  擴(kuò)展一個(gè)問(wèn)題,當(dāng)前的代碼是會(huì)攔截所有的 ViewController,如果我們想針對(duì)某些 ViewController 不攔截又需要怎么辦呢?

  其實(shí)很簡(jiǎn)單,同上面的 isInitTheme 屬性一樣,再添加一個(gè)判斷是否需要進(jìn)行監(jiān)聽(tīng)的屬性:

   // 攔截器是否有效

  @property(nonatomic, assign) BOOL disabledInterceptor;

  然后一樣需要通過(guò) runtime 來(lái)實(shí)現(xiàn)實(shí)例變量。然后在 Aspects 攔截成功后進(jìn)行判斷是否需要下一步的操作:

  - (instancetype)init {

  if ([super init]) {

  // 使用 Aspects 進(jìn)行方法的攔截

  // AspectOptions 三種方式選擇:在原本方法前執(zhí)行、在原本方法后執(zhí)行、替換原本方法

  [UIViewController aspect_hookSelector:@selector(viewWillAppear:) withOptions:AspectPositionAfter usingBlock:^(idaspectInfo, BOOL animated){

  UIViewController * vc = [aspectInfo instance];

  if (!vc.disabledInterceptor) {

  [self viewWillAppear:animated viewController:vc];

  }

  } error:NULL];

  }

  return self;

  }

  在這里,通過(guò)攔截來(lái)取代派生的基類(lèi),這樣的做法的好處是 業(yè)務(wù)代碼不需要對(duì)框架的主動(dòng)迎合,使得業(yè)務(wù)能夠被框架感知 ,這里只拿 UIViewControler 來(lái)做例子,但不限 UIViewControler, 其它的類(lèi)也是適用的。

  這里介紹了通過(guò)攔截器來(lái)取代派生基類(lèi),但是在需要用繼承的地方法還是需要使用繼承,適當(dāng)選擇最優(yōu)的方案才是最明智的。

 

文章來(lái)源:DengYonghao的簡(jiǎn)書(shū)

您還未登錄,請(qǐng)先登錄

熱門(mén)帖子

最新帖子

?