文档章节

写给iOS小白的MVVM教程(一): 从MVC到MVVM之一个典型的MVC应用场景 #

ios122
 ios122
发布于 2015/10/16 18:56
字数 3108
阅读 352
收藏 2
点赞 0
评论 2

前言

本着实践为主的原则,此系列文章不做过多的概念性的阐述和讨论;更多的代码和篇幅用来展示MVC和MVVC下的基础代码结构与具体实现,来展示各自优劣.这篇文章,更多的在于发掘MVC与MVVC的共性,以期为那些对MVVC感兴趣的iOS开发者,找到一种平滑的过渡与重构代码的方式.如果对MVVC感兴趣,可以直接将本文的大部分代码引用到自己的项目中,毕竟代码是写出来的!开篇之前,你可以先到这里下载本文的示例工程: https://github.com/ios122/ios122

在这一篇章里,我会分别使用我所理解的MVC与MVVC两种模式来完成同一个应用场景,以期帮助那些熟悉传统MVC模式代码的iOS攻城狮,能更好理解MVVC.限于篇幅,将MVC和MVVM拆分为两个部分,今天要说的是一个典型的MVC的应用场景,为基于MVC的MVVM重构做个基础.这篇文章着重进行了接口准备,必须的知识点的说明等内容.

预设场景:按分类请求一组博客,点击获取博客详情

我们选取最常见的一组场景: 根据某种规则获取一组数据,点击某一条数据,可以跳转到下一界面获取数据详情.这里我会根据分类请求此分类下的博客列表,点击某一条信息,可跳转到博客详情页.简单说,其实我们真正需要实现的只有两个页面: 博客分类列表页 与 博客详情页.

数据接口准备

我们至少需要两个接口,一个可以根据分类来获取博客列表,一个用来根据id获取博客详情.

使用预定义的接口

如果你没有自己的服务器或者对服务器开发不熟悉,可以使用我准备的这两个测试接口:

博客列表接口

http://www.ios122.com/find_php/index.php?viewController=YFPostListViewController&model[category]=ui&model[page]=2
  • ui分类名称,目前预定义支持: ui, network, tool,autolayout 四个分类.
  • 2,获取第几页的数据,从0开始计数,指请求此分类下第几页的数据.预定义每个分类下有100条数据,每20条数据一页.
  • 返回示例:
[
    {
        "id": "ui_40",
        "title": "title_ui_40",
        "desc": "desc_ui_40"
    },
    {
        "id": "ui_41",
        "title": "title_ui_41",
        "desc": "desc_ui_41"
    },
    {
        "id": "ui_42",
        "title": "title_ui_42",
        "desc": "desc_ui_42"
    },
    {
        "id": "ui_43",
        "title": "title_ui_43",
        "desc": "desc_ui_43"
    },
    {
        "id": "ui_44",
        "title": "title_ui_44",
        "desc": "desc_ui_44"
    },
    {
        "id": "ui_45",
        "title": "title_ui_45",
        "desc": "desc_ui_45"
    },
    {
        "id": "ui_46",
        "title": "title_ui_46",
        "desc": "desc_ui_46"
    },
    {
        "id": "ui_47",
        "title": "title_ui_47",
        "desc": "desc_ui_47"
    },
    {
        "id": "ui_48",
        "title": "title_ui_48",
        "desc": "desc_ui_48"
    },
    {
        "id": "ui_49",
        "title": "title_ui_49",
        "desc": "desc_ui_49"
    },
    {
        "id": "ui_50",
        "title": "title_ui_50",
        "desc": "desc_ui_50"
    },
    {
        "id": "ui_51",
        "title": "title_ui_51",
        "desc": "desc_ui_51"
    },
    {
        "id": "ui_52",
        "title": "title_ui_52",
        "desc": "desc_ui_52"
    },
    {
        "id": "ui_53",
        "title": "title_ui_53",
        "desc": "desc_ui_53"
    },
    {
        "id": "ui_54",
        "title": "title_ui_54",
        "desc": "desc_ui_54"
    },
    {
        "id": "ui_55",
        "title": "title_ui_55",
        "desc": "desc_ui_55"
    },
    {
        "id": "ui_56",
        "title": "title_ui_56",
        "desc": "desc_ui_56"
    },
    {
        "id": "ui_57",
        "title": "title_ui_57",
        "desc": "desc_ui_57"
    },
    {
        "id": "ui_58",
        "title": "title_ui_58",
        "desc": "desc_ui_58"
    },
    {
        "id": "ui_59",
        "title": "title_ui_59",
        "desc": "desc_ui_59"
    }
]

