问题背景
使用 opencode 时,退出后会提示一句:
1 |
|
但如果终端窗口关得太快,或忘记复制,这个会话 ID 就丢了,导致无法恢复之前的会话。
我们要实现的就是知道的会话记录,并且可以随时重续之前的会话。如:
1 | === 最近的会话 (第5/13页,共122条) === |
使用 opencode 时,退出后会提示一句:
1 |
|
但如果终端窗口关得太快,或忘记复制,这个会话 ID 就丢了,导致无法恢复之前的会话。
我们要实现的就是知道的会话记录,并且可以随时重续之前的会话。如:
1 | === 最近的会话 (第5/13页,共122条) === |
日常开发中,AI 编码工具(如 Claude Code、Cursor)只能在电脑终端使用,离开电脑就无法交互。当你在手机上想让人 AI 执行任务(如查日志、改代码、跑脚本),或者想通过微信/Telegram 远程操控时,就缺少一个桥梁。
cc-connect 正是解决这个问题的工具——它连接 AI 编码工具和即时通讯软件(微信、Telegram),让你在手机上发消息,电脑上的 AI 接收后执行并返回结果。
实现的效果类似如下:
1 | cc-connect |
1、异常数据的发现及补充、日志分类优化
2、日志数据的文件保存、滚动存储
3、日志文件上传
4、日志回捞
| 序号 | 目标(Target) | ||
|---|---|---|---|
| 1 | app | ||
| 2 | sdk | 三方库 | |
| 3 | h5 | 网页 |
| 序号 | 类型 | 描述 | |
|---|---|---|---|
| 1 | api_app | app中的网络请求 | |
| 2 | api_app_cache | app中的网络请求的网络缓存请求 | |
| 3 | api_buriedPoint | 埋点的网络请求 | |
| 4 | sdk_other | sdk的各种事件(初始化等) | |
| 5 | sdk_api | sdk中的网络请求 | |
| 6 | dart | 语法 | |
| 7 | widget | 视图(布局像素越界等) | |
| 8 | click_other | 点击、 | |
| 9 | click_share | 分享 | |
| 10 | native_route | 路由/跳转 | |
| 11 | h5_route | 与网页跳转有关 | |
| 12 | h5_js | js交互 | |
| 13 | monitor_network | 监控:网络类型变化 | |
| 14 | monitor_lifecycle | 监控:生命周期变化 | |
| 15 | buriedPoint_other | 埋点数据生成等 | |
| 16 | im | IM | |
| 17 | heartbeat | 心跳 | |
| 18 | other | 其他 |
| 序号 | 目标(Level) | 描述 | |
|---|---|---|---|
| 1 | Normal | 正常信息(目前用于请求开始) | |
| 2 | Success | 成功信息(目前用于请求结束:成功) | |
| 3 | Warning | 警告信息(目前用于请求结束:报错) | |
| 4 | Error | 错误日志(目前用于请求结束:失败) | |
| 5 | Dangerous | 危险(处理白屏等) | 一般会进行额外的埋点 |
| 序号 | 列表 | 标志 | 包含 |
|---|---|---|---|
| 1 | 全部 | all | 所有 |
| 2 | 警告 | warning | 所有的警告 |
| 3 | 错误 | error | 所有的错误 |
| 4 | 接口 | api | api_app、api_cache(不包括sdk_api、api_buriedPoint) |
| 5 | 点击 | click | click_share、click_other、h5_js |
| 6 | 路由 | route | navite_route、h5_route |
| 7 | 网页 | H5 | h5_route、h5_js |
| 8 | sdk | sdk | sdk_api、sdk_other |
| 9 | code | code | dart、widget |
| 10 | 埋点 | buriedPoint | api_buriedPoint、buriedPoint_other |
| 11 | 监控 | monitor | monitor_lifecycle、monitor_network |
| 12 | 其他 | other | other |
| 13 | api结果 | api_result | type=api_app & level != Normal |
| 14 | im | im | 本地缓存消息、历史消息、收到的消息等 |
| 15 | 心跳 | heartbeat |
就是**”基础库”或者“基础组件”,**意思是把代码重复的部分提炼出一个个组件供给功能使用。
使用:Dialog,各种自定义的UI控件、能在项目或者不同项目重复应用的代码等等。
目的:复用,解耦。
依赖:组件之间低依赖,比较独立。
架构定位:纵向分层(位于架构底层,被其他层所依赖)。
就是**”业务框架”或者“业务模块”**,也可以理解为“框架”,意思是把功能进行划分,将同一类型的代码整合在一起,所以模块的功能相对复杂,但都同属于一个业务。
使用:按照项目功能需求划分成不同类型的业务框架(例如:注册、登录、外卖、直播…..)
目的:隔离/封装 (高内聚)。
依赖:模块之间有依赖的关系,可通过路由器进行模块之间的耦合问题。
架构定位:横向分块(位于架构业务框架层)。
其实组件相当于库,把一些能在项目里或者不同类型项目中可复用的代码进行工具性的封装。
而模块相应于业务逻辑模块,把同一类型项目里的功能逻辑进行进行需求性的封装。
1、产生背景:
所有模块代码都编写在一个项目中,在项目越来越大后,测试/使用某个模块或功能,需要编译运行整个项目,麻烦。
2、组件化思路:
将每个模块作为一个组件,加一个中间层来协调各个模块间的调用,所有的模块间的调用都会经过中间层中转。(只让其他模块对中间层产生耦合关系,中间层不对其他模块发生耦合。)
3、组件化好处:
业务划分更佳清晰,新人接手更佳容易,可以按组件分配开发任务。
项目可维护性更强,提高开发效率。
更好排查问题,某个组件出现问题,直接对组件进行处理。
开发测试过程中,可以只编译自己那部分代码,不需要编译整个项目代码。
方便集成,项目需要哪个模块直接通过CocoaPods集成即可。
完整的项目:https://github.com/dvlproad/033-Data-Notification-iOS
启动时候注册关系表,执行时候遍历关系表
1 | // 核心思想1:注册,以统一管理,后续调用时候,从注册表中查询并执行对应的操作 |
缺点:入参,取参不明显。
哈希的本质 哈希冲突的解决:链地址法和开放寻址法。
问:使用多代理模式实现一个类似通知的功能,请问应该用什么来什么结构来存储代理和类的结构?
答:在多代理(Multiple Delegates)模式下,要存储多个代理对象,并且这些代理对象通常是弱引用,以防止循环引用。实现一个类似通知的功能,可以考虑使用 NSPointerArray 或 NSMapTable 来存储代理对象。如果你只是想管理多个代理对象,NSPointerArray 是更简单的选择。如果你需要将某个对象(如 UIViewController)与多个代理进行关联,NSMapTable 更合适,所以我们选择 NSMapTable 。
将要执行的方法统一放到类中,通过protocal或者类。
1 | // 根据 protocal 查找 class |
模块协议拆分为多个子协议,用户
网络处理协议、数据管理协议(单独)、用户界面更新协议
改进思想
1 | // 1、发起请求(LoginViewController) |
上述代码还可以演变成实现通知的一种形式(当注入的类中,有多个类都遵循了(实际上不应该出现)模块协议时候)
1 | // 1、发起请求(LoginViewController) |
下列图片来源于 《架构分层.graffle》 中的【三、模块间设计】
跳转的使用示例:
通知的实现示例:
跳转 + 数据变化后内部通知的实现示例:
项目示例:CJStandardProject 中 CTMediator+CJDemoModuleMain.h
核心思想:直接执行,让系统通过反射来找到要响应事件的类和方法
1 | void exec(target, action, params) { |
实现示例:


通过前一个页面的已获得数据,对所进入的新页面中的数据进行预填充。
eg1:商品列表 —> 商品详情:使用数据携带
eg2:愿望星count个数:通过count的0与非0,知晓所进入的页面初始状态更有可能是哪种状态
1、如果所进入的页面没有缓存数据,则携带的数据在进入的时候直接使用,后台接口返回实时数据后,再更新
2、如果所进入的页面有缓存数据,则携带的数据只能给缓存数据,而不能是后台接口返回的实时数据
通过缓存框架,将数据缓存起来(key需携带uid),下次界面展示时候,优先从缓存中获取。
附:缓存框架详见:flutter_cache_kit使用文档
建立Service层,管理用户所有数据的变动。
好处:与普通数据的缓存相比,能够在将来增加数据变动时候,通过本地通知系统,告知相关页面更新相应数据,而不用等到重新下载后才能显示已知道会更新的数据。
使用要点:
修改的时候,同步数据;下次界面需要数据,优先从用户管理服务中获取初始数据
eg1:用户愿望单收藏、商品收藏、品牌收藏、足迹数据
eg2:用户会员中心数据
如果没有从前一页携带数据过来,则使用与产品约定的默认数据来加载。
1 | if (携带数据) return 携带的.orderCount; |
对携带的数据与默认数据中相同的对象,使用携带的数据替换掉,一起整合成初始默认数据(携带的数据优先级高)。
页面处理优化及处理过程中的内容(图片)加载优化如下图所示:
图片来源可查看 《app启动与页面加载.graffle》中的【二、页面加载】版面
从上图中,可以看出弱网情况下,我们可以针对不同的网络:做不同超时,重试设置,以及取不同质量的图片数据。
图片优化,请查看我的另一篇文章:图片框架.md
图片高磁盘占用,请查看我的另一篇文章:《高磁盘占用的排查与优化.md》
api数据缓存,请查看我的另一篇文章:《网络框架.md》
弱网优化,请查看我的另一篇文章:《弱网优化空间探索.md》
架构模式:架构模式的出现是为了管理复杂的应用程序,这样可以在一个时间内专门关注一个方面。例如,您可以在不依赖业务逻辑的情况下专注于视图设计。同时也让应用程序的测试更加容易。同时也简化了分组开发。不同的开发人员可同时开发视图、控制器逻辑和业务逻辑。我们经常说的MVC架构、MVVM架构属于此类。
框架:这个最好理解了,通常是代码重用。框架与设计模式的概念容易弄混,两者有相似之处,但却有着根本的不同。设计模式是对在某种环境中反复出现的问题以及解决该问题的方案的描述规范,它比框架更抽象;框架为已经解决问题的具体实现方法,能直接执行或复用;设计模式是比框架更小的元素,一个框架中往往含有一种或多种设计模式。Xcode自带的Foundation、UIKit,以及我们经常使用的AFNetworking,MJExtension,SVProgressHUD就属于这一类。
设计模式:设计模式可以通俗的理解为实现/解决某些问题,而形成的解决方案规范。增加代码的可重用性,让代码能更容易理解和可靠。我们通常说所的代理模式、迭代器模式、策略模式就属于这一类。对各种设计模式的了解可以帮助我们更快的解决编程过程中遇到的问题。
三者关系:架构(动词)>框架>设计模式。
软件通过架构,可以设计出很多不同的框架。在一个框架中,也可以使用很多的设计模式。设计模式不是哪儿哪儿都可以用的,只有当出现了某一特定的问题时,才利用设计模式去解决。设计模式不是用的越多越好,在维护的时候,过多的设计模式会极大的增添维护成本。
1 | 背景 |
问:我不要频繁安装+卸载来安装我想要的包。我能不能同时装一个测试环境和正式环境?
答:不行。安装两个包,严格上来讲,不用想了,不行。具体原因涉及应用id,及app根据该id配置了各种三方key环境(除非你连三方的也都额外提供一套),太详细的道理我就不讲了。”不行“就对了。但是,我能让你安装一个包,却使用不同环境的功能。或者额外安装一个除与应用id相关的功能外其他皆一样的安装包。
一、切换环境常见的场景为:
1、测试时候:换环境不用一直下载;
2、演示时候:某个环境使用不了,不用重新下载;
3、抓包时候:想切换代理,不用重新打包安装;
见下文:app环境与切换
二、额外安装一个除与应用id相关的功能外其他皆一样的安装包的使用场景:
1、app中不能接受切换环境,即严格限制一个包只能有一个环境
不允许切换环境的考虑原因:
测试包不允许切到生产:怕脏数据
生产包不允许切到测试:测试环境不是外界用户真正需要的
见我的另一篇文章:《iOS的重签名.md》


流程图:《开发工具(含环境切换等).graffle》中的【一、切换环境】版面

未截取到的其他埋点参数见我的另一篇文章:《埋点规范_结构.md》

| 支付 | 支付宝 | ||
| 认证 | 实名认证、真人认证 | ||
| 选择器 | 生日选择、活动时间选择、项目多选 | ||
| 分享 | |||
| 关键页面入口 | 个人主页、发布页、引导页、完善性别/生日 | ||
| 位置相关 | 地图位置选择 |
快捷入口


《h5js.md》
见我在”安全”专题里的另一篇文章:《安全/开发工具的安全性保障.md》
目前的脚本操作如下:

其中自定义的脚本操作有如下:

文档:《Charles.md》
dio添加代理
1 | (dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = |