猿题库 iOS 客户端架构设计


原文: http://mp.weixin.qq.com/s?__biz=MjM5NTIyNTUyMQ==&mid=444322139&idx=1&sn=c7bef4d439f46ee539aa76d612023d43&scene=1&srcid=0105PiNf2cwY7DMCyEWBJfEL#rd

大抵

当有限栽架构中权衡而来的架构

些微栽架构的优点都惦记如果,缺点又都想回避,我们当片栽架构中权衡了她们之优缺点,设计有了一个新的架,起了一个名叫:MVVM
without Binding with DataController,架构图如下:

起名 1

自己今年二十三东,他们结合二十四年,二十四年前的冬季,也是十二月,他们以一道,于是化了下,后来,这个家里不仅出矣她们,还有本人

优点

通过者的事例我们得以视,这个架构起几乎单长:

层次分明,职责明确:和界面有关的逻辑完全划及 ViewModel 和 View
一整个,其中 ViewModel 负责界面相关逻辑,View 负责绘制;Data Controller
负责页面相关的数据逻辑,而 Model 还是负纯粹的数据层逻辑。
ViewController 仅仅只是充当简单的胶水作用。

耦合度低,测试性高:除开 ViewController
外,各个部件可以说凡是一点一滴解耦合的,各个部分吗是得了独立测试的。同一个功效,可以分级由不同之开发人员分别进行付出界面与逻辑,只需要建立好接口即可。

复用性高:解耦合带来的额外利益虽是复用性高,例如同一个View,只待差不多一个工厂方法生成
ViewModel,就足以直接服用。数据逻辑代码不放在 ViewController
层也可以重新有利于的服药。

学学成本低: 本质上吧,这个架构属于对 MVC 的优化,主要在解决
Massive View Controller 问题,把本来属于 View Controller
的天职根据界面与逻辑部分相应的拆到 ViewModel 和 DataController
当中,所以是一个那个便于掌握的架构设计,即使是新手也堪长足上手。

开发成本低: 完全无待引入任何第三在库就得进行付出,也避免了以
MVVM 维护本高的题目。

实施性高,重做本低:可以于 MVC
架构上渐重构的架,不欲完整重写,是平种植和 MVC 兼容的统筹。

唯独怎么一眨眼眼内

猿题库是一个享有数千万用户的创业企业,从20013年题库项目启动到2015年,团队维持了极度高之生效率,使我们的制品成功了五只特别本及数十独稍版的便捷迭代。

每当这样快速的开过程遭到,如何保管代码的成色,降低后期维护的成本,以及为品种越来越快的版迭代速度提供支持,成为了咱关心的第一问题。这篇文章用说明我们于猿题库
iOS 客户端的架构设计。

您得领略自家若之凡趟要手纸

 

举凡下雨天用在雨伞在街口等我放学的客

即首稿子详细介绍了猿题库客户端架构的宏图及思想,当然,也起恢宏底代码示例。Lancy
引入了一个叫做吧 Data Controller 的层级为 View Controller 瘦身,并且借鉴了
MVVM 的想来用界面与底层解耦。

它被本人说,我思要什么她还可以吃自己,但是并非让我去因别人

而,好的架都与具体的业务场景相关,希望大家在学习的时吧能充分理解她的试用场景,最终会改造自己APP的架构。

自我想起来从前不时说之同句子话

ViewModel

列一个 View 都见面有一个遥相呼应之 ViewModel,这个 ViewModel 会包含展示这个
View 所欲之享有数据。
咱们会动工厂方法来创造 View Model,例如这事例里,Subject View Model
不需关注传递给他是怎么样的 Subject,所有的课目或者只是用户被之课。

@interface APEHomePracticeSubjectsViewModel : NSObject
@property (nonatomic, strong, nonnull) NSArray<APEHomePracticeSubjectsCollectionCellViewModel *>
*cellViewModels;
@property (nonatomic, strong, nonnull) UIColor *backgroundColor;
+ (nonnull APEHomePracticeSubjectsViewModel *)viewModelWithSubjects:(nonnull NSArray<APESubject *>
 *)subjects;

@end

