iOS中的事件響應鏈、單例模式、工廠模式、觀察者模式

學習內容

歡迎關注我的iOS學習總結——每天學一點iOS://github.com/practiceqian/one-day-one-iOS-summary

iOS中事件傳遞和相應機制

  1. iOS中的事件(主要有三類)

    • 觸摸事件(touch Event)
    • 運動/加速計事件(motion Event)
    • 遠程控制事件(remote-control Event)
  2. UIResponder(響應者對象)

    • iOS中只有繼承了UIResponder的類才能接收並處理事件,稱之為響應者對象。

    • 為什麼繼承了UIResponder的類都能夠接收並處理事件呢?

      • //UIResponder類提供了以下四個對象方法來處理觸摸事件
        - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
        - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
        - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
        - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
        
  3. 事件響應鏈

    • 所有處理事件響應的類都是UIResponder的子類,響應者鏈是一個由不同對象組成的層次結構,其中的每個對象依次獲得響應事件消息的機會,事件將沿著響應者鏈一直向下傳遞,直到該事件被接收並且處理

    • 事件的傳遞和響應鏈

      //事件傳遞是從第一響應者->UIApplication
      View->ViewController->UIWindow->UIApplication->nil
      //事件的響應是從UIApplication->FirstResponder
      UIApplication->UIwindow->ViewController->View
      
      • 可以通過[responder nextResponder]找到當前responder的下一個responder,持續這個過程,最後會找到UIApplication對象
      • 通常情況下,在first responder(即用戶所點擊的view)就會處理事件請求,進入事件分發機制
    • 幾種繼承關係

      • UIWindow->UIView->UIResponder
      • UIApplication->UIResponder
  4. 事件分發機制

    • 第一響應者指的是當前接受觸摸的響應者對象(通常是一個UIView對象),表示當前對象正在與用戶進行交互,它是響應鏈的開端,響應鏈和事件分發機制都是為了找出第一響應者
    • 當發生觸摸操作後,系統會將該操作打包成一個UIEvent對象,並將其放入當前活動Application的事件隊列(隊列的特點是先進先出,先發生的事件先處理)
    • UIApplication會從事件隊列中取出事件並將其分發給當前應用的UIWindow對象
    • UIwindow對象首先會使用hitTest:withEvent:方法尋找該觸摸事件發生時所在的視圖,histTest->view
    • hitTest:withEvent:方法的處理流程
      • 調用pointInside:withEvent:方法判斷觸摸點是否在當前視圖內
      • 如果返回no,則hitTest方法返回nil,如果返回yes,則向當前視圖的所有子視圖發送hitTest:withEvent消息
      • 如果第一次有子視圖返回非空對象,那麼UIWindow對象調用的histTest:withEvent(UIWindow繼承自UIView,UIView含有hitTest方法)方法返回該對象,如果所有的子視圖都返回nil,那麼histTest:withEvent方法返回自身
    • 如果histTest:withEvent方法沒有找到第一響應者或者第一響應者沒有處理該事件,那麼該事件就會沿著響應者鏈向上回溯,如果UIWindow對象和UIApplication對象都不能處理該事件,那麼該事件就會被丟棄。
    • 如果對事件響應需要有特殊的操作,那麼可以重寫hisTest:withEvent方法來實現
    • UIView(或者繼承自UIView的子類)不能接收觸摸事件的三種情況
      • UIView的userInteractionEnabled設置為NO(不可交互)
      • UIView的hidden設置為YES(隱藏了UIView,父控制項隱藏的話子控制項也會被隱藏)
      • alpha透明度<0.01(0~0.01)為透明
  5. UIApplication的幾種狀態

    • not Running(程式未啟動狀態)
    • Inactive(未激活)程式在前台運行,但是沒有接收到事件,在沒有事件需要處理的情況下程式通常處於這個狀態
    • active(激活)程式在前台運行,並且接收到了事件
    • Background(後台),程式在後台運行並且可以執行程式碼,大多數程式掛起到後台都會在這個狀態停留一會,一段時間後會進入掛起(Suspended)狀態,經過特殊處理的請求可以長期處於這個狀態。
    • Suspended(掛起),程式在後台運行但是不能執行程式碼,系統自動把程式變成這個狀態並且不會發出通知,但是還在記憶體中運行,當系統遇到記憶體壓力時,就會自動將程式從記憶體中清除,為前台程式提供更多的記憶體。

