首页 > 其他分享 >高效处理 iOS 应用中的大规模礼物数据:以直播项目为例(1-礼物池)

高效处理 iOS 应用中的大规模礼物数据:以直播项目为例(1-礼物池)

时间:2024-11-29 14:02:46浏览次数:7  
标签:zip 为例 self iOS json let model 礼物

引言

在现代iOS应用开发中,处理大规模数据是一个常见的挑战。尤其实在直播项目中,礼物面板作为展示用户互动的重要部分,通常需要实时显示海量的礼物数据。这些数据不仅涉及到不同的区域、主播的动态差异,还需要保证高效的加载与渲染,以提供流畅的用户体验。

本篇博客将以直播应用中的礼物面板为例,深入探讨如何高效地管理和处理这些庞大的数据。我们将分享一种基于“礼物池”设计的解决方案,从服务端下载并解压存储数据,再根据实时的面板数据提取礼物信息的方式,确保数据的高效存取与更新。同时,还将讨论如何通过合理的本地缓存和更新机制,进一步提升应用性能,并优化用户体验。

如果你也在处理类似的大数据问题,或者正在开发类似的直播应用,本篇博客将为你提供实用的思路和解决方案。

架构介绍

上面所展示的礼物面板中的所有礼物数据,大概有2-4M,这对于一个请求来说应该算是一个非常大的数据量,而由于针对不同地区不同的主播所展示的礼物也不同,那么我们就可以需要频繁的来请求整个礼物面板的数据,每次都请求这么大的数据显然这并不理智。为此我们可以考虑将它分割成两部分:礼物池和礼物面板。

  • 礼物池:礼物池内存放的是所有的礼物该数据由一个接口下发为固定数据几乎不会变动出发有新类型的礼物加入。
  • 礼物面板:根据层级返回每个一级tab对应下的二级tab,而二级tab下只需要包含礼物的id。

礼物池的数据结构如下:

截图中只展示了礼物列表中的一个礼物,而且数据并没有完全展示出来,那这组json来说一共有1600个这样的礼物,整个礼物池大小为3.8M。

礼物面板的数据结构如下:

我们抛开一级tab直接看二级tab下的gifts里面只保存了礼物的id。这也就大大减小了礼物面板的数据大小、获取到礼物的id之后再从礼物池中读取礼物的完整数据。这样即使频繁请求礼物面板接口也不会造成很大性能影响。

礼物池的存储与更新机制

礼物池json文件的压缩包是通过一个接口获取的,该接口会返回礼物池的压缩文件路径,以及礼物池的当前版本号。

等获取到该接口的数据之后,我们需要做的有三件事:

  1. 对比礼物版本号与当前本地礼物版本号,如果相等则不需要更新json文件,直接加载本地json。
  2. 否则将新的礼物池版本号进行保存。
  3. 开始下载新的礼物池压缩包。
    /// 请求礼物池数据
    private func requestGiftPoolData() {
        MWNetworkHelper.request(endpoint: MWAPINormalEndpoint.api_giftPoolData, parameters: [:], modelType: MWGiftPoolModel.self) {[weak self] model, data, error in
            guard let self = self else { return }
            guard let model = model else {
                MWLogHelper.error("请求礼物池数据失败 err:\(error?.description ?? "")", context: "MWGiftPoolManager")
                return
            }
            // 是否需要下载新的zip
            if model.version == MWUserDefaultsAppHelper.giftPoolVersion {
                self.loadLocalJson(model: model)
                return
            }
            // 将礼物版本号保存到本地
            MWUserDefaultsAppHelper.giftPoolVersion = model.version
            
            self.startDownloadZip(zip_url: model.default_zip)
        }
    }

本地读取

如果本地已经有了礼物池数据,且当前礼物池版本号与本地版本号相同时,那么我们就可以直接读取本地json,但读取本地json时并不一定就会成功,当读取失败,或者转换失败时,需要重新进行下载。

    /// 加载本地json文件
    /// - Parameter model: 礼物池model
    private func loadLocalJson(model:MWGiftPoolModel) {
        // 本地版本号和服务器版本号一致 直接读取
        if let jsonPath = MWUserDefaultsAppHelper.giftPoolJsonPath {
            MWLogHelper.info("读取本地json path:\(jsonPath)", context: "MWGiftPoolManager")
            // 拼接document路径
            let document = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first ?? ""
            let filePath = document.appending("/\(jsonPath)")
            do {
                let jsonData = try Data(contentsOf: URL(fileURLWithPath: filePath))
                let json = try JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers)
                if let jsonDict = json as? [String:Any] {
                    MWLogHelper.info("读取本地json成功", context: "MWGiftPoolManager")
                    self.jsonDict = jsonDict
                    self.convertModel(jsonDict: jsonDict)
                }
            } catch {
                MWLogHelper.error("读取本地json失败 重新下载 err:\(error.localizedDescription)", context: "MWGiftPoolManager")
                // 转换失败也需要重新下载
                self.startDownloadZip(zip_url: model.default_zip)
            }
            
        }
    }