ViewModel 可以涵盖重复小之 ViewModel,就比如 View 可以生 SubView
一样。SubjectView
的中间是由一个UICollectionView兑现的,所以我们啊给了对应之 Cell
设计了一个 ViewModel。
要额外注意的凡,ViewModel
一般的话会包含的展示界面所待的有所因素,但粒度是可以操纵。一般的话,我们唯有把会见因为业务转移而变之一对要为
ViewModel 的相同部分,例如这里的 titleColor 和 backgroundColor
会因为主题不同而别,但字体的深浅(titleFont)却是未会见转移的,所以不需要从事不管巨细的都加至
ViewModel 里。

@interface APEHomePracticeSubjectsCollectionCellViewModel : NSObject
@property (nonatomic, strong, nonnull) UIImage *image;
@property (nonatomic, strong, nonnull) UIImage *highlightedImage;
@property (nonatomic, strong, nonnull) NSString *title;
@property (nonatomic, strong, nonnull) UIColor *titleColor;
@property (nonatomic, strong, nonnull) UIColor *backgroundColor;
+ (nonnull APEHomePracticeSubjectsCollectionCellViewModel *)viewModelWithSubject:(nonnull
APESubject *)subject;
+ (nonnull APEHomePracticeSubjectsCollectionCellViewModel *)viewModelForMore;

@end

嗬还惦记只要好一些,再好一些

总结

凡一旦大嘴硬死撑的她

 

听了自己最好多的悲喜

DataController

对接下我们关心 Model 和 VC 之间的涉嫌。如前提到,在传统的 MVVM
中,ViewModel 接管了 ViewController
的多数任务,包括数据获得,处理,加工等等,导致该十分有或移得臃肿。我们以立即片逻辑抽离出来,引入一个初的预制构件,DataController。

ViewController 可以往 DataController
请求获取或是操作数据,也得拿一部分轩然大波传递让
DataController,这些事件可以是 UI 事件触发的。DataController
在吸纳这些请求后,再望 Model
层获取或是更新数据,最后更将抱的多寡加工成 ViewController
最终需要之数据返回。

这般做下,使得数据有关的逻辑解耦合,数据的抱、修改、加工都在 Data
Controller 中处理,View Controller 不关注数据如何获取,如何处理,Data
Controller 也未体贴界面如何展示,如何相互。同时 Data Controller
因为一心同界面无关,所以可以生出再度好的测试性和复用性。

DataController 层和 Model 层之间的无尽并无是执迷不悟的,但需确保每一个
ViewController 都产生一个相应的 DataController。Data Controller
更强调的是彼作工作逻辑对外的接口。而当 DataController 中调用更底层的
Model 层逻辑是我们推荐的编程范式,例如数据加工层,网络层,持久层等。

当后头的事例中,我们会另行详尽的任课 DataController 的实现细节。

其未喜欢自己示弱,也直接要求自去努力学习,得好成绩

ViewModel

事先来拘禁右视图相关的片段,传统的 MVC 当中 ViewController
中发生大量的数量显示和样式定制的逻辑,我们引入 MVVM 中 ViewModel
的定义,将即时有视图逻辑移到了 ViewModel 当中。

在这规划被,每一个 View 都见面有一个遥相呼应之 ViewModel,其包含了之 View
数据展示同样式定制所欲的持有数据。同时,我们不引入双向绑定机制还是相机制,而是通过传统的代办回调或是通知来以
UI 事件传递让之外。而 ViewController 只待好成一个 ViewModel
并拿此装配于相应的 View,并接受相应的 UI 事件即可。

诸如此类做来几乎单好处:首先是 View 的意解耦合,对于 View
来说,只需要规定好相应的 ViewModel 和 UI 事件之回调接口即可与 Model
层完全割裂;而 ViewController 可以避与 View
的具体表现打交道,这有的职责被传送给了 ViewModel,有效之减轻了
ViewController
的承受;同时我们扔用了民俗绑定机制,使用了民俗的容易理解的回调机制来传递
UI
事件,降低了习成本,同时使数据的注入和流出变得易观察与决定,降低了保障了调适的成本。

自记忆里的生父

View

View 只待定义好装配 ViewModel 的接口及概念好 UI 回调事件即可:

@protocol APEHomePracticeSubjectsViewDelegate <NSObject>
- (void)homePracticeSubjectsView:(nonnull APEHomePracticeSubjectsView *)subjectView
             didPressItemAtIndex:(NSInteger)index;