2.博客详情接口

http://www.ios122.com/find_php/index.php?viewController=YFPostViewController&model[id]=ui_0
  • ui_0 表示博客唯一标识.其应为分类博客列表返回的一个有效id.
  • 返回示例:
{
    "title": "title of ui_0",
    "body": "<h2>Hello iOS122</h2> Scann To Join Us <br /> <image alt=\"qq\" src=\"https://raw.githubusercontent.com/ios122/ios122/master/1443002712802.png\" />"
}

自定义接口

如果你有自己的服务器接口,直接使用即可;但是下面的oc代码,你可能也要对应变换下;如果你对服务器接口开发不是很了解,可以先阅读下这篇文章: iOS程序猿如何快速掌握 PHP,化身”全栈攻城狮”?.

假定,你已经阅读并领会了 << iOS程序猿如何快速掌握 PHP,化身”全栈攻城狮”? >>,这篇文章,新建问及那,并把下面的代码复制到对应文件中,然后根据自己的需要更改即可:

博客列表接口源文件

<?php // YFPostListViewController.php

class YFPostListViewController
{

  public $model = array(); //!< 传入的数据.
  private $countOfPerPage = 20; //!< 每页数据条数.

  /* 获取内容,用于输出显示. */
  protected function getContent()
  {
    /* 预定义一组数据 */
    $datasource = array();

    $categorys = array('ui', 'network', 'tool', 'autolayout');

    for ($i=0; $i < count($categorys); $i++) {
      $categoryName = $categorys[$i];

      $categoryData = array();

      for ($j=0; $j < 100; $j++) {
        $item = array(
          'id' => "{$categoryName}_{$j}",
          'title' => "title_{$categoryName}_{$j}",
          'desc' => "desc_{$categoryName}_{$j}"
        );

        $categoryData[$j] = $item;
      }

      $datasource[$categoryName] = $categoryData;
    }

    $queryCategoryName = $this->model['category'];
    $queryPage = $this->model['page'];

    $targetCategoryData = $datasource[$queryCategoryName];

    $content = array();

    for ($i = $this->countOfPerPage * $queryPage ; $i < $this->countOfPerPage * ($queryPage + 1); $i ++ ) {
      $content[] = $targetCategoryData[$i];
    }

    $content = json_encode($content);

     return $content;
  }

  public function show()
  {
   $content = $this->getContent();

   header("Content-type: application/json");

   echo $content;
  }
}

博客详情接口源文件

<?php // YFPostViewController.php

class YFPostViewController
{

  public $model = array(); //!< 传入的数据.

  /* 获取内容,用于输出显示. */
  protected function getContent()
  {
    $id = $this->model['id'];

    $content = array(
      'title' => "title of {$id}",
      'body' => '<h2>Hello iOS122</h2> Scann To Join Us <br /> <image alt="qq" src="https://raw.githubusercontent.com/ios122/ios122/master/1443002712802.png" />'
    );

    $content = json_encode($content);

     return $content;
  }

  public function show()
  {
   $content = $this->getContent();

   header("Content-type: application/json");

   echo $content;
  }
}

MVC 版本实现: 类似的代码,你不知道敲过了多少遍

技术要点

下面列出将要用到的技术点,如有你不熟悉的,可点击对应链接访问:

  • 使用 AFNetworking 来处理网络请求;
  • 使用 MJExtension实现JSON到数据模型的自动转换;
  • 使用 MJRefresh 实现下拉刷新与上拉加载更多的效果;
  • 使用 Masonry 进行AutoLayout布局;
  • 使用 MBProgressHUD 优化页面加载时的进度提示;

思路分析

  • 博客分类列表页面:

    1. 在前一页面指定博客分类;
    2. 页面加载时自动发起网络请求获取对应分类的数据;
    3. 获取数据成功后,自动刷新视图;获取失败,则给出错误提示;
    4. 点击某一条数据,可跳转到博客详情页.
  • 博客详情页面:

    1. 在前一页面指定博客id;
    2. 页面加载时自动发起网络请求获取id的博客详情;
    3. 获取成功后,自动刷新视图;获取失败,则给出错误提示.

博客列表页面

博客列表效果图

1. 在前一页面指定博客分类;

这一步,大家肯定都会:

YFMVCPostListViewController * mvcPostListVC = [[YFMVCPostListViewController alloc] init];
    
mvcPostListVC.categoryName = @"ui";
    
[self.navigationController pushViewController: mvcPostListVC animated: YES];

2. 页面加载时自动发起网络请求获取对应分类的数据;

为了保证每次都能进入列表页,都能自动刷新数据,建议在 viewWillAppear:方法刷新数据:

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear: animated];
    
    [self updateData];
}

updateData方法进行数据的更新:

- (void)updateData
{
    AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
    
    NSString * urlStr = [NSString stringWithFormat: @"http://www.ios122.com/find_php/index.php?viewController=YFPostListViewController&model[category]=%@&model[page]=0", self.categoryName];
    
    [manager GET: urlStr parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
        NSLog(@"JSON: %@", responseObject);
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        NSLog(@"Error: %@", error);
    }];
}

此处使用的是预定义接口,数据请求成功后,控制台输入如下:

JSON: (
        {
        desc = "desc_ui_0";
        id = "ui_0";
        title = "title_ui_0";
    },
        {
        desc = "desc_ui_1";
        id = "ui_1";
        title = "title_ui_1";
    },
        {
        desc = "desc_ui_2";
        id = "ui_2";
        title = "title_ui_2";
    },
        {
        desc = "desc_ui_3";
        id = "ui_3";
        title = "title_ui_3";
    },
        {
        desc = "desc_ui_4";
        id = "ui_4";
        title = "title_ui_4";
    },
        {
        desc = "desc_ui_5";
        id = "ui_5";
        title = "title_ui_5";
    },
        {
        desc = "desc_ui_6";
        id = "ui_6";
        title = "title_ui_6";
    },
        {
        desc = "desc_ui_7";
        id = "ui_7";
        title = "title_ui_7";
    },
        {
        desc = "desc_ui_8";
        id = "ui_8";
        title = "title_ui_8";
    },
        {
        desc = "desc_ui_9";
        id = "ui_9";
        title = "title_ui_9";
    },
        {
        desc = "desc_ui_10";
        id = "ui_10";
        title = "title_ui_10";
    },
        {
        desc = "desc_ui_11";
        id = "ui_11";
        title = "title_ui_11";
    },
        {
        desc = "desc_ui_12";
        id = "ui_12";
        title = "title_ui_12";
    },
        {
        desc = "desc_ui_13";
        id = "ui_13";
        title = "title_ui_13";
    },
        {
        desc = "desc_ui_14";
        id = "ui_14";
        title = "title_ui_14";
    },
        {
        desc = "desc_ui_15";
        id = "ui_15";
        title = "title_ui_15";
    },
        {
        desc = "desc_ui_16";
        id = "ui_16";
        title = "title_ui_16";
    },
        {
        desc = "desc_ui_17";
        id = "ui_17";
        title = "title_ui_17";
    },
        {
        desc = "desc_ui_18";
        id = "ui_18";
        title = "title_ui_18";
    },
        {
        desc = "desc_ui_19";
        id = "ui_19";
        title = "title_ui_19";
    }
)

3. 获取数据成功后,自动刷新视图;获取失败,则给出错误提示;

这一部分,涉及的变动较多,我就直接贴代码了.你会注意到View和数据已经交叉进行了,很乱的感觉.而这也是我们想要使用MVVM重构代码的重要原因之一.

//
//  YFMVCPostListViewController.m
//  iOS122
//
//  Created by 颜风 on 15/10/14.
//  Copyright (c) 2015年 iOS122. All rights reserved.
//

#import "YFMVCPostListViewController.h"
#import "YFArticleModel.h"
#import <AFNetworking.h>
#import <MJRefresh.h>
#import <MBProgressHUD.h>

@interface YFMVCPostListViewController ()<UITableViewDelegate, UITableViewDataSource>
@property (nonatomic, strong) UITableView * tableView;
@property (nonatomic, strong) NSMutableArray * articles; //!< 文章数组,内部存储AFArticleModel类型.
@property (assign, nonatomic) NSInteger page; //!< 数据页数.表示下次请求第几页的数据.

@end

@implementation YFMVCPostListViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
}

- (NSMutableArray *)articles
{
    if (nil == _articles) {
        _articles = [NSMutableArray arrayWithCapacity: 42];
    }
    
    return _articles;
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear: animated];

    // 马上进入刷新状态
    [self.tableView.header beginRefreshing];
}