下载zip解压并存储

如果json文件需要更新或者首次下载,那么在下载完成之后需要将json写入到本地,供以后直接读取。

    /// 开始下载zip
    private func startDownloadZip(zip_url:String) {
        // 获取zip文件名
        let lastPathComponent = zip_url.components(separatedBy: "/").last ?? ""
        MWNetworkHelper.downloadFile(url: zip_url,file: "giftPool",fileName: lastPathComponent) { progress in
            
        } completion: {[weak self] path, error in
            guard let self = self else { return }
            if let path = path {
                MWLogHelper.info("下载zip成功 path:\(path)", context: "MWGiftPoolManager")
                self.startUnZipFile(zipURL: path)
            } else {
                MWLogHelper.error("下载zip失败 err:\(error?.description ?? "")", context: "MWGiftPoolManager")
            }
        }
    }

此时下载完成时一个zip包,借助Zip进行解压,解压完成之后获取到json,构建数据模型,并将json输入写入到document文件夹,保存相对路径。

    /// 开始解压
    private func startUnZipFile(zipURL: URL) {
        do {
            let unzipUrl = try Zip.quickUnzipFile(zipURL)
            // 获取文件名
            let lastPathComponent = zipURL.lastPathComponent.components(separatedBy: ".").first ?? ""
            MWLogHelper.info("解压zip成功 path:\(unzipUrl)", context: "MWGiftPoolManager")
            let jsonURL = unzipUrl.appendingPathComponent("\(lastPathComponent).json")
            MWLogHelper.info("拼接路径 path:\(jsonURL)", context: "MWGiftPoolManager")
            // 将文件路径存储起来(document以后)
            let filePath = lastPathComponent.appending("/\(lastPathComponent).json")
            MWUserDefaultsAppHelper.giftPoolJsonPath = filePath
            
            let jsonData = try Data(contentsOf: jsonURL)
            let json = try JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers)
            if let jsonDict = json as? [String:Any] {
                self.jsonDict = jsonDict
                self.convertModel(jsonDict: jsonDict)
            }
        } catch {
            MWLogHelper.error("解压zip失败 err:\(error.localizedDescription)", context: "MWGiftPoolManager")
        }
    }

json数据转哈希表

从上面的代码中我们还可以看见一个比较重要的方法covertModel(),该方法接收的就是礼物池的原始数据,然后我们通过遍历礼物池下的gifts礼物列表来构建礼物的数据模型。模型构建完成之后,我们使用键值对的形式将礼物的数据模型和礼物id成对的保存到表中。

    /// 礼物池(键值对形式)
    public var giftPoolMap:[Int:MWGiftModel] = [:]
    /// 转model
    private func convertModel(jsonDict: [String:Any]) {
        if let giftPools = jsonDict["gifts"] as? [[String:Any]] {
            // 遍历giftpools创建一个字典
            for giftDict in giftPools {
                let giftModel = MWGiftModel(JSON: giftDict)
                guard let giftId = giftModel?.giftId else {
                    continue
                }
                giftPoolMap[giftId] = giftModel
            }
        }
        giftPoolCallback?(giftPoolMap)
        MWGiftLoader.shared.startLoadGift(giftPoolMap: giftPoolMap)
        MWLogHelper.debug("转成模型 :\(giftPoolMap.count)", context: "MWGiftPoolManager")
    }

方便后续从礼物池中直接读取礼物模型。

结语

在本篇博客中,我们探讨了直播应用中礼物面板的核心架构设计,以及如何通过礼物池实现高效的数据加载与更新。这种基于本地缓存和远程更新机制的方案,不仅提升了应用的响应速度,还有效降低了网络请求对性能的影响。

然而,礼物池只是整个礼物实现的一部分,为了真正的完成礼物展示,还需要处理礼物面板的数据解析,动态筛选,以及与礼物池的高效匹配。在下一篇博客中,我们将深入解析礼物面板的实现细节。

