<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      MVC / MVP / MVVM 架構解析

      認真對待每時、每刻每一件事,把握當下、立即去做。

      MVC 模式的目的是實現一種動態的程序設計,使后續對程序的修改和擴展簡化,并且使程序某一部分的重復利用成為可能。除此之外,此模式通過對復雜度的簡化,使程序結構更加直觀。下面主要對 MVC 架構下的優化方案以及其項目結構解析。

      img

      一. MVC 相應層應該做什么?

      1. 控制器(Controller)業務層

      控制器(Controller)-->業務層, Model 與 View 層的中介,負責轉發請求,對請求進行處理,把 Model 數據在 View 上展示出來。

      主要職責:

      • 管理 View Container 的生命周期;
      • 負責生成所有的 View 實例,并放入 View Container;
      • 監聽來自 View 與業務有關的事件,通過與 Model 的合作,來完成對應事件的業務;

      2. 視圖(View)展現層

      視圖(View) -->展現層,承載 UI 展示和事件響應(交互)。

      主要職責:

      • 響應與業務無關的事件,并因此引發動畫效果,點擊反饋(如果合適的話,盡量還是放在 View 去做)等。
      • 界面元素表達;

      3. 模型(Model)數據層

      模型(Model) -->數據層,數據處理層,包括網絡請求,數據加工,算法實現等。

      主要職責:

      • 給 ViewController 提供數據;
      • 給 ViewController 存儲數據提供接口;
      • 提供經過抽象的業務基本組件,供 Controller 調度;

      img

      4. 示例解析

      在 iOS 中的 ControlllerUIViewController,所以導致很多人會把視圖寫在 Controller 中,如下圖:

      @implementation DemoViewController
      
      - (void)viewDidLoad {
          [super viewDidLoad];
      
          //setupUI
      
          //1.createView
          UIView *view = [[UIView alloc]init];
          view.frame = CGRectMake(100, 100, 100, 100);
          view.backgroundColor = [UIColor orangeColor];
          [self.view addSubview:view];
          
          //2.createButton
          UIButton *btn = [UIButton buttonWithType:UIButtonTypeInfoDark];
          btn.center = self.view.center;
          [self.view addSubview:btn];
          
          //3...
      }
      

      這種寫法在我剛學習編程的時候也這樣寫過,先說這樣寫的好處,以及初學者為什么會這么寫:

      • 比如按鈕,可以在當前控制器直接 add target: 添加點擊事件,在當前控制器內就能調用到點擊方法,不需要設置代理之類的;
      • 比如要找某個界面,直接切到這個界面對應的 controller 就行,因為View 寫在 Controller 里面,不用去別的地方找就這里有;
      • 比如一個 View,里面有一張圖片,圖片依賴于網絡資源,這樣寫的好處,可以直接讓 ViewController 中就能拿到資源,不需要傳值;

      缺點:

      • 導致 Controller 特別臃腫,里面代碼特別多,視圖一復雜起來,代碼量可能過1000行,不好維護;
      • 寫在 Controller 里無法復用,除非你在 VC2 里面 copy 當前 VC 中的 View 的代碼;
      • 特別low!!會被懂架構的人瞧不起,噴你根本不是 MVC,是 MC 架構;

      如何告別 MC 模式,真正走到 MVC

      先給自己洗腦,iOSController 不是 UIViewController,而是普通的 Controller,沒有 View。(很關鍵的一步)。

      模塊化劃分,每個模塊對應自己的一個 View,例如 Demo 模塊,View 層里面有個 DemoView,將界面元素寫到 View 中。

      二. MVC 相應層之間如何通信?

      img

      1. View 層和 Controller 層雙向通信

      1.1 Controller 如何將數據傳遞到 View 層

      • 創建 View 的時候通過 View 的函數作為外部參數傳進去。

      1.2 View 層(用戶事件)如何傳遞到 Controller 層

      1.2.1 代理(delegate)

      通過代理(delegate),代理委托模式通過定義協議方法實現解耦, View 只關心事件觸發不處理具體邏輯;

      // 1. 定義協議
      @protocol CustomViewDelegate <NSObject>
      - (void)customView:(UIView *)view didTapButton:(UIButton *)button;
      @end
      
      // 2. View 持有 delegate 弱引用
      @interface CustomView : UIView
      @property (nonatomic, weak) id<CustomViewDelegate> delegate;
      @end
      
      @implementation CustomView
      - (void)buttonTapped:(UIButton *)sender {
          [self.delegate customView:self didTapButton:sender]; // 觸發代理方法
      }
      @end
      
      // 3. Controller 實現協議
      @interface ViewController () <CustomViewDelegate>
      @end
      
      @implementation ViewController
      - (void)viewDidLoad {
          CustomView *view = [[CustomView alloc] init];
          view.delegate = self; // 設置代理
      }
      
      - (void)customView:(CustomView *)view didTapButton:(UIButton *)button {
          NSLog(@"Delegate: 按鈕點擊事件處理"); // Controller 響應事件
      }
      @end
      
      
      1.2.2 target-action 監聽

      在 Controller 設置 target-action 監聽,Controller 給 View 添加一個 target,當用戶的觸摸事件發生時,view 產生 action,Controller 接收到之后做出相應的響應,直接建立 View 與控制器的響應鏈關系,適合簡單控件事件;

      // 1. View 暴露添加 target 的方法
      @interface CustomView : UIView
      - (void)addTarget:(id)target action:(SEL)action;
      @end
      
      @implementation CustomView {
          id _target;
          SEL _action;
      }
      
      - (void)addTarget:(id)target action:(SEL)action {
          _target = target;
          _action = action;
      }
      
      - (void)buttonTapped {
          #pragma clang diagnostic push
          #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
          [_target performSelector:_action withObject:self]; // 執行 Action
          #pragma clang diagnostic pop
      }
      @end
      
      // 2. Controller 設置 Target-Action
      @implementation ViewController
      - (void)viewDidLoad {
          CustomView *view = [[CustomView alloc] init];
          [view addTarget:self action:@selector(handleButtonTap:)]; // 綁定事件
      }
      
      - (void)handleButtonTap:(CustomView *)sender {
          NSLog(@"Target-Action: 按鈕點擊事件處理"); // Controller 響應事件
      }
      @end
      
      
      1.2.3 數據源模式 data source

      通過數據源模式 data source,通過數據驅動 UI 更新,控制器實現數據獲取協議供 View 調用;

      // 1. 定義數據源協議
      @protocol CustomViewDataSource <NSObject>
      - (NSString *)textForButtonInView:(CustomView *)view;
      @end
      
      // 2. View 持有 dataSource 引用
      @interface CustomView : UIView
      @property (nonatomic, weak) id<CustomViewDataSource> dataSource;
      - (void)reloadData; // 觸發數據更新
      @end
      
      @implementation CustomView
      - (void)reloadData {
          NSString *text = [self.dataSource textForButtonInView:self]; // 獲取數據
          [_button setTitle:text forState:UIControlStateNormal];
      }
      @end
      
      // 3. Controller 實現數據源
      @interface ViewController () <CustomViewDataSource>
      @end
      
      @implementation ViewController
      - (void)viewDidLoad {
          CustomView *view = [[CustomView alloc] init];
          view.dataSource = self;
          [view reloadData]; // 初始化數據
      }
      
      - (NSString *)textForButtonInView:(CustomView *)view {
          return @"DataSource 模式"; // 提供動態數據
      }
      @end
      
      1.2.4 Block(閉包)

      Block(閉包):?View 定義閉包屬性,Controller 通過賦值閉包來響應事件。?優點,代碼緊湊,適合簡單回調。?缺點,需注意循環引用(使用 [weak self])。

      class CustomView: UIView {
          var onButtonTap: (() -> Void)?
          @objc func buttonTapped() { onButtonTap?() }
      }
      // Controller 中賦值
      customView.onButtonTap = { [weak self] in self?.handleTap() }
      

      2. Model 層和 Controller 層雙向通信

      我們來看下這里的 Model 層通信,先看一段代碼。

      @implementation DemoViewController
      
      - (void)viewDidLoad {
          [super viewDidLoad];
      
          //loadDatas
          [[AFHTTPSessionManager manager]GET:url
                                  parameters:parameters
                                    progress:nil
                                     success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject)
          {
              //刷新tableView
              _datas = responseObject;
              [_tableView reloadDatas];
              
          } failure:nil];
      }
      

      這種寫法在我剛學習編程的時候也這樣寫過,先說這樣寫的好處,以及初學者為什么會這么寫:

      • 簡單,網絡請求完,直接在當前控制器刷新 TableView 的數據源;
      • 比如要找某個界面的網絡請求,直接切到這個界面對應的 controller 就行,因為數據請求 寫在 Controller 里面,不用去別的地方找,就這里有;
      • 比如當前網絡請求接口,需要外部參數,比如前一個界面的 uuid,這樣寫的好處,可以直接讓當前請求在 Controller 中就能拿到資源,不需要傳值;

      缺點:

      • 又導致 Controller 特別臃腫,里面代碼特別多,如果當前控制器需要多次請求,代碼量可能過1000行,不好維護;
      • 寫在 Controller 里無法復用,除非你在 VC2 里面 copy 當前 VC 中的 網絡請求的代碼;
      • 如果某些接口有依賴要求,接口1請求完再請求接口2,需要嵌套起來;
      • 特別 low!!會被懂架構的人瞧不起,噴你根本不是 MVC,如果你還用了上面的 View 寫在 Controller 的操作的話,恭喜你,最終大法 - Controller 架構 順利完成,并不需要什么 Model && View

      iOSController 就算是 UIViewController,也沒看到 Model 啊,沒有 Model。(很關鍵的一步);

      模塊化劃分,每個模塊對應自己的一個 Model,例如 Demo 模塊,Model 層里面有個 DemoModel,將網絡請求&&數據處理寫到 Model 中;

      2.1 Controller 調用和傳值到 Model

      Controller 層直接調用 Model 層類方法和實例方法,并通過參數傳值。

      2.2 Model 層數據如何回調到 Controller 層

      Model 層數據如何回調到 Controller 層,Controller 層如何知道 Model 層數據發生了改變。

      2.2.1 Block 回調

      輕量級單向通信,適合簡單回調但需注意循環引用

      //Model
      @implementation DemoModel
      
      + (void)fetchDatasWithUUid:(NSString *)uuid success:(successBlock)block{
      
          //Model發送網絡請求
          NSDictionary *parameters = @{@"uuid":uuid}
              [[AFHTTPSessionManager manager]GET:url
                                      parameters:parameters
                                        progress:nil
                                         success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject)
              {
                  //通過block異步回調~
                  block(responseObject);
          
              } failure:nil];   
      }
      
      //Controller
      @implementation DemoViewController
      
      - (void)viewDidLoad {
          [super viewDidLoad];
      
          //loadDatas
          [DemoModel fetchDatasWithUUid:_uuid success:^(NSArray *array) {
              _datas = array;
              [_tableView reloadDatas];
          }];
      }
      
      2.2.2 KVO(監聽)

      KVO(監聽),監聽 Model 的每個屬性的變化來做出響應;

      // Model.h
      @interface MyModel : NSObject
      @property (nonatomic, strong) NSString *data;
      @end
      
      // Controller.m
      - (void)viewDidLoad {
          [super viewDidLoad];
          [self.model addObserver:self 
                       forKeyPath:@"data" 
                          options:NSKeyValueObservingOptionNew 
                          context:nil];
      }
      
      - (void)observeValueForKeyPath:(NSString *)keyPath 
                            ofObject:(id)object 
                              change:(NSDictionary *)change 
                             context:(void *)context {
          if ([keyPath isEqualToString:@"data"]) {
              self.label.text = change[NSKeyValueChangeNewKey]; // 響應變化
          }
      }
      
      - (void)dealloc {
          [self.model removeObserver:self forKeyPath:@"data"];
      }
      
      2.2.3 Notification(通知)

      Notification(通知),Model 中創建一個 NSNotificationCenter,在 Controller 中創建一個方法來接收通知。當 Model 發生變化時,他會發送一個通知,而 Controller 會接收通知,一對多廣播式通信,適合跨模塊解耦但性能開銷較大。

      img

      解釋一下上面這幅圖,一個完整的模塊被分為了三個相對獨立的部分,分別是Model,View,Controller,對應到我們 App 中的依次為繼承自 NSObject 的數據中心,承載 UI 展示和事件響應的 View 以及我們最最常用的 UIViewController。

      其中 VC 持有 View 和 Model 部分,View 通過代理或者 Target-Action 的方式把用戶的操作傳遞給 VC,VC 負責根據不同的用戶行為做出不同響應。如果需要加載或刷新數據則直接調用 Model 暴露的接口,如果數據可以同步拿到,則直接使用獲取到的數據刷新 View。如果數據需要通過網絡請求等其他異步的方式獲取,VC 則通過監聽 Model 發出的數據更新(成功或失敗)通知,在收到通知時根據成功或者失敗對 View 進行相應的刷新操作。可以看出來整個過程中 View 和 Model 是沒有直接交互的,所有的操作都是通過 VC 進行協調的。

      基礎的 MVC 講解完畢,其實本質上就是讓 Controller 減壓,不該控制器管的他別讓他知道,如上基礎 MVC 操作之后的優勢:

      • MVC 架構分明,在同一個模塊內,如果視圖有問題,找到該模塊的 View 就行,其他同理,Controller 代碼大大減少,負責 View 的代理事件就可以;
      • 可以復用,比如你一個產品列表的數據,首頁也要用,產品頁也要用,直接分別在其對應的 VC1 && VC2 調用函數 [ProductModel fetchDatas] 即可,無需寫多次,View 的復用同理;
      • 結構分明,便于維護,拓展也是在此基礎上拓展,代碼干凈簡潔。

      三. MVC 架構常見的疑惑

      1. 遺失的網絡邏輯(網絡數據請求應該放在那里?)

      蘋果使用的 MVC 的定義是這么說的:所有的對象都可以被歸類為一個 Model,一個 View,或是一個控制器。就這些,那么把網絡代碼放哪里?和一個 API 通信的代碼應該放在哪兒?

      你可能試著把它放在 Model 對象里,但是也會很棘手,因為網絡調用應該使用異步,這樣如果一個網絡請求比持有它的 Model 生命周期更長,事情將變的復雜。顯然也不應該把網絡代碼放在 View 里,因此只剩下控制器了。這同樣是個壞主意,因為這加劇了厚重控制器的問題。那么應該放在那里呢?顯然 MVC 的 3 大組件根本沒有適合放這些代碼的地方。

      網絡請求與數據處理的歸屬爭議:

      1. ?純數據模型派:
        認為 Model 應僅定義數據結構,網絡請求和數據處理應由 Controller 或單獨的服務類(如 NetworkManager)處理。

      2. ?增強 Model 派:

        支持將網絡請求封裝在 Model 內部,通過擴展方法或靜態函數實現,例如:

        extension NGLoginModel {
            static func fetchAccount(completion: @escaping (NGLoginModel?) -> Void) {
                NetworkManager.request(url: "api/login") { data in
                    let account = NetcallAccount(data: data)
                    completion(NGLoginModel(info: account))
                }
            }
        }
        

        這種方式保持數據與獲取邏輯的緊密性,但可能增加 Model 的復雜度。

      https://www.jianshu.com/p/309f0477aac1

      四. MVP / MVVM

      1. MVP / MVVM 解析

      這里引用優秀博客,歡迎大家去學習:https://www.jianshu.com/p/b5043499b096

      2. MVVM + RxSwift

      這里我用一個示例來說如何使用 MVVM + RxSwift,對于具體 RxSwfit 詳細內容這里不做解析。

      數據模型?:定義了電影數據結構,遵循 Decodable 協議以便從 JSON 解析。

      import Foundation
      
      struct Movie: Decodable {
          let title: String
          let overview: String
          let posterPath: String
          let releaseDate: String
          
          enum CodingKeys: String, CodingKey {
              case title
              case overview
              case posterPath = "poster_path"
              case releaseDate = "release_date"
          }
      }
      

      網絡服務層?:使用 Alamofire 進行網絡請求,返回 RxSwift 的 Observable 對象。

      import RxSwift
      import Alamofire
      
      class MovieAPI {
          static let shared = MovieAPI()
          private let apiKey = "YOUR_API_KEY" // 替換為實際API Key
          
          func fetchPopularMovies() -> Observable<[Movie]> {
              let url = "https://api.themoviedb.org/3/movie/popular"
              let parameters: [String: Any] = [
                  "api_key": apiKey,
                  "language": "en-US"
              ]
              
              return Observable.create { observer in
                  AF.request(url, parameters: parameters)
                      .validate()
                      .responseDecodable(of: MovieResponse.self) { response in
                          switch response.result {
                          case .success(let movieResponse):
                              observer.onNext(movieResponse.results)
                              observer.onCompleted()
                          case .failure(let error):
                              observer.onError(error)
                          }
                      }
                  return Disposables.create()
              }
          }
      }
      
      struct MovieResponse: Decodable {
          let results: [Movie]
      }
      

      視圖模型?:包含業務邏輯,使用 BehaviorRelay 存儲數據狀態,處理加載和錯誤狀態。

      import RxSwift
      import Alamofire
      
      class MovieAPI {
          static let shared = MovieAPI()
          private let apiKey = "YOUR_API_KEY" // 替換為實際API Key
          
          func fetchPopularMovies() -> Observable<[Movie]> {
              let url = "https://api.themoviedb.org/3/movie/popular"
              let parameters: [String: Any] = [
                  "api_key": apiKey,
                  "language": "en-US"
              ]
              
              return Observable.create { observer in
                  AF.request(url, parameters: parameters)
                      .validate()
                      .responseDecodable(of: MovieResponse.self) { response in
                          switch response.result {
                          case .success(let movieResponse):
                              observer.onNext(movieResponse.results)
                              observer.onCompleted()
                          case .failure(let error):
                              observer.onError(error)
                          }
                      }
                  return Disposables.create()
              }
          }
      }
      
      struct MovieResponse: Decodable {
          let results: [Movie]
      }
      

      ?視圖控制器:負責 UI 展示,通過 RxSwift 綁定 ViewModel 數據到 UI 控件。

      import RxSwift
      import Alamofire
      
      class MovieAPI {
          static let shared = MovieAPI()
          private let apiKey = "YOUR_API_KEY" // 替換為實際API Key
          
          func fetchPopularMovies() -> Observable<[Movie]> {
              let url = "https://api.themoviedb.org/3/movie/popular"
              let parameters: [String: Any] = [
                  "api_key": apiKey,
                  "language": "en-US"
              ]
              
              return Observable.create { observer in
                  AF.request(url, parameters: parameters)
                      .validate()
                      .responseDecodable(of: MovieResponse.self) { response in
                          switch response.result {
                          case .success(let movieResponse):
                              observer.onNext(movieResponse.results)
                              observer.onCompleted()
                          case .failure(let error):
                              observer.onError(error)
                          }
                      }
                  return Disposables.create()
              }
          }
      }
      
      struct MovieResponse: Decodable {
          let results: [Movie]
      }
      

      這個示例完整展示了 MVVM 架構在 iOS 中的實現,RxSwift 的使用使得數據綁定和異步操作更加簡潔高效:

      • ?Model 層?:Movie 和 MovieAPI 負責數據處理;
      • ?ViewModel 層?:MovieListViewModel 處理業務邏輯;
      • ?View 層?:MovieListViewController 負責 UI 展示;
      posted @ 2025-09-15 10:25  背包の技術  閱讀(16)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 亚洲精品麻豆一二三区| 在线 欧美 中文 亚洲 精品| 亚洲国产精品一二三区| 人妻精品动漫H无码中字| 亚洲日本精品一区二区| 亚洲第一无码专区天堂| 久久久这里只有精品10| 一区二区三区在线色视频| 精品无码三级在线观看视频| 99在线视频免费观看| 国产人与禽zoz0性伦多活几年 | 亚洲国产精品久久久天堂麻豆宅男 | 中文字幕乱码一区二区免费| 日韩精品一区二区三区日韩| 国产精品无码素人福利不卡| 成人国产精品中文字幕| 亚洲精品久久| 久久精品国产一区二区三| 日韩乱码人妻无码中文字幕视频| 国产精品久久中文字幕| 国产二区三区不卡免费| 欧美极品色午夜在线视频| 亚洲老女人区一区二视频| 免费观看全黄做爰大片| 东京热大乱系列无码| 湘阴县| 亚洲日本韩国欧美云霸高清| 少妇办公室好紧好爽再浪一点| 中国熟女仑乱hd| 从江县| 扒开双腿猛进入喷水高潮叫声| 天干天干夜啦天干天干国产| 久女女热精品视频在线观看| 肉色丝袜足j视频国产| ww污污污网站在线看com| 国内精品无码一区二区三区| 日韩有码精品中文字幕| 麻豆久久天天躁夜夜狠狠躁| 一本大道久久香蕉成人网| 绝顶丰满少妇av无码| 亚洲爆乳WWW无码专区|