- (UITableView *)tableView
{
    if (nil == _tableView) {
        _tableView = [[UITableView alloc] init];
        
        [self.view addSubview: _tableView];
        
        [_tableView makeConstraints:^(MASConstraintMaker *make) {
            make.edges.equalTo(UIEdgeInsetsMake(0, 0, 0, 0));
        }];
        
        _tableView.delegate = self;
        _tableView.dataSource = self;
        
        NSString * cellReuseIdentifier = NSStringFromClass([UITableViewCell class]);
        
        [_tableView registerClass: NSClassFromString(cellReuseIdentifier) forCellReuseIdentifier:cellReuseIdentifier];
        
        _tableView.header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{
            self.page = 0;
            
            [self updateData];
        }];
        
        _tableView.footer = [MJRefreshBackNormalFooter footerWithRefreshingBlock:^{
            [self updateData];
        }];
        
    }
    
    return _tableView;
}

/**
 * 更新视图.
 */
- (void) updateView
{
    [self.tableView reloadData];
}

/**
 *  更新数据.
 *
 *  数据更新后,会自动更新视图.
 */

- (void)updateData
{
    AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
    
    NSString * urlStr = [NSString stringWithFormat: @"http://www.ios122.com/find_php/index.php?viewController=YFPostListViewController&model[category]=%@&model[page]=%ld", self.categoryName, (long)self.page ++];
    
    [manager GET: urlStr parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
        [self.tableView.header endRefreshing];
        [self.tableView.footer endRefreshing];
        
        if (1 == self.page) { // 说明是在重新请求数据.
            self.articles = nil;
        }
        
        NSArray * responseArticles = [YFArticleModel objectArrayWithKeyValuesArray: responseObject];
        
        [self.articles addObjectsFromArray: responseArticles];
        
        [self updateView];
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        [self.tableView.header endRefreshing];
        [self.tableView.footer endRefreshing];
        
        MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
        hud.mode = MBProgressHUDModeText;
        hud.labelText = @"您的网络不给力!";
        [hud hide: YES afterDelay: 2];

    }];
}

# pragma mark - tabelView代理方法.

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    NSInteger number  = self.articles.count;
    
    return number;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString * cellReuseIdentifier = NSStringFromClass([UITableViewCell class]);
    
    UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier: cellReuseIdentifier forIndexPath:indexPath];
    
    YFArticleModel * model = self.articles[indexPath.row];

    NSString * content = [NSString stringWithFormat: @"标题:%@ 内容:%@", model.title, model.desc];
    
    cell.textLabel.text = content;
    
    return cell;
}

@end

4. 点击某一条数据,可跳转到博客详情页.

只需要再额外实现下 -tableView: didSelectRowAtIndexPath:方法即可:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 跳转到博客详情.
    YFArticleModel * articleModel = self.articles[indexPath.row];
    
    YFMVCPostViewController * postVC = [[YFMVCPostViewController alloc] init];
    
    postVC.articleID = articleModel.id;
    
    [self.navigationController pushViewController: postVC animated: YES];
}

博客详情页面

博客详情效果图

1. 在前一页面指定博客id;

这里其实就是博客列表的控制器的那几句:

// 跳转到博客详情.
YFArticleModel * articleModel = self.articles[indexPath.row];
    
YFMVCPostViewController * postVC = [[YFMVCPostViewController alloc] init];
    
postVC.articleID = articleModel.id;
    
[self.navigationController pushViewController: postVC animated: YES];

2. 页面加载时自动发起网络请求获取id的博客详情;

此处为了方便,我们依然使用预定义的博客详情接口:

AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
    
    NSString * urlStr = [NSString stringWithFormat: @"http://www.ios122.com/find_php/index.php?viewController=YFPostViewController&model[id]=%@", self.articleID];
    
    [manager GET: urlStr parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
        NSLog(@"%@", responseObject);
        
        [self updateView];
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        
        MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
        hud.mode = MBProgressHUDModeText;
        hud.labelText = @"您的网络不给力!";
        [hud hide: YES afterDelay: 2];
    }];

请求的输入,Xcode控制台打印输出,类似于:

{
    body = "<h2>Hello iOS122</h2> Scann To Join Us <br /> <image alt=\"qq\" src=\"https://raw.githubusercontent.com/ios122/ios122/master/1443002712802.png\" />";
    title = "title of ui_0";
}

3. 获取成功后,自动刷新视图;获取失败,则给出错误提示.

你会注意到,我们在上一步获取的数据,body部分内部是HTML字符串,所以我们要使用webView来显示博客详情.这和最近炒得很火的的混合开发模式有些像,但是目前主流的博客应用,几乎都是这么做的.完整代码如下:

//
//  YFMVCPostViewController.m
//  iOS122
//
//  Created by 颜风 on 15/10/16.
//  Copyright (c) 2015年 iOS122. All rights reserved.
//

#import "YFMVCPostViewController.h"
#import "YFArticleModel.h"
#import <AFNetworking.h>
#import <MBProgressHUD.h>


@interface YFMVCPostViewController ()<UIWebViewDelegate>
@property (strong, nonatomic) UIWebView * webView;
@property (strong, nonatomic) YFArticleModel * article;
@end

@implementation YFMVCPostViewController

- (UIWebView *)webView
{
    if (nil == _webView) {
        _webView = [[UIWebView alloc] init];
        
        [self.view addSubview: _webView];
        
        [_webView makeConstraints:^(MASConstraintMaker *make) {
            make.edges.equalTo(UIEdgeInsetsMake(64, 0, 0, 0));
        }];
    }
    
    return _webView;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
}


- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear: animated];
    
    [self updateData];
}


/**
 * 更新视图.
 */
- (void) updateView
{
    [self.webView loadHTMLString: self.article.body baseURL:nil];
}

/**
 *  更新数据.
 *
 *  数据更新后,会自动更新视图.
 */

- (void)updateData
{
    [MBProgressHUD showHUDAddedTo:self.view animated: YES];
    
    AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
    
    NSString * urlStr = [NSString stringWithFormat: @"http://www.ios122.com/find_php/index.php?viewController=YFPostViewController&model[id]=%@", self.articleID];
    
    [manager GET: urlStr parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
        self.article = [YFArticleModel objectWithKeyValues: responseObject];
        
        [self updateView];
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        
        MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
        hud.mode = MBProgressHUDModeText;
        hud.labelText = @"您的网络不给力!";
        [hud hide: YES afterDelay: 2];
    }];
}


@end

小结

此篇主要展示了一个典型的列表-->详情场景的MVC实现,相关技术代码可以直接用于自己的项目中.尽管这是简化的场景,但依然可以很明显地看出来数据,网络请求与视图间的相互调用,使代码整体的可复用性大大降低! 而这,也是我们下次要用 MVVC 重构这个示例的核心目的之一!

© 著作权归作者所有

共有 人打赏支持
ios122
粉丝 72
博文 76
码字总数 132179
作品 3
东城
程序员
加载中

评论(2)

ios122
ios122
@红薯 手机端 markdown 语法解析有问题
c
chaosecho
表示在爪机上,这个排版没法看
iOS应用架构谈(二):View层的组织和调用方案(中)

关于MVC、MVVM等一大堆思想 其实这些都是相对通用的思想,万变不离其宗的还是在开篇里面我提到的那三个角色:数据管理者,数据加工者,数据展示者。这些五花八门的思想,不外乎就是制订了一个...

hejunbinlan
2015/08/05
0
0
iOS渐变字体、动态条纹、获取特定位置cell、笔记App、购物车界面等源码

iOS精选源码 RAC实现添加购物车(http://www.code4app.com/thread-29956-1-1.html) 一个基于Photos.framework的图片多选,持续功能的更新中(http://www.code4app.com/thread-29959-1-1.html)...

sunnyaigd
05/29
0
0
fir.im Weekly - 30 天从 0 开始学 Swift

最近一周看到很多很好用的的轮子,忍不住推荐给大家。此外,本期 fir.im Weekly 还精选了一些实用的 iOS , Android , UI 动画的干货文章。让我们一起来看下: Hacking with Swift 由@稀土圈...

风起云飞fir_im
2015/11/05
0
0
iOS架构模式MVC+MVP+mvvm架构

随着iOS职位的火热,越来越多的人都想成为一名优秀的iOS开发工程师,那么在竞争激烈的时代,应该如何成为一名iOS开发工程师呢?现在让大家了解一下iOS架构模式 作为一个开发者,有一个学习的...

iOS晓雯
06/24
0
0
ReactiveCocoa & MVVM 学习总结二

二. MVVM 1. 什么是MVVM From: https://github.com/ReactiveCocoa/ReactiveViewModel#model-view-viewmodel MVVM是指 Model-View-ViewModel的简称,与MVC的一个主要区别是 view 拥有view mo......

lewis-180
2015/09/23
0
0
浅谈MVC、MVP、MVVM架构模式

在开发过程中,我们常常会针对一类问题而制定相关的处理方案,于是就有了相关的架构模式。目前主流的架构模式最主要就是MVC、MVP和MVVM这三种。 其实在不同的开发环境下,根据实际开发环境是...

安然若知
06/04
0
0
MVVM(微软的WPF基础)-MVC(常用模型/设计)-WPF(微软.NETFramework3.0

最近看 好多人说IOS MVC 过时了 要用MVVM 什么什么的,感觉 很新奇,就去搜了一下,发现原来所谓的MVVM就是 之前微软在10年左右就推出的WPF ,鄙人不才,搞过两年多的C#开发,因此 做了下比较...

RainOrz
2015/08/17
0
7
iOS自定义弹出视图、收音机APP、图片涂鸦、加载刷新、文件缓存等源码

iOS精选源码 一款优秀的 聆听夜空FM 源码(http://www.code4app.com/thread-14546-1-1.html) zhPopupController 简单快捷弹出自定义视图(http://www.code4app.com/thread-14861-1-1.html) WHS......

sunnyaigd
06/04
0
0
写给iOS小白的MVVM教程(序)

这几天,需要重构下部分代码,这里简要记录下.但是涉及的技术要点还是很多,所以分为多个篇章叙述.此教程来源于,并将于应用于实践,不做过多的概念性阐释和争论.每个篇章都会附上实际的可执行的代...

ios122
2015/10/13
130
0
iOS架构:Proxy实现局部模块化(附Demo)

博客更新记录:首先感谢 Casa Taloyum 前辈对该篇博客错误的指出(或者说打脸😭哈哈),笔者对 AOP 理解错误影响到各位读者的阅读体验,在此深表歉意。Casa Taloyum 前辈指出该玩儿法应该叫...

indulge_in
03/30
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Weblogic问题解决记录

问题:点击登录,页面刷新但是不进去管理界面。解决:删除cookies再登录。

wffger
18分钟前
0
0
RxJava2的错误处理方案

最近使用retrofit2 + rxKotlin2写接口访问,想尽量平铺代码,于是就想到当借口返回的状态码为「不成功」时(比如:code != 200),就连同网络错误一起,统一在onError方法中处理。想法总是好的...

猴亮屏
26分钟前
0
0
程序的调试信息

调试二进制程序时,经常要借助GDB工具,跟踪程序的执行流程,获取程序执行时变量的值,以发现问题所在。GDB能得到这些信息,是因为编译程序时,编译器保存了相应的信息。Linux下的可执行程序...

qlee
48分钟前
0
0
应用级缓存

缓存命中率 从缓存中读取数据的次数与总读取次数的比例,命中率越高越好 java缓存类型 堆缓存 guavaCache Ehcache3.x 没有序列化和反序列化 堆外缓存ehcache3.x 磁盘缓存 存储在磁盘上 分布式...

writeademo
今天
0
0
python爬虫日志(3)find(),find_all()函数

1.一般来说,为了找到BeautifulSoup对象内任何第一个标签入口,使用find()方法。 以上代码是一个生态金字塔的简单展示,为了找到第一生产者,第一消费者或第二消费者,可以使用Beautiful Sou...

茫羽行
今天
0
0
java:thread:顺序执行多条线程

实现方案: 1.调用线程的join方法:阻塞主线程 2.线程池 package com.java.thread.test;public class MyThread01 implements Runnable {@Overridepublic void run() {Syste...

人觉非常君
今天
0
0
ElasticSearch 重写IK分词器源码设置mysql热词更新词库

常用热词词库的配置方式 1.采用IK 内置词库 优点:部署方便,不用额外指定其他词库位置 缺点:分词单一化,不能指定想分词的词条 2.IK 外置静态词库 优点:部署相对方便,可以通过编辑指定文...

键走偏锋
今天
19
0
Git 2.18版本发布:支持Git协议v2,提升性能

Git 2.18版本发布:支持Git协议v2,提升性能Git 2.18版本发布:支持Git协议v2,提升性能 新版本协议的主要驱动力是使 Git 服务端能够对各种 ref(分支与 tag)进行过滤操作。 这就意味着,G...

linux-tao
今天
0
0
python浏览器自动化测试库【2018/7/22-更新】

64位py2.7版本 更新 document_GetResources 枚举页面资源 document_GetresourceText 获取指定url的内容 包括页面图片 下载地址下载地址 密码:upr47x...

开飞色
今天
42
0
关于DCL双重锁失效及解决方案

关于DCL双重锁失效及解决方案 Double Check Lock (DCL)实现单例 DCL 方式实现单例的优点是既能够在需要时才初始化单例,又能够保证线程安全,且单例对象初始化后调用getInstance方法不进行...

DannyCoder
今天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部