2017-12-27 MVC/ 前端/ iOS

iOS: .txt 小说阅读器功能开发的 8 个老套路

本文介绍本地 .txt 小说阅读器功能开发的 8 个相关技术点。

网络 .txt 小说开发,则多了下载和缓存两步

一本书有什么,即书的数据结构

一本书有书名,有正文,有目录

手机书架上的书很多,需给书分配一个 id,去除重复

小说用户的常见操作有两种,当前阅读进度记录和书签列表

小说的主要模型 ReadModel

书的两个自然属性 ID 和目录

( 一本书有书名,这里与 ID 合并 )

书的两个用户操作属性,阅读记录和书签

可看出,书的正文去哪了?

class ReadModel: NSObject,NSCoding {

    /// 小说ID, 书名
    let bookID:String
    
    /// 目录, 章节列表
    var chapterListModels = [ChapterBriefModel]()
    
    /// 当前阅读记录
    var recordModel:ReadRecordModel?
    
    /// 书签列表
    var markModels = [ReadMarkModel]()
    
    
}

小说的目录模型 ChapterBriefModel

class ChapterBriefModel{
    
    /// 章节ID    
    var id: Int!

    /// 小说ID
    var bookID:String!
    
    /// 章节名称
    var name:String!
}

有了目录,要阅读,尚缺正文

小说的章节模型

包含具体的阅读章节纯文本 content,和用来渲染呈现的富文本 fullContent

含有上一章和下一章的 ID,作为一个链表,用于连续阅读


class ReadChapterModel: NSObject,NSCoding {
    
    /// 小说ID
    let bookID: String
    
    /// 章节ID
    let id: Int
    
    /// 上一章ID
    var previousChapterID: Int?
    
    /// 下一章ID
    var nextChapterID: Int?
    
    /// 章节名称
    var name:String!
    
     /// 内容
    /// 此处 content 是经过排版好且双空格开头的内容。 
    var content:String!
    
    /// 完整富文本内容
    var fullContent:NSAttributedString!
    
    /// 本章有多少页
    var pageCount: Int = 0
    
    /// 分页数据
    var pageModels = [ReadPageModel]()
    
    /// 内容的排版属性
    private var attributes = [NSAttributedString.Key: Any]()
    
}

1,基础呈现:

网上下载了一本 《三国演义》,制作一个基本的阅读界面

.txt 小说 -> 小说代码模型

小说本身自带,ID 、小说名称、章节列表和小说正文,
章节列表和小说正文有一个对应关系: 章节内容范围数组
这里把小说的 ID 和小说名称合并为一个属性

class ReadModel: NSObject,NSCoding {

/// 小说ID,使用书名作为 ID

/// 小说名称
// app 里面有很多书,有 id, 不重复
let bookID:String

/// 当前阅读记录
// 用户操作

// 初始化的时候,用户没操作,设置第一个章节为阅读记录
var recordModel:ReadRecordModel?

/// 书签列表
// 用户操作
var markModels = [ReadMarkModel]()

/// 章节列表
var chapterListModels = [ReadChapterListModel]()

/// 本地小说全文
var fullText:String!

/// 章节内容范围数组 [章节ID:[章节优先级:章节内容Range]]
var ranges:[String:[String:NSRange]]!

}

1.1 模型解析
1.1.1 数据结构
1.2 视图呈现
2,计算页码
2.1 翻页
3,目录
4,书签
5,调进度
5.1 全文的进度展示与调节
5.2 当前章节的进度展示与调节
6,翻页方式
7,更改排版方式
8,长按文本复制







关键点 3:碉堡了,我大 RxSwift 的循环引用

RxSwift 的内存管理,很少通过 weak, 通常是手动管理,手动 dispose,

dispose , 即把相关对象置为 nil

Sink<Observer>

extension ObservableType {

    /**
     
     */
    public func withLatestFrom<Source: ObservableConvertibleType, ResultType>(_ second: Source, resultSelector: @escaping (Element, Source.Element) throws -> ResultType) -> Observable<ResultType> {
        return WithLatestFrom(first: self.asObservable(), second: second.asObservable(), resultSelector: resultSelector)
    }
}

做了一个简单的过滤,
去掉了拦截

一般的 RxSwift 的 Extension, 几个函数搞一下

简单的否定操作符,事件流里面的每一个事件,只需要简单处理下,

没有更多的状态管理,只需要简单的函数层次上的处理

extension ObservableType where Element == Bool {
    /// 否定操作符
    public func not() -> Observable<Bool> {
        return self.map(!)
    }
}




从 if else 开始,

事件流的逻辑控制

filter 是选取

share 是多流


事件池,当然是 RxSwift 实现的,非常好


线程锁,
异步任务,
无法保证事件完成的先后顺序


socket 多线程开发,话说那无敌的左手

不用多线程,可理解为,啥事都用好用的左右手处理

使用多线程,快多了,基本也是左右开弓

怎么怼得过那谁的三头六臂。

线程的优先级倒置,

有时候,你觉得你的 Leader 是个什么 X,
不如,换我上

线程的优先级倒置,简单理解,配置有问题

用户操作,切换线程,

线程的手动切换



解析 YYModel 源代码,学习 Runtime



提升前端素质,心中有点 B 树

Screen Shot 2020-03-28 at 8.49.18 PM.png

平衡的树,才有用,

效率要 O ( log ( n ) ), 不要 O ( n )

O ( n ) 是线性查找,也就是链表

B 树,是一颗好树