iOS中的設計模式

  1. 設計原則

    • 單一職責原則
      • 一個類只承擔一個職責,如果一個類擁有了兩種職責,那麼需要考慮是否將這個類拆分開來,比如UITableViewCell,如果有多重樣式的cell,那麼我們應該考慮的是分成幾種類型的cell去寫,而不是在一個cell中實現多種類型
    • 開閉原則
      • 對軟體實體的修改,最好使用擴展而非直接修改的方式
      • 對類,函數,如果需要修改,盡量採用繼承的方式來擴展類的功能,而不是直接修改類的程式碼(如果工程不是特別複雜的情況下,那麼直接修改類的程式碼也可以)
    • 里式替換原則
      • 子類可以擴展父類的方法,但不應該重寫父類的方法
    • 介面隔離原則
      • 對象不應被強迫依賴它不使用的方法
    • 依賴倒置原則
      • 高層模組不應該依賴底層模組,二者都應該依賴其抽象,抽象不應該依賴細節,細節應該依賴抽象
    • 迪米特法則
      • 一個對象對另一個對象了解的越多,那麼他們的耦合度就越高,當修改一個對象時,對另一個對象的影響就越大(一個對象應該對另一個對象保持最少的了解,盡量實現低耦合高內聚)
    • 組合/聚合復用原則
      • 盡量使用合成/聚合,在一個新的對象裡面使用一些已有的對象,使之成為新對象的一部分,新的對象通過對這些對象的委派達到復用已有功能的目的(盡量使用合成/聚合,不要使用繼承)
  2. iOS開發中幾種常用的設計模式

    • 單例模式(兩種實現方法)

      • //使用@synchronized保證執行緒安全的懶載入寫法,但是由於@synchronized是互斥鎖導致性能比較低
        + (Singleton *)sharedInstance { 
            @synchronized (self) {
                if (!instance) { 
                    instance = [[Singleton alloc] init];
                } 
            }
            return instance; 
        }
        //使用GCD中的dispatch_once寫法,同時滿足了執行緒安全和靜態分析器的要求
        + (Singleton *)sharedInstance { 
            static dispatch_once_t predicate;
            dispatch_once(&predicate, ^{
                instance = [[Singleton alloc] init];
            });
            return instance; 
        }
        
      • 以上兩種單例模式的實現都不能保證SingleTon是唯一的,因為通過[[Single alloc]init]方式創建仍然會創建出新的實例

      • 創建絕對單例的方法

        //通過攔截alloc方法來創建絕對單例
        //new->alloc
        //alloc->allocWithZone
        //alloc方法的內部就是調用了allocWithZone方法,allocWithZone方法的作用就是申請空間創建對象並將創建的對象返回。
        + (instancetype)shareInstance
        {
            if (instance == nil) {
                static dispatch_once_t once;
                dispatch_once(&once, ^{
                    instance = [[Singleton alloc] init];
                });
            }
            return instance;
        }
        
        + (instancetype)allocWithZone:(struct _NSZone *)zone
        {
            if (instance == nil) {
                static dispatch_once_t onceToken;
                dispatch_once(&onceToken, ^{
                    instance = [super allocWithZone:zone];
                });
            }
            return instance;
        }
        
    • 工廠模式

      • //一個工廠類需要執行多個方法,可以將每個方法衍生出一個類繼承自這個工廠類,實例化工廠類時根據傳入的參數創建對應方法的實例
        @implementation OperationFactory
        
        + (Operation *) createOperat:(char)operate{
            Operation *oper = nil;
            switch (operate) {
                case '+':
                {
                    oper = [[OperationAdd alloc] init];
                    break;
                }
                case '-':
                {
                    oper = [[OperationSub alloc] init];
                    break;
                }
                case '*':
                {
                    oper = [[OperationMul alloc] init];
                    break;
                }
                case '/':
                {
                    oper = [[OperationDiv alloc] init];
                    break;
                }
                default:
                    break;
            }
            return oper;
        }
        @end
        
    • 委託模式(這裡只列出使用步驟,A為委託對象,B為代理對象)

      • 類A介面為文件中創建協議,聲明@requeired和@optional方法

      • 類A添加一個屬性,使用weak修飾(否則會造成循環引用)

        @property (nonatomic,wead) id<協議名>delegate

      • 類B介面文件/類擴展中遵守協議,並在實現文件中實現協議的required/optional方法

      • 類B中創建一個類A對象,類A對象的delegate設置為self(類B自身)

      • 在類A對象中調用[self.delegate 方法]

    • 觀察者模式(兩種方式)

      • 使用NSNotification通知實現
      • 使用KVO(key,value,observer)鍵值觀測實現
Tags: