欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

iOS 开发实训第八周周报

程序员文章站 2022-04-11 13:17:48
...

一、学习笔记

  • 单例模式:

    • 单例模式用于保证一个类只有一个实例,在不同类中使用单例对象时,保证获取的都是同一个对象

    • iOS中应用广泛,在系统提供类中,UIApplicationNSUserDefaultNSNotificationCenterNSBundle等都是单例类

    • 单例模式的实现原理就是要保证单例类对象的allocinit操作在应用的整个生命周期中只会执行一次,当单例类对象被创建后,其他地方调用该对象时就直接返回已存在的对象即可,在iOS中的实现,为了保证allocinit只执行一次,可以使用dispatch_once函数,dispatch_once函数的作用就是在整个应用生命周期中只能执行一次的代码块,具体实现如下:

      • SingletonClass.h
      #import <Foundation/Foundation.h>
      
      @interface SingletonClass : NSObject
      
      + (instancetype)singletonInstance;
      
      @end
      
      • SingletonClass.m
      #import "SingletonClass.h"
      
      @implementation SingletonClass
      
      static SingletonClass *singletonInstance = nil;
      
      #pragma mark - ARC
      
      + (instancetype)singletonInstance {
        
          static dispatch_once_t once;
          
          dispatch_once(&once, ^{
              singletonInstance = [[self alloc] init];
          });
          
          return singletonInstance;
      }
      
      + (instancetype)allocWithZone:(struct _NSZone *)zone {
      
          static dispatch_once_t once;
          
          dispatch_once(&once, ^{
              singletonInstance = [super allocWithZone:zone];
          });
          
          return singletonInstance;
      }
      
      - (id)copyWithZone:(struct _NSZone *)zone {
          
          return singletonInstance; // copy方法也是直接返回实例对象
      }
      
      @end
      
  • MVC架构:

    • MVC模式的目的是实现一种动态的程序设计,使后续对程序的修改和扩展简化,并且使程序某一部分的重复利用成为可能,使程序结构更加直观,即使得程序低耦合、可复用、结构简洁清晰,包括三个模块,各模块的定义和功能如下:

      • 模型 Model:数据处理层,包括网络请求、数据加工、数据库操作等
      • 视图 View:所有App上看得到的界面
      • 控制器 Controller:Model和View的中介,将Model返回的数据在View上展示出来
    • 在iOS下,MVC架构可以用下图表示

      iOS 开发实训第八周周报

      iOS 开发实训第八周周报

    • 如何在代码中应用MVC模式:

      • 很多初学者都会把View写在Controller里,比如

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

          • 比如按钮,可以在当前控制器直接添加目标,添加点击事件,在当前控制器内就能调用到点击方法,不需要设置代理之类的
          • 比如要找某个界面,直接切到这个界面对应的Controller就行,因为View写在Controller里面,不用去别的地方找
          • 比如一个View,里面有一张图片,图片依赖于网络资源,这样写的好处,可以直接让View在控制器中就能拿到资源,不需要传值
        • 缺点:

          • 导致Controller特别臃肿,里面代码特别多,因为对于复杂的View,代码量可能超过1000行,导致Controller难以维护
          • 写在Controller里的View的代码无法复用
        • ViewController分离:将View封装成一个视图类,视图类里的点击事件通过代理回调,在Controller中实现回调事件

          // View
          @implementation DemoView
           
          - (instancetype)initWithTitleStr:(NSString *)titleStr {
              if (self = [super init]) {         
                  UIButton *btn = [UIButton buttonWithType:UIButtonTypeInfoDark];
                  [self addSubview:btn];
                  [btn addTarget:self action:@selector(p_clickBtn:) forControlEvents:UIControlEventTouchUpInside];
                
                  // ...
              }
              return self;
          }
           
          - (void)p_clickBtn:(UIButton *)sender {    
              // 通过代理回调
              [_delegate respondsToSelector:@selector(clickBtn:)]
              [_delegate clickBtn:sender] : nil;
          }
           
          // Controller
          @implementation DemoViewController
           
          - (void)viewDidLoad {
              [super viewDidLoad];
               
              // setupUI
              DemoView *view = [DemoView viewWithTitleStr:@"我是参数"]; // 传递参数
              view.delegate = self;
              [self.view addSubview:view];
          }
           
          #pragma mark - privateDelegate
          - (void)clickBtn:(UIButton *)sender {
              // View层按钮的点击事件回调
          }
          
      • 将网络访问、数据库操作等放在Controller中也是很常见的问题,比如

        @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的数据源
          • 如果当前网络请求接口,需要外部参数,比如前一个界面的id,这样写可以直接让当前请求在控制器中就能拿到资源,不需要传值
        • 缺点:

          • 也会导致Controller特别臃肿,难以维护
          • 无法复用
          • 如果某些借口有依赖要求,比如借口一请求完再请求借口二,嵌套起来导致代码结构过于复杂
        • ModelController分离:按需要建立数据模型类,对数据的操作都放在模型类里,通过函数参数传值,通过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];
              }];
          }
          
  • MVVM架构:

    • 虽然MVC的层次明确,但是由于功能日益的增加、代码的维护,使得更多的代码被写在了Controller中,这样Controller还是会变得非常臃肿,所以在MVC的基础上衍生出了一种新的架构模式MVVM架构

    • MVC和MVVM的架构模式图对比:

      • MVC

      iOS 开发实训第八周周报

      ​ 在MVC中,Controller要做的事还是太多,包括表示逻辑、业务逻辑,随着功能的增加代码量会过大

      • MVVM

      iOS 开发实训第八周周报

      • 对比:

        iOS 开发实训第八周周报

    • MVVM就是在MVC的基础上分离出业务处理的逻辑到ViewModel层,简单来说,就是API请求完数据,解析成Model,之后在ViewModel中转化成能够直接被视图层使用的数据,交付给View,下面是一个实例,分别用MVCMVVM架构实现同一个功能:

      • 功能:一个页面,判断用户是否手动设置了用户名,如果设置了则正常显示用户名;如果没有设置,则显示“未设置用户名”

      • MVC

        • Model:
        // User.h
        #import <Foundation/Foundation.h>
        
        @interface User : NSObject
        
        @property (nonatomic, copy) NSString *userName;
        @property (nonatomic, assign) NSInteger userId;
        
        - (instancetype)initWithUserName:(NSString *)userName userId:(NSInteger)userId;
        
        @end
        
        // User.m
        @implementation User
        
        - (instancetype)initWithUserName:(NSString *)userName userId:(NSInteger)userId {
            self = [super init];
            if (!self) return nil;
            _userName = userName;
            _userId   = userId;
            return self;
        }
        
        @end
        
        • ViewController:
        // DemoViewController.h
        #import "HomeViewController.h"
        #import "User.h"
        
        @interface DemoViewController ()
        
        @property (nonatomic, strong) UILabel *lb_userName;
        @property (nonatomic, strong) User *user;
        
        @end
          
        // DemoViewController.m
        @implementation DemoViewController
        
        - (void)viewDidLoad {
            [super viewDidLoad];
        
            // 创建User实例并初始化
            if (_user.userName.length > 0) {
                _lb_userName.text = _user.userName;
            } else {
                _lb_userName.text = [NSString stringWithFormat:@"未设置用户名"];
            }
        }
        
        @end
        
      • MVVM

        • Model:
        // User.h
        #import <Foundation/Foundation.h>
        
        @interface User : NSObject
        
        @property (nonatomic, copy) NSString *userName;
        @property (nonatomic, assign) NSInteger userId;
        
        @end
        
        • ViewModel:
        // UserViewModel.h
        #import <Foundation/Foundation.h>
        #import "User.h"
        
        @interface UserViewModel : NSObject
        
        @property (nonatomic, strong) User *user;
        @property (nonatomic, copy) NSString *userName;
        
        - (instancetype)initWithUserName:(NSString *)userName userId:(NSInteger)userId;
        
        @end
          
        // UserViewModel.m
        #import "UserViewModel.h"
        
        @implementation UserViewModel
        
        - (instancetype)initWithUserName:(NSString *)userName userId:(NSInteger)userId {
            self = [super init];
            if (!self) return nil;
            _user = [[User alloc] initWithUserName:userName userId:userId];
            if (_user.userName.length > 0) {
                _userName = _user.userName;
            } else {
                _userName = [NSString stringWithFormat:@"未设置用户名"];
            }
            return self;
        }
        
        @end
        
        • Controller:
        // DemoViewController.h
        #import "HomeViewController.h"
        #import "UserViewModel.h"
        
        @interface HomeViewController ()
        
        @property (nonatomic, strong) UILabel *lb_userName;
        @property (nonatomic, strong) UserViewModel *userViewModel;
        
        @end
          
        // DemoViewController.m
        @implementation HomeViewController
        
        - (void)viewDidLoad {
            [super viewDidLoad];
        
            _userViewModel = [[UserViewModel alloc] initWithUserName:@"liu" userId:123456];
            _lb_userName.text = _userViewModel.userName;
        }
        
        @end
        
      • 对比可知,MVVMMVCController中的业务逻辑等剥离出来放在ViewModel中,那么在ViewController中就不需要进行判断,只需要将数据显示到View上,如下图所示

        iOS 开发实训第八周周报

    • 优点:

      • 低耦合:View可以独立于Model变化和修改,一个ViewModel可以绑定到不同的View上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变
      • 可重用性:可以把一些视图逻辑放在一个ViewModel里面,让很多View重用这段视图逻辑
      • 可测试性:界面素来是比较难以测试的,而现在测试可以针对ViewModel来写

二、遇到的问题及解决方法

  • UITableViewController如何去除多余的单元格分割线:

    • 当单元格Cell的个数不够覆盖整个View时,系统会默认自动添加一些分割线,去除的方法为用一个空的UIView作为TableFooterView
    // 设置tableFooterView
    UIView *view = [[UIView alloc] init];
    view.backgroundColor = [UIColor clearColor];
    self.tableView.tableFooterView = view;
    

三、参考链接