ios搜索算法
A. 如何在iOS 6 时代的App Store 中 脱颖而出
美国时间9月12日,Apple推出了举世瞩目的新一代iPhone,与之俱来的还有之前已放出数个测试版本的iOS 6。在iOS 6中,Apple对App Store做出的改变成为了开发者们关注的焦点。为此MIX智游汇团队整理了一个关于手游应用商店优化(ASO)的白皮书,里面总结了各种ASO优化技巧和注意事项,并在最后提供了一页供开发者们打印出来时时对照的checklist。
ASO (App Store Optimization, 应用商店优化) 是与传统的 SEO (SearchEngine Optimization) 优化相对应的:作为开发者,今天我们面临的困境是,如何在App Store上的数十万个 App 中让自己精心开发的产品崭露头角?新的 App Store环境中,可以采取哪些方面的ASO策略应对?
根据上面的几条重要变化,我们发现在可能采用的新设计方案中,开发者们能迅速抓住的稻草并不多,具体来说有以下一些ASO应对策略建议。
时刻关注新的排名和搜索算法
重视图标设计
认真制作截图
合理设置名称和关键字
建立为iOS 6优化的移动页面
关注 Genius智能推荐的新变化
希望这些建议能够帮助各位手游开发者更好地利用App Store获得更多的用户和收益。
B. iOS:常用排序算法
冒泡排序是相邻数据进行两两比较,假设升序排序,则一趟排序下来,就会有一个最大数产生在数组最末端。因为有 n 个数据需要进行比较,而每一趟排序需要遍历n个数据,所以时间复杂度为O(n^2)
快速排序是定下一个基准数(一般默认定义最左侧数据为基准数,可以理解为参照数),每一趟排序都需要从左(角标 i)右(角标 j)两侧开始像中间进行排序,因为基准数定义在左侧,一般先从右侧开始向左侧移动,j--;遇到小于基准数的暂停,左侧开始向右移动,i++;遇到大于基准数的暂停;然后交换i 和 j 所对应的数据。当i和j相遇的时候,则将相遇值与基准数进行交换,一趟排序结束。时间复杂度是O(log2 n)
C. iOS 开发中都会使用哪些算法
一般开发用不上,熟悉了语法规则之后,就是砌积木而已。需要什么功能就砌上去。数学逻辑初中水平都用不上。。
D. 你所不知道的iOS从古到今经典Bug!
iPhone手机的成功得益于它背后的iOS系统,绚丽的界面,流畅、优秀的用户体验,让多少人对它倾慕,纷纷投向它的怀抱,是大部分用户购买iPhone的关键原因之一。当然,世上从来没有十全十美的东西,iOS系统也是如此。iPhone作为全球最流行的智能手机,当发生问题的时候总是能引起更多的关注。这些年来,每一代iOS系统升级,总会带来一些意想不到甚至是啼笑皆非的Bug,下面我们不妨一起来看看。
一、快捷菜单同时按会假死
近段时间,有果粉在论坛曝光了iPhone的一个诡异Bug,只要iPhone上划屏幕呼出控制中心,并同时按下AirDrop与拍照按钮(或计算器按钮),就会造成手机假死,并且过一会儿自动重启。看到此消息后,不少果粉出于好奇也进行了尝试,都纷纷中招了。
之后有技术党表示该Bug会在iOS 10-iOS 10.2.1系统中出现,目前苹果已经意识到这一问题,在最新的iOS 10.3 Beta版系统已经修复了这个问题,而正式版iOS 10.3会很快推送。为了避免造成不必要的麻烦,建议大家还是不要出于好奇心去尝试了。
二、1970变砖Bug
关于这个Bug,应该是果粉们最无奈的Bug,据说当时德国某个科技网站的编辑,无意间将iPhone的日期设置到了1970年1月1日,然后,在他重启的时候,好端端的iPhone变砖了,而且连接电脑并进入到DFU模式也无法启动。这真的是No zuo no die。
当时有大神指出凡是运行iOS 8以上系统的64位设备都有可能遇到这一问题,32位设备则是安全的,当时解决这个问题也是比较棘手。不过之后苹果公司意识到了这一Bug的存在,在之后的版本中已经修复了,目前设备还处于iOS 8-iOS 9.3系统之间的用户还是不要去尝试了,变砖后解决起来比较麻烦。
三、神奇字符短信导致崩溃
在iOS 8系统的时候,有用户在短信应用中发现这个Bug:通过iMessage发出一段阿拉伯字符就会让接收人的iPhone崩溃,并让信息应用无法打开。据老果粉透露,其实这个Bug由来已久,早在iOS 6系统就出现过,在iOS 7中得到修复,没想到iOS 8又再次出现。
虽然该Bug并不会影响用户的日常使用,而且只需要别人再发一条正常的短信就可以恢复正常。之后有技术党指出,该Bug的产生原因应该和iOS的通知中心预览功能处理Unicode字符时存在缺陷有关。它会展示短信的一部分,但其中如果包含上述字符,则属于缺失字符,导致崩溃并且可能牵连到整个系统,庆幸的是这个Bug对中文的iOS系统没有影响。
四、“连夜模式”导致闹钟失灵
这是一个出现在iOS 9.1系统的Bug,当时有用户打开了“连夜升级”模式(Overnight updates),睡觉时刚好收到了新系统的推送然后设备自动进行升级。第二天醒来发现此前设置的闹铃会失效。在当时导致了不少使用iPhone的上班族,因为没有听到闹钟睡过头而上班迟到。
针对这个Bug,有资深果粉表示,其实这个iOS闹钟Bug问题由来已久,早在2010年的iOS 4上就已经出现过由于日历应用调整导致闹铃失效的问题。而在iOS 9.0.1更新时,官方也表示针对几处有关闹钟的Bug进行了修复,然而这个“连夜更新”模式中的闹钟Bug却被忽略了。各位上班族还是要养成良好的睡眠习惯不要太依赖闹钟。
五、避开屏幕锁进入系统
即使被誉为最安全的iOS系统,也被人找到方法绕过安全验证直接进入系统,这是在iOS 8时的一个Bug。当时如果你的手机没有调用出指纹识别功能,而设置的是数字密码,这个时候只需要把手机连接数据线插上电源,然后在锁屏状态下按住Home键呼出Siri语音,然后只需随便问Siri一个问题,在Siri回答的同时按下Home键,向右滑动屏幕解锁即可进入系统。
当然,这个Bug也并非100%成功,不过上述操作成功率还是比较高的。而且这样的Bug属于威胁到安全性的漏洞,对于iPhone 5s之前的那些不具备指纹识别的iOS设备存在较大安全隐患。即使该Bug在后续的版本中已经修复,但对于iOS以安全着称的系统出现这样的情况实在不应该,这样的Bug难免会让那些还停留在iOS 8的老设备的用户无法放心。
六、苹果商店无法搜索各大App
在2016年5月份的时候,不断有网友表示苹果商店无法搜索淘宝、微信、微博、携程等大批知名互联网品牌APP,相反搜索显示到的是其他App,当时不仅是中国地区,美国。澳大利亚等地都出现过这样的情况,而这些知名大品牌App,不可能是因为安全问题而被苹果下架,很显然不是苹果人为操作的。
针对这个Bug,当时外界猜测了两种情况:第一种情况是由于苹果在对其搜索算法上进行了调整,不过在调整的时候意外的出现了未知Bug,导致部分关键词搜索结果和App的关键词覆盖;第二种情况是苹果Apple美国加利福尼亚州库总部服务器那段时间遭到了病毒攻击,当然苹果并未针对此Bug作出回应,而是尽快修复了这个Bug。
七、输错密码导致“永久”停用
一般情况下,智能手机屏幕解锁都有防护措施,为了防止不法分子试出密码,一旦输入多次密码错误后,手机会停用一段时间,而且随着输入的次数越多,每次停用的时间也会越来越长。在前两年,有网友发出图片显示自己的iPhone已停用22990398分钟,换算下来大概是四十五年,对于一款寿命为四五年的电子产品来说,几乎算的上“永久”停用了。
当时有技术党表示,这应该属于一个系统Bug,然而这种情况非常少,所以苹果官方也一直没有给出回应以及解决方案。当然出现这种情况唯一的办法是只能刷机了,所以大家平时使用iPhone的时候要有备份手机的习惯,避免出现意外。
总结
古语有云,金无足赤,人无完人,即使是独步天下的iOS也并非完美无缺,它曾经也有过这么多让人啼笑皆非的Bug。但瑕不掩瑜,即使出现过这些Bug也不能否认iOS是非常出色的系统,而且每次出现Bug后,官方也会很快回应,并且不断进步和完善。在这里也要提醒大家,如果大家在使用的时候出现这些或其他Bug,尽量找苹果官方售后,避免自己折腾或者找一些不靠谱的修理店导致出现更大的问题。还有定时对手机进行备份,能够在出现问题时及时恢复,不至于全部清除一无所有。
看完觉得好的请点一下喜欢,觉得有意思的可以打赏一下。
E. ios 算法整理(OC版本实现)
原始待排序数组| 6 | 2 | 4 | 1 | 5 | 9 |
排序完毕,输出最终结果1 2 4 5 6 9
以上算法的调用示范
F. 如何做 ASO 优化
如何做好ASO优化无疑是近一两年来做推广时最热的话题,无论是渠道的推波助澜还是kpi考核之痛,就像SEO兴起时一样,越来越多的人开始接触了解。接下里的内容便给大家分享自己在做苹果商店推广过程中的一些实战经验,教大家做好ASO优化。
一、ASO 是什么
【基础功能】:
实时热搜:可以查看当前最新的热词搜索,可用来辅助做关键词挑选。
搜索指数排行:按照分类对高频关键词进行了搜索指数的排行,可以看到每个分类下搜索频率最高的关键词以及对应的搜索数量及排名第一的产品。
ASO优化助手:在设置应用名称+关键字时,可以用来检测是否超过规定字符。
【核心功能】:
实实时排名:可以根据日期看到产品在类目下的排名情况
关键词|ASO: 不仅可以看到已覆盖的关键词数量,以及出现在TOP10的数量。还可以看到每个词的实时变化情况。包括(排名、搜索结果、搜索指数)
竞品对比:可以看到优化后,和竞争对手的差距,结果你懂的。
推荐理由:
综合来说是一款很不错的ASO优化辅助工具,免费注册使用且数据更新较快,集合优化了多种功能。
G. ios SKU 组合算法
通俗来讲,一个SKU 就是商品在规格上的一种组合,比如说,一件衣服 有红色 M号的 也有蓝色 L号的 ,不同的组合就是不同的SKU
近段时间,刚好遇到要在商品详情页购买商品的时候,实现选择不同规格组合的sku,预判无库存sku选项置灰,减少客户不必要sku的选择。
网上搜寻了一大批有关sku选择算法的文章,然后被各路大神的一顿操作秀得一脸懵逼,简单来说就是没看懂。。。
当然基于以上参考得到的灵感,终于总结出来了一种简单易用通俗易懂的sku选择算法,思路简单,唯一的bug是sku数据有n多层的时候,计算量大耗内存。当然现在手机的运算能力都是杠杠的,正常来说商品的sku也不会有几十上百层那么夸张。
接下来我先捋一下思路吧!
Tips: sku 有三种状态,可选(正常),不可选(置灰),已选中(高亮),
一,sku算法初版:计算所有sku的组合 与 有库存sku的组合的交集,交集里面的sku为可选项,反之其他sku为不可选。
1.计算所有sku的组合-->集合A
A = ["34,61,66" , "34,61,67" , ......]
2.计算有库存的sku的组合 -->集合B
一般是从后台服务器返回的 eg:
3. 计算集合A与集合B的交集,交集里面的所有元素就是初始时所有可选sku ID ,反之其他sku ID就是置灰(无库存不可选状态)
4.以上三步就是简易的sku算法核心思路,弹出规格框时,计算集合A和集合B的交集,得到初步赛选结果,告诉客户,哪些sku无库存不可选置灰显示,可选的为正常状态显示,减少客户做不必要的选择操作。
5.当然,细心的你很快就会发现这样的sku算法会导致无法判断出,已选sku的兄弟节点是否可选的bug。
二,优化兄弟节点的可选状态判断bug
1.如上图 已选Platform 属性的 34,长度属性的 62 , 我们要判断的已选sku兄弟节点属性分别是Platform 属性的 35,长度属性的 61。
2.即:
要判断 长度属性的 61是否为可选,就要判断,34,61这样的组合是否属于有库存组合里面子集,是:可选,不是: 不可选。
同理:
要判断 Platform 属性的 35是否为可选,就要判断,35,62这样的组合是否属于有库存组合里面子集,是:可选,不是: 不可选。
3.细心的你肯定发现了规律,34,61 或者 35,62 这样的组合都有一个共同点
即:包含n个已选skuID,n = 已选sku个数-1 .
三,计算兄弟节点是否可选
1,计算已选sku ID 同类属性的组合 ==集合C 即:计算Platform 属性和长度属性的组合
集合C = ["34,61","34,62","35,61","35,61","35,62"]
2.计算已选skuID的子集 ==集合D 即:计算 [34,62]的子集
集合D = ["34","62","34,62"]
3.从集合D里面筛选出,含有n个已选skuID的子集(n = 已选sku个数-1 )==集合E
集合E = ["34","62"]
4.最后计算可选兄弟节点组合,集合C里面的组合只要是含有集合E里面的元素都是可选兄弟节点组合
可选兄弟节点组合F = 【集合C里面的组合只要是含有集合E里面的元素】
5.
兄弟节点可选skuID = 【(集合A与集合B的交集的skuID集合)再与 集合F 的并集 】
四,整理
1.筛选有库存的sku组合为可选sku 其余为不可选sku
2.计算兄弟节点可选sku
3.可选sku正常显示,不可选sku置灰,已选sku高亮
附Demo: sku算法demo
五,拓展一下
H. iOS开发面试拿offer攻略之数据结构与算法篇附加安全加密
集合结构 线性结构 树形结构 图形结构
1.1、集合结构 说白了就是一个集合,就是一个圆圈中有很多个元素,元素与元素之间没有任何关系 这个很简单
1.2、线性结构 说白了就是一个条线上站着很多个人。 这条线不一定是直的。也可以是弯的。也可以是值的 相当于一条线被分成了好几段的样子 (发挥你的想象力)。 线性结构是一对一的关系
1.3、树形结构 说白了 做开发的肯定或多或少的知道 xml 解析 树形结构跟他非常类似。也可以想象成一个金字塔。树形结构是一对多的关系
1.4、图形结构 这个就比较复杂了。他呢 无穷。无边 无向(没有方向)图形机构 你可以理解为多对多 类似于我们人的交集关系
数据结构的存储
数据结构的存储一般常用的有两种 顺序存储结构 和 链式存储结构
2.1 顺序存储结构
发挥想象力啊。 举个列子。数组。1-2-3-4-5-6-7-8-9-10。这个就是一个顺序存储结构 ,存储是按顺序的 举例说明啊。 栈,做开发的都熟悉。栈是先进后出 ,后进先出的形式 对不对 ?
他的你可以这样理解, hello world 在栈里面从栈底到栈顶的逻辑依次为 h-e-l-l-o-w-o-r-l-d 这就是顺序存储,再比如队列 ,队列是先进先出的对吧,从头到尾 h-e-l-l-o-w-o-r-l-d 就是这样排对的
2.2 链式存储结构
再次发挥想象力 这个稍微复杂一点 这个图片我一直弄好 ,回头找美工问问,再贴上 例如 还是一个数组 1-2-3-4-5-6-7-8-9-10 链式存储就不一样了 1(地址)-2(地址)-7(地址)-4(地址)-5(地址)-9(地址)-8(地址)-3(地址)-6(地址)-10(地址)。每个数字后面跟着一个地址 而且存储形式不再是顺序 ,也就说顺序乱了,1(地址) 1 后面跟着的这个地址指向的是 2,2 后面的地址指向的是 3,3 后面的地址指向是谁你应该清楚了吧。他执行的时候是 1(地址)-2(地址)-3(地址)-4(地址)-5(地址)-6(地址)-7(地址)-8(地址)-9(地址)-10(地址),但是存储的时候就是完全随机的。明白了?
单向链表双向链表循环链表
还是举例子。理解最重要。不要去死记硬背 哪些什么。定义啊。逻辑啊。理解才是最重要滴
3.1 单向链表
A->B->C->D->E->F->G->H . 这就是单向链表 H 是头 A 是尾 像一个只有一个头的火车一样 只能一个头拉着跑
3.2 双向链表
数组和链表区别:
数组:数组元素在内存上连续存放,可以通过下标查找元素;插入、删除需要移动大量元素,比较适用元素很少变化的情况
链表:链表中的元素在内存中不是顺序存储的,查找慢,插入、删除只需要对元素指针重新赋值,效率高
3.3 循环链表
循环链表是与单向链表一样,是一种链式的存储结构,所不同的是,循环链表的最后一个结点的指针是指向该循环链表的第一个结点或者表头结点,从而构成一个环形的链。发挥想象力 A->B->C->D->E->F->G->H->A . 绕成一个圈。就像蛇吃自己的这就是循环 不需要去死记硬背哪些理论知识。
二叉树/平衡二叉树
4.1 什么是二叉树
树形结构下,两个节点以内 都称之为二叉树 不存在大于 2 的节点 分为左子树 右子树 有顺序 不能颠倒 ,懵逼了吧,你肯定会想这是什么玩意,什么左子树右子树 ,都什么跟什么鬼? 现在我以普通话再讲一遍,你把二叉树看成一个人 ,人的头呢就是树的根 ,左子树就是左手,右子树就是右手,左右手可以都没有(残疾嘛,声明一下,绝非歧视残疾朋友,勿怪,勿怪就是举个例子, I am very sorry ) , 左右手呢可以有一个,就是不能颠倒。这样讲应该明白了吧
二叉树有五种表现形式
1.空的树(没有节点)可以理解为什么都没 像空气一样
2.只有根节点。 (理解一个人只有一个头 其他的什么都没,说的有点恐怖)
3.只有左子树 (一个头 一个左手 感觉越来越写不下去了)
4.只有右子树
5.左右子树都有
二叉树可以转换成森林 树也可以转换成二叉树。这里就不介绍了 你做项目绝对用不到数据结构大致介绍这么多吧。理解为主, 别死记,死记没什么用
1、不用中间变量,用两种方法交换 A 和 B 的值
2、****求最大公约数
3、模拟栈操作
栈是一种数据结构,特点:先进后出 -
练习:使用全局变量模拟栈的操作
#include <stdio.h>
#include <stdbool.h>
#include <assert.h>
//保护全局变量:在全局变量前加 static 后,这个全局变量就只能在本文件中使用 static int data[1024] ;//栈最多能保存 1024 个数据
static int count = 0 ;//目前已经放了多少个数(相当于栈顶位置)
4、排序算法
选择排序、冒泡排序、插入排序三种排序算法可以总结为如下:
都将数组分为已排序部分和未排序部分。
1.选择排序将已排序部分定义在左端,然后选择未排序部分的最小元素和未排序部分的第一个元素交换。
2.冒泡排序将已排序部分定义在右端,在遍历未排序部分的过程执行交换,将最大元素交换到最右端。
3.插入排序将已排序部分定义在左端,将未排序部分元的第一个元素插入到已排序部分合适的位置。
4.1、选择排序
【选择排序】:最值出现在起始端
第 1 趟:在 n 个数中找到最小(大)数与第一个数交换位置
第 2 趟:在剩下 n-1 个数中找到最小(大)数与第二个数交换位置
重复这样的操作...依次与第三个、第四个...数交换位置
第 n-1 趟,最终可实现数据的升序(降序)排列。
4.2、冒泡排序
【冒泡排序】:相邻元素两两比较,比较完一趟,最值出现在末尾
第 1 趟:依次比较相邻的两个数,不断交换(小数放前,大数放后)逐个推进,最值最后出现在第 n 个元素位置
第 2 趟:依次比较相邻的两个数,不断交换(小数放前,大数放后)逐个推进,最值最后出现在第 n-1 个元素位置
…… ……
第 n-1 趟:依次比较相邻的两个数,不断交换(小数放前,大数放后)逐个推进,最值最后出现在第 2 个元素位置
5、折半查找(二分查找)
折半查找:优化查找时间(不用遍历全部数据) 折半查找的原理:
1.数组必须是有序的
2.必须已知 min 和 max (知道范围)
// 已知一个有序数组, 和一个 key , 要求从数组中找到 key 对应的索引位置
字符串反转
给定字符串 " hello,world ",实现将其反转。输出结果: dlrow , olleh
序数组合并
将有序数组 {1,4,6,7,9} 和 {2,3,5,6,8,9,10,11,12} 合并为{1,2,3,4,5,6,6,7,8,9,9,10,11,12}
HASH 算法
哈希表
例:给定值是字母 a ,对应 ASCII 码值是 97,数组索引下标为 97。
这里的 ASCII 码,就算是一种哈希函数,存储和查找都通过该函数,有效地提高查找效率。
在一个字符串中找到第一个只出现一次的字符。如输入" abaccdeff ",输出' b '字符( char )是一个长度为 8 的数据类型,因此总共有 256 种可能。每个字母根据其 ASCII 码值作为数组下标对应数组种的一个数字。数组中存储的是每个字符出现的次数。
查找两个子视图的共同父视图
思路:分别记录两个子视图的所有父视图并保存到数组中,然后倒序寻找,直至找到第一个不一样的父视图。
求无序数组中的中位数
中位数:当数组个数 n 为奇数时,为 (n + 1)/2 ,即是最中间那个数字;当 n 为偶数时,为 (n/2 + (n/2 + 1))/2 , 即是中间两个数字的平均数。
首先要先去了解一些几种排序算法: iOS 排序算法
思路:
1.排序算法+中位数
首先用冒泡排序、快速排序、堆排序、希尔排序等排序算法将所给数组排序,然后取出其中位数即可。
2.利用快排思想
1、简述 SSL 加密的过程用了哪些加密方法,为何这么作?
SSL 加密的过程之前有些过,此处不再赘述。
SSL 加密,在过程中实际使用了 对称加密 和 非对称加密 的结合。
主要的考虑是先使用 非对称加密 进行连接,这样做是为了避免中间人攻击秘钥被劫持,但是 非对称加密的效率比较低。所以一旦建立了安全的连接之后,就可以使用轻量的 对称加密。
2、RSA 非对称加密
对称加密[算法]在加密和解密时使用的是同一个秘钥;而[非对称加密算法]需要两个[密钥]来进行加密和解密,这两个秘钥是[公开密钥]( public key ,简称公钥)和私有密钥( private key ,简称私钥)。
RSA 加密
与对称加密[算法]不同,[非对称加密算法]需要两个[密钥]:[公开密钥]( publickey )和私有密钥( privatekey )。公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。因为加密和解密使用的是两个不同的[密钥],所以这种算法叫作[非对称加密算法]。
RSA**** 加密原理
RSA 是常用的加密模式,其加密原理可用以下的例子进行简要的论述。
随机取两个质数
以上就是本篇所整理的,感谢观看!
I. iOS 开发中都会使用哪些算法
很少需要自己来写算法和数据结构,基本的算法和数据结构都已经集成到库中了。但需要你了解各种算法和数据结构的不同,以便选择适当的库。比如各种排序、查找、字典、数组,是经常用到的。
假如连最基本的算法和数据结构的知识都没有,就算是写一些界面逻辑代码,也经常有性能问题。举个例子,有一个很大的消息列表按照时间排序,而有新的 20 条消息来了,有些人完全无意识地,将 20 条消息一条条依次在一个大数组前面逐个插入,这样就会引起数组的重复移动。这样的代码初看起来逻辑也正确,但就会很慢。
一个稍微严肃一点的 iOS 程序,经常用到三种语言,Swift 编写看得见的界面,C++ 编写看不见的底层,而 Objective-C 用于界面和底层之间的相互调用穿透。
但很多人理解的 iOS 开发,就仅仅只是界面、动画之类的看得见的东西。在界面之下有很多看不见的更深层的东西。这些就需要算法和数据结构知识。比如需要写一个绘图软件,照相磨皮软件,就涉及到图形算法。一个录音声音处理,就需要处理声音的波形。一个电子书软件就涉及到排版。一个类似 Flipboard 的内容聚合软件就涉及网页的抽取。
当然上述的很多算法和数据结构不需要自己来写,但假如完全没有这方面的知识,就算有库用了,但很可能连怎么使用也不会。比如最基本的图形学知识,矩阵都不知道,OpenGL 接口是不会用的。
J. iOS 开发中都会使用哪些算法
md5
base64
sha1
HMAC_SHA1
归并排序, 这个是有2个已经排序好的数组, 要组成一个新数组, 手动实现了一遍归并排序, 但是感受不出效率
各种图片算法, 直接用的GPUImage