标签:zip,为例,self,iOS,json,let,model,礼物
From: https://blog.csdn.net/weixin_39339407/article/details/144102940

相关文章

  • 如何禁止IOS移动端网页橡皮筋的效果?
    在iOS移动端网页上禁用橡皮筋效果(也称为“overscroll”或“bounce”效果),主要有以下几种方法:1.使用CSS属性overscroll-behavior:这是最推荐和最现代的方法。overscroll-behavior属性允许你控制浏览器在滚动到边界时的行为。body{overscroll-behavior-y:contain;/*......
  • iOS 升级到XCode15运行项目导致XCode闪退
    XCode升级到15之后运行现有的老项目会导致XCode闪退,这种闪退还很奇怪,运行第一次的时候不闪退,运行第二次的时候XCode闪退必现,这么神奇的现象着实让人摸不着头脑,XCode闪退时候的报错信息如下:Thread0Crashed::Dispatchqueue:com.apple.main-thread0CallstackAnalysis......
  • 以Deformable_DETR为例说明训练过程
    以Deformable_DETR为例说明使用服务器训练过程下载程序文件根据论文提供的github地址fundamentalvision/Deformable-DETR:DeformableDETR:DeformableTransformersforEnd-to-EndObjectDetection.下载zip到本地租用服务器在autodl平台租用服务器,申请账号氪金之后去市场......
  • iOS手机免越狱群控系统:实现同步投屏与多设备管理的新工具
    随着移动设备在企业和个人生活中的广泛应用,对多台iOS设备进行集中管理和控制的需求日益增长。传统的越狱方式虽然能解锁更多功能,但伴随着系统稳定性下降、安全性减弱等风险。相比之下,iOS免越狱群控系统提供了一种更为安全、高效的解决方案。本章将详细介绍如何使用这种新工具来实......
  • C++练级计划-> 《IO流》iostream fstream sstream详解
    如果是想全部过一遍就看完,如果想具体的了解某一个请点目录。因为有三种流的使用可能内容多 目录流是什么?C++IO流(iostream)io流的注意事项cin和cout为什么能直接识别出类型和数据fstreamfstream的使用方法: 1.以二进制打开文件并写入和读取2.以文本打开文件并读取或写......
  • iOS系统资源调度机制解析
    在开发高性能iOS应用时,深入了解并合理利用iOS系统的资源调度机制至关重要。资源调度涉及到线程的创建与管理、任务的分配与执行、以及进程优先级的调整等多个方面。本文将重点介绍iOS系统中的核心资源调度机制——GrandCentralDispatch(GCD),并深入探讨其在多线程管理和性能优化中......
  • Vue-axios
    axios请求基于promise的Http请求库//本地安装npminstallaxiosimportaxiosfrom'axios';<!--通过CDN的方式引用axios--><scriptsrc="https://unpkg.com/axios@1.4.0/dist/axios.min.js"></script>常用的HTTP请求方式:GET:获取数据POST:新增数据PUT:......
  • 中柏N100小主机入手使用问题记录,MiniPC驱动安装,功耗调整,BIOS设置
    中柏N00II简介双11在京东买的,439块,用了政府补贴。看到有其他人买到更低。等了10多天才到。有线网口:单千兆,Realtek。WiFi:IntelAC9560带蓝牙5.1显示接口:HDMIx1,Displayx1USB2.0x2,3.1x2拆机拆下,底部的四颗螺丝,有一个角有个伸缩拉手,拉以下就打开了。配的固态硬盘......
  • vue3+vite打包配置,并部署nginx,解决部署之后axios请求跨域
    配置base配置base避免打包部署到服务器上后可能会报404,无法正确的获取的资源。//vite.config.tsexportdefaultdefineConfig({ base:"./"})配置路径别名//vite.config.tsimport{defineConfig}from"vite";importvuefrom"@vitejs/plugin-vue";//配置组件路......
  • iphone无需越狱,iOS中控技术实现电脑控制手机
    相信不少朋友都有过这种需求吧,就是想通过电脑来控制自己的手机。特别是对于一些工作需要用到大量手机的运营人员来说,如果能够通过电脑批量操作的话,那效率一下子就能提升好几个档次了。而今天要分享的就是这样一种无需越狱,就能实现电脑控制手机的iOS中控技术。在开始之前,先给......