好的树,便于查找,search 操作的复杂度是 O(log (n)

好的树,增加和删除节点后,可以维持自己的结构,这个意思是,一直都便于查找。

不可维持自己的结构的树,只能爽一次

维持自己的结构的树,search 操作的复杂度保持在 O(log (n),品质始终如一

B 树,一个节点,可以包含多个 KV,有效的降低了树的高度。

B 树,有一个 degree ( n ), 也就是一个节点最多可以有 n 个内容,n + 1 个 children.

节点包含的内容,与节点孩子的关系:

n 个可比较的内容,可以分割出 n + 1 个孩子
n 个可比较的内容,可以分割出 n + 1 段范围,每一颗子树,存在于指定的范围

就像黄瓜,切四刀,可以吃五口 ( 一般的刀法下 )

B 树,不是二叉搜索树,譬如: AVL , 红黑树
B 树的孩子挺多的

B 树,是一颗平衡的树,
AVL 通过记录节点的高度,
红黑树,通过
B 树通过持有多节点

B 树的孩子挺多的
这样有效降低了层级,也就是树的高度,降下来了

从下往上生长

添加,删除

自保持,自平衡,自我维持住,

便于查找,

查找效率高

数据库,什么系统的刚需

红黑树,

要区分,就要做标记

划分 MVC , 是为了 代码 分层、 分区,隔离 不同功能的代码, 数据 流 上。
在代码 数据流向 模块间 竖起 明显的隔离线。

感觉 , 有三个 Model .

  • Service , 一统 外部数据。网络请求, 本地 数据库。

  • DataSource, 遵守 *UITableViewDataSource * Protocol , 有 ViewModel 的 感觉。
    一统 内部数据。 就是 显示数据与操作数据。 给用户看的, 和 用的。

  • Model ,数据解析 序列化。 与后台 约定的数据结构,申明的 一大堆属性,
    与简单的 业务数据 处理, mapper 出,提供 我方需要的 结构化的 数据。



前端 的 Controller

是 用户

只有 View 和 Model,
就是 视频 、 动画



MVCS
Store, Service.
配合 Aggregate Data Source.
在 dataSource 中, 处理 cell 数据。
将 数据 判断结果 甩出去。

DataSource

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    MyProductsCell *cell = [tableView dequeueReusableCellWithIdentifier: self.cellIdentifier forIndexPath: indexPath];
    cell.myProduct = self.myProductItems[indexPath.row];
    
    BOOL isCellContained = [self.tempSelectedArray containsObject: self.myProductItems[indexPath.row]];
    if (self.myProductsCtrlDataSourceBlock) {
        self.myProductsCtrlDataSourceBlock(cell, self.myProductItems[indexPath.row], isCellContained, indexPath);
    }
    
    return cell;
}

VC:


- (void)networkMyProductsCtrlStoreSuccess:(id)successData failure:(NSString *)failureStr;{
    if (successData) {
        NSArray *dataArray = (NSArray *)successData;
        if (dataArray.count == 0) {
            self.networkResult = EmptyData;
        }
        NSMutableArray *myProductsTempArray = [NSMutableArray array];
        for (NSDictionary *dict in dataArray) {
            MyProduct *myProducts = [MyProduct yy_modelWithDictionary:dict];
            [myProductsTempArray addObject:myProducts];
        }
        self.myProductsCtrlDataSource = [[MyProductsCtrlDataSource alloc] initWithItems: myProductsTempArray cellIdentifier: kMyProductsCell configureCellBlock:^(MyProductsCell *cell, MyProduct *MyProduct, BOOL isCellContained, NSIndexPath *indexPath) {
            if (isCellContained && self.myProductsTableView.editing) {
                [self.myProductsTableView selectRowAtIndexPath: indexPath animated:YES scrollPosition:(UITableViewScrollPositionNone)];
            }
        }];

相同点,内部有 各种 分门别类的数据源, 经过 外部的简单参数,
相当于 各种材料 都在 手上,非常集中 ,非常爽滴 进行 复杂运算,数据加工,
返回给外部。
很明显, 这就是一层
Model Layer.

一层,就是 一统。
高度封装,就是高度定制化。

Store

外部数据 统一处理,
网络 与 持久化数据库

包括, 各种网络请求 增删改。
好像并没有 直接的 关联。
其实 就是 直接在里面 用Net API match 几下,改下实例 存储的 临时变量, 返回给外部 结果。

Aggregate TableView DataSource

内部数据,统一处理

getter
暴露出 readonly 的 数组,供外部查询。

setter
暴露出数据操作方法。
高度封装。
相当于API 调用。
外部传几个 很简单的 参数,
内部各种数据运算出结果,返回外部。

如果是,MVC,

VC 中

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    MyProductsCell *cell = [tableView dequeueReusableCellWithIdentifier: self.cellIdentifier forIndexPath: indexPath];
    
    cell.myProduct = self.myProductItems[indexPath.row];
   // cell.delegate = self;
    if ([self.tempSelectedArray containsObject:cell.myProduct] && self.myProductsTableView.editing) {
        [tableView selectRowAtIndexPath:indexPath animated:YES scrollPosition:(UITableViewScrollPositionNone)];
    }
    return cell;
}


不同的拆分,不同的写法, 实质上是不同的 逻辑思想,不仅仅是 对应 逻辑的翻译。就是 相同业务逻辑的翻译,使用胶水代码/语法糖

MVVM. 响应式。data binding.
否则 在 JS 中,又要改 视图,又要 改数据源。

传值,每次都要改变的,
用函数行为参数,
用 argument.

状态 需要保存一会的,
用 属性。

本地搜索,
ML,
文字匹配,
文字转拼音 匹配

Mac three

local player

Realm , AudioKit

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,026评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,655评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,726评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,204评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,558评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,731评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,944评论 2 314
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,698评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,438评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,633评论 2 247
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,125评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,444评论 3 255
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,137评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,103评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,888评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,772评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,669评论 2 271

推荐阅读更多精彩内容