@end
@interface APEHomePracticeSubjectsView : UIView
@property (nonatomic, strong, nullable, readonly) APEHomePracticeSubjectsViewModel *viewModel;
@property (nonatomic, weak, nullable) id<APEHomePracticeSubjectsViewDelegate> delegate;
- (void)bindDataWithViewModel:(nonnull APEHomePracticeSubjectsViewModel *)viewModel;
@end

渲染界面的早晚,完全靠 ViewModel 进行,包括 View 的 SubView 也会以
ViewModel 里面的子 ViewModel 渲染。

- (void)bindDataWithViewModel:(nonnull APEHomePracticeSubjectsViewModel *)viewModel {
    self.viewModel = viewModel;
    self.backgroundColor = viewModel.backgroundColor;
    [self.collectionView reloadData];
    [self setNeedsUpdateConstraints];
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:
(NSIndexPath *)indexPath {
    APEHomePracticeSubjectsCollectionViewCell *cell = [collectionView
dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath];
    if (0 <= indexPath.row && indexPath.row < self.viewModel.cellViewModels.count) {
        APEHomePracticeSubjectsCollectionCellViewModel *vm =
self.viewModel.cellViewModels[indexPath.row];
        [cell bindDataWithViewModel:vm];
}
    return cell;
}

迄今,我们就算做到了颇具的步子。我们回过头又拘留一下 ViewController
的任务就是回变的非常简单,装配好 View,向 DataController 请求数据,装配
ViewModel,配置于 View,接收 View
的UI事,一切繁杂的操作都能够的代理出去。

他们

MVVM

MVVM,Model-View-ViewModel,一个从 MVC
模式面临迈入而来之设计模式,最早为2005年于微软的 WPF 和 Silverlight
的架构师 John Gossman 提出。在 iOS 开发被执 MVVM
的语句,通常会拿大气本在 ViewController 里的视图逻辑和数据逻辑移到
ViewModel 里,从而有效之减轻了 ViewController 的背。

除此以外通过分离出来的 ViewModel 获得了重复好的测试性,我们得以对 ViewModel
来测试,解决了界面元素难给测试的题材。MVVM
通常还会见及一个强硬的绑定机制同工作,一旦 ViewModel 所对应的 Model
发生变化时,ViewModel 的习性也会发生变化,而彼此呼应的 View
也就有变化。

一样的,MVVM 也出他的老毛病:

一个至关重要的短是,MVVM 的修成本以及开发成本都不行高。MVVM
是一个血气方刚的设计模式,大多数人数对客的打听都不如 MVC
熟悉,基于绑定机制来进展编程需要自然之就学才能够比较好的左。同时于 iOS
客户端开发中,并不曾现成的绑定机制可以动用,要么使用 KVO,要么引入类似
ReactiveCocoa 这样的老三方库,使得学习成本与开发成本进一步提高。

其他一个欠缺是,数据绑定使 Debug
变得重复难了。数据绑定使程序非常能便捷的传递至任何职位,在界面及发现的 Bug
有或是由于 ViewModel 造成的,也产生或是由 Model 层造成的,传递链越丰富,对
Bug 的固化就是逾紧。

又还非得指出的凡,在风俗的 MVVM 架构中,ViewModel
依然承载的大量底逻辑,包括工作逻辑,界面逻辑,数据存储和网络有关,使得
ViewModel 仍然发生或移得与 MVC 中 ViewController 一样臃肿。

记忆里的他们还是那么青春,那么强壮有力

后记

MVVM
是一个挺棒的架,私底下自己耶会见因此其来开片私房项目,但于店铺项目里,我会还郑重的设想中利弊。我开这规划之时段,心仪
MVVM
的类好处,又忌惮于它的样弊端,再考虑到集团的出以及维护成本,所以最后计划成了当今如此。

个人觉得,好之架构设计的且是暨团组织和业务场景息息相关的。我们及时套架构帮助我们缓解了
ViewController
代码堆积的题材,也带来了再也清晰明了之代码层级和模块职责,同时没有引入了多之复杂性。希望大家为会充分理解当下套架构的适用场景,在大团结的
APP 架构设计中兼有借鉴。

纵然更换了呢

ViewController

咱见面让每一个 ViewController 都创一个遥相呼应之 DataController。
如我们叫主页建一个好像起名叫APEHomePraticeViewController,同时他见面来一个应和之
DataController 起名叫
APEHomePraticeDataController。同时我们将页面拆分为几独片,每个片发生一个互呼应的
SubView。代码如下:

@interface APEHomePracticeViewController () <APEHomePracticeSubjectsViewDelegate>

@property (nonatomic, strong, nullable) UIScrollView *contentView;
@property (nonatomic, strong, nullable) APEHomePracticeBannerView *bannerView;
@property (nonatomic, strong, nullable) APEHomePracticeActivityView *activityView;
@property (nonatomic, strong, nullable) APEHomePracticeSubjectsView *subjectsView;

@property (nonatomic, strong, nullable) APEHomePracticeDataController *dataController;

@end

viewDidLoad 的下,初始化好各个 SubView,并设置好布局:

- (void)setupContentView {
    self.contentView = [[UIScrollView alloc] init];
    [self.view addSubview:self.contentView];
    self.bannerView = [[APEHomePracticeBannerView alloc] init];
    self.activityView = [[APEHomePracticeActivityView alloc] init];
    self.subjectsView = [[APEHomePracticeSubjectsView alloc] init];
    self.subjectsView.delegate = self;
    [self.contentView addSubview:self.bannerView];
    [self.contentView addSubview:self.activityView];
    [self.contentView addSubview:self.subjectsView];
    // Layout Views ...
}

通下去,ViewController 会向 DataController 请求 Subject
相关的数量,并在恳求完成后,用收获的数目生成 ViewModel,将该配于
SubjectView,完成界面渲染,代码如下:

- (void)fetchSubjectData {
    [self.dataController requestSubjectDataWithCallback:^(NSError *error) {
        if (error == nil) {
            [self renderSubjectView];
        }
    }];
}
- (void)renderSubjectView {
    APEHomePracticeSubjectsViewModel *viewModel =
        [APEHomePracticeSubjectsViewModel viewModelWithSubjects:self.dataController.openSubjects];
    [self.subjectsView bindDataWithViewModel:viewModel];
}

自记忆里的妈妈

缺点

不可否认的凡,这个企划也罢产生那个相应的缺陷,由于那个将传统 MVVM 里面的 VM
拆成稀组成部分,会照成下面的片段景:

  1. 当页面的彼此逻辑很多时,需要反复的当 DC-VC-VM
    里来转传递消息,造成了大气粘水代码。
  2. 另外,由于当风的 MVVM 中 VM 原本是紧密的,一些错综复杂的并行本来好于
    VM 中直接就测试,如今倒是待同时使 DC 和 VM
    并附上一些胶水代码才能够展开测试。
  3. 靡了 Binding,代码写起会另行艰难一点(仁者见仁,智者见智)。

粗的下,在印象里,就是开店开客栈开始客栈

DataController

如若我辈事先所说,每一个 ViewController 都见面出一个相应的
DataController,这无异于好像 DataController
的主要职责是拍卖者页面及之具有数据相关的逻辑,我们遂其也 View Related
Data Controller。

// APEHomePracticeDataController.h
@interface APEHomePracticeDataController : APEBaseDataController
// 1
@property (nonatomic, strong, nonnull, readonly) NSArray<APESubject *> *openSubjects;
// 2
- (void)requestSubjectDataWithCallback:(nonnull APECompletionCallback)callback;
@end

点的这代码

  1. 咱定义了一个界面最终需要之数量的
    property,这里是 openSubjects,这个 property
    会存储用户打开的课列表,他的类型是APESubject
  2. 咱俩尚会定义一个接口来求 openSubject 数据。
    DataController 这同层是一个世故很高之构件,一个 DataController
    可以复用更小之 DataController,这等同接近更粗之 DataController
    通常仅见面包含纯粹的恐怕更抽象的 Model
    相关的逻辑,例如网络要,数据库请求,或是数据加工等。我们遂这同样类似
    DataController 为 Model Related Data Controller。
    Model Related Data Controller 通常会为上层提供正交的数据:

// APEHomePracticeDataController.m
@interface APEHomePracticeDataController ()
@property (nonatomic, strong, nonnull) APESubjectDataController *subjectDataController;
@end
@implementation APEHomePracticeDataController
- (void)requestSubjectDataWithCallback:(nonnull APECompletionCallback)callback {
    APEDataCallback dataCallback = ^(NSError *error, id data) {
        callback(error);
    };
    [self.subjectDataController requestAllSubjectsWithCallback:dataCallback];
    [self.subjectDataController requestUserSubjectsWithCallback:dataCallback];
}
- (nonnull NSArray<APESubject *> *)openSubjects {
    return self.subjectDataController.openSubjectsWithCurrentPhase ?: @[];
}
@end

每当我们的 APEHomePraticeDataController 的兑现着,就带有了一个
APESubjectDataController,这个 subjectDataController 会负责请求 All
Subjects 和 User Subjects,并将那加工成上层所最终用之 Open
Subjects。(备注:这个事例里的 callback
会回调多次凡猿题库产品之需求,如有得,可当当下等同重叠控制请求都得后再行调用上层回调)
实际,Model Related Data Controller 可以常备的觉得即使是大家经常在描绘的
Model 层代码,例如 UserAgent,UserService,PostService
之类的服务。之后读者若想重构就种化这个架构,大可以不必纠结于形式,直接当
DataController 里调用旧有代码的逻辑即可,如图下这样的作为都是允许的:

起名 2


数据结构

以重新好的言传身教,我们连下去要介绍一下 Subject 相关的数据结构:
APESubject 是课程的资源布局,包含了 Subject 的 id 和 name
等资源属性,这一部分性质是用户无关的;APEUserSubject
是用户之科目信息,包含了用户是否打开某个学科的属性。

@interface APESubject : NSObject
@property (nonatomic, strong, nullable) NSNumber *id;
@property (nonatomic, strong, nullable) NSString *name;
@end
@interface APEUserSubject : NSObject
@property (nonatomic, strong, nullable) NSNumber *id;
@property (nonatomic, strong, nullable) NSNumber *updatedTime;
///  On or Off
@property (nonatomic) APEUserSubjectStatus status;
@end

任由我已经是她们怀中襁褓里之宝物

但愿会叫大家帮忙。

本身爸爸坐那么圈电视,我就在后边欺负我父亲

笔者介绍

蓝晨钰(@晨钰lancy),iOS
开发者,现居北京,就职于猿题库,关注代码质量,团队频率与成品体验,博客:
http://gracelancy.com 。

谢蓝晨钰授权发表,本文的有着打赏归蓝晨钰所有。

真正发出了一致栽冬临之实感

我几乎两全前写了千篇一律篇稿子,叫《被误会的 MVC 和叫神化的
MVVM》,其中的不少考虑是和本文的撰稿人
Lancy 交流得到的。当时多人过来问:能直接上猿题库的代码吗?这次 Lancy
的马上首文章就是直接上代码了。

由小到死,什么事他都未会见为我哭笑不得

就套架构帮助猿题库彻底解耦了UI和逻辑层的出工作,并且只要 View Controller
的代码极为精简,由于 Data Controller 与界面无关,它竟然使单元测试和 TDD
成为可能。

家,A+B+c

MVC

MVC,Model-View-Controller,我们打者古老而经的设计模式入手。采用 MVC
这个架构的最深的独到之处在其定义简单,易于理解,几乎任何一个程序员都见面所有了解,几乎每一样所计算机院校都叫过有关的知识。而于
iOS 客户端开发被,MVC 作为法定推荐的主流架构,不但 SDK
已经也我们实现好了 UIView、UIViewController
等有关的零件,更是起雅量的文档和范例供我们参考学习,可以说凡是平种特别通用而成熟的架构设计。

可 MVC 也有异的坏处。由于 MVC
的概念过于简短朴素,已经越难以适应如今客户端的求,大量的代码逻辑在
MVC 中连没定义得深懂得到底该放在什么地方,导致他们很爱就见面积聚在
Controller 里,成为了人人所说的 Massive View Controller。

即这样,生活走过二十四年

Show me the code

咱以猿题库主页也例,展示我们是怎样使用使用这架构的。

起名 3

主页有几只有组成,最上面的小猴子 Banner
页,用于滚动展示一些挪信息;中间闹一个用户名字的页面,用于展示用户信息与答题情况跟部分心灵鸡汤;最下的即片凡一个课目选择页面,展示了用户被的学科入口,在又多选择间可以进一步安排这些科目入口。接下来我们见面为学科页面(SubjectView)为例展示一些细节。

从今上楼一次等及简单独阶梯到上平等叠楼都得缓好几次于

挺好的

二十四年前,他们结婚的时段也是十二月

凡是高达小学时每天中午上班休息一个小时而如对接自己又要于本人下厨的其

便是哄她开心,至少她笑了,我便特别乐意

不过争议过后要么会温柔相待


打扣开看电视要戴上花镜

正,乌鲁木齐下了洗雪

一时间

立刻是我记忆里的她们

俺们会同走之更远

而是,我会想到,花了之连接会发生办法挣回来

新生,逐渐开始知道啊叫做爱情

止就是是指向自己好,能陪同,冷暖相依

身体逐渐消瘦

长长久久

凡柴米油盐酱醋茶之常

他们的爱情

以那以后,我可怜细心的想念了相思自己之爱情观

啊时候自己看她们不再是本身记忆中的他俩了也

下一场就是这样,折腾着

相同比照正经且笃定的答问到是好好谈的婚恋

大凡自家小时候客及完班浑身是汗累的疲惫不堪听到自己喊爸爸就是当下露出笑脸的他

直至后来,我开观察这对都在一起二十四年的一直夫妻

自家而不好看,能可怜出来这么的姑娘,你们两个没良心的

大学之早晚,我道了一个阳朋友

我现花钱也是会大方

突然间

高考前一个圣诞节,爸爸还特别请了一束熊小朋友被本人

自身老爸是个小发性的人

那么,现在即使聊这些年生的事吓了

大说,你快点回来吧。

自己死去活来容易她,一直到现在我每天起码还见面让其自从一个电话

是伴随,是相知,是加上相厮守

生活,A+B+c

但是我掌握,他是当真的不行怀念自己

自说一样句喂

以后a+b+C

立刻妻子问“是好好谈的或只是是摆的游艺”

至于记忆里的他俩


以是十二月

关于他们的痴情

尔后,从好好谈,到接受不了多距离和忙碌

这内敛的老公,其实,他的心目自还理解

悠闲了扑到自家大背着及,然后给我爸爸背着我

自我爸说,你比较我顿时一世的爱人漂亮多了

大抵之上,只是于它说说好吃饭,想要啊我让她买

与它们并下的时节,她会客吃自身看好的衣服

以性好,从小我哉便特别爱给鼻子上脸

咱或说了离别

自她们之人进一步佝偻

这就是说就让爱情

举凡为自己钻进了辫子可连一朝都并未坚持得矣自己就算披头散发的外

不论是是记忆里我的他俩

从自我首先次从大的手臂上看到了老年斑

要今天既出了白发的他们

未会见是盖去远就是疏远

但他总会抱怨我不怎么回家

倘那些,他有史以来都不曾叫妈妈打过。

或者今天曾同米好几之杀女儿

今日为大打电话,我问问他,周爸爸,你闹没有起思只要之东西起名,我采购吃你

实在的情爱

免会见是以忙碌就无语不过道

她俩即使是这般

爸爸从还没有当说罢想我

一般我会调自己喜爱的频段,然后关着我爸爸和自己一样块看

今后,换自己来当你的避风港,怎么样

妻最不宽裕的早晚,过年过节或会采购漂亮衣服给我

容我具备的莫名其妙取闹的

自身给贝贝,宝贝的贝,据说小时候受哥哥由了小宝宝的小名,后来深受自己起名的时节,就由了贝贝,家里的传家宝。可是,后来哥哥换了小名,而自要么为这个,一直没换了,都是她们之宝物

凡刀子嘴豆腐心

他俩见面为了琐事偶有争论

自我有不行问他,我说还说上辈子女儿是父亲的情侣,爸爸您以为是不是

或是移做是自身要好出去,我不怕无见面让自己买那值钱的行头

正巧

自我大说,我啊都出,不用你进,你足足你自己因此的虽哼


接下来想吃自己还好之生存。

是会见管自身化妆的优美的它们

正巧,真巧

本身啊在如此做。

TO:老爸老妈

凡是家里的中流砥柱

妈妈是独专门而后来居上之贤内助

大凡未舒服时端过来的次

自家问话他,周爸爸,你想不思量自己

打自首先不行吃妈妈拔白头发

他们都于自家身边

本你是本人的避风港

愿以及时被相依相守的感情遭到

往书信很缓慢,车马稀远,一生就爱一个丁

只是它说要是吃自己富养的生活,要于自家学会不要人家为了哟自己不怕见面随之走

哈哈哈。妈妈听了,满脸嫉妒的说

时刻静好,大抵如此吧

自起火来来回回放好几潮盐

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图