AVIMAble协议为AVSDK和IMSDK的协作起到了哪些作用?
1、抽象接口就相当于一个属性声明和方法声明,让满足此协议的类扩展自身功能
2、将内部流程逻辑实现经常变化的方法封装到抽象协议之中
3、同样一个方法的声明,不同的类就有不同的实现流程和逻辑,采用抽象接口。典型声明吃饭的方法,人类和老虎类具体吃放方法的实现就会不一样,而且还不能使用继承的方式,因为虎类和人类没有子类父类的关系,委托代理的意义:声明一个抽象的协议,来扩展类的方法,前提是很多类都需要扩展这一个方法,接口隔离变化
4、尽量建立细密度的抽象接口的意义在于只能调用抽象接口中定义的方法,到底有哪些方法函数是IM和AV都需要的
5、一系列可调用方法的集合,面向接口(协议)编程,更加关注具体接口而不是实现类,平时更加熟悉面向对象编程,网咯请求类实例化一个对象然后发起请求,定义对象时,往往需要定义各种属性,然后对象执行方法,调用的方法直接依赖该对象,起不到解耦的作用,也就是说业务变动,需要重构对象和被调用的方法的时候,所有引用改对象的地方都需要重新设置属性和被调用的方法,所以业务变化尤其要考虑到。最好就是通过接口的定义不再关心对象和属性
6、如何实现在不修改已有类的业务代码下满足新的业务需求,调用者只关心方法的调用,并不在乎到底是那个类在调用该方法,只要这个类满足此协议就可以调用方法
7、对象可以继承对象,Protocol也可以继承Protocol,Protocol更加灵活多变,比如某天当网络请求从get变成Post,难道要把所有的get请求对象都进行更改么,当然最好的方式就是在封装的那个接口里进行更改
8、接口和实现的分离,适用团队协作,实现了系统的松散耦合,便于升级扩展,需要提前定义好接口,接口一变,全部乱套
9、IMUserAble、IMHostAble、AVUserAble、AVMultiUserAble
扩展类别和协议的继承使用起来有什么不同?
1、扩展类别不会影响以后的继承,而且类别会立即实现,而协议只是在需要的时候才实现
2、写协议C的类别1时,类别1同时可以满足协议A
互动直播时观众的屏幕资源分配?
互动观众带着房间Model----取出用户Model----判断用户身份为互动观众并全局保存------设置互动观众特定按钮———开启SDK-----配置房间参数(roomID和房间权限)———进入房间-----半自动接收视频列表请求远程视频数组——-判断视频帧的ID是否与主播ID一样——---一样就全屏,不一样就判断摄像头开关排版右上角-----渲染远程视频——加入聊天室----加入结果回调发送群消息----键盘监控和构建消息——新消息回调——来自主播的特殊消息响应----按钮事件响应
主播已经邀请观众1,观众1打开自己的摄像头,观众2观看观众1画面,现在主播邀请观众2也打开摄像头,结果观众2打开摄像头的位置覆盖了原来属于观众1的画面,另外观众1在请求观众2的画面的时候,竟然将观众2的画面覆盖到了原本属于观众1摄像头的位置,在观众2断开视频互动的时候,问题消失
问题1、主播先邀请了观众1后邀请了观众2,现在断开观众1,现在观众2的位置没变,观众1的位置完全消失,现在重新连接观众1之后,发现观众1竟然覆盖在了观众2上面
方法一:现在远程视频包含一个远程主播和一个远程观众,需要将远程观众的位置进行调整至摄像头关闭的情况,那么直接更新Rect没有用,因为已有的远程视频ID数组并没有因为摄像头的开关而发生改变,所以尽管重新请求远程数据,也会发现不具备重新请求的需要,因为新获取的远程视频ID数组跟现有的远程视频ID数组一模一样,要么不管它,要么直接给自己的摄像头一个固定的位置,远程的观众的摄像头也是固定的位置,要么删除现有的远程观众渲染层以及远程视频ID的数组的处主播以外的所有远程观众,自然就会在有远程观众视频画面到来的时候根据是否打开摄像头重新判断出这个远程视频画面应该有的位置。但是这样做的前提就是必须要让主播的远程视频画面成为数组的第一个元素,否则会删除错误。
现在观众关闭摄像头,如果是观众主动关闭摄像头,会发现渲染层全部消失,然后继续创建新的渲染层,中间会有一个黑屏的过渡,当主播主动断开观众的时候,问题来了,摄像头也关闭了,也有了黑屏的过渡,但是就没有然后了,问题出在哪里呢?
主播邀请观众打开摄像头,观众主动关闭摄像头之后,主播端没有直接重新去请求远程视频ID,造成卡在哪里?
主播邀请观众,观众同意打开摄像头,但是当观众主动关闭摄像头的时候,请求远程视频竟然发生了严重的卡顿,这是为何?
主播:1、摄像头全屏,2、远程视频从右上角开始往下排
情况:
1、没有远程观众2、远程观众A3、远程观众A和B4、远程观众A和B和C5、远程观众A退出,回调事件检测有摄像头关闭,得到的新视频源数组完全是旧视频源数组的子类,直接移除观众A的渲染层,B和C保持不变,现在A重新打开,检测到有两个远程观众,那么就把渲染层放置在此时C观众的位置,单纯的判断此时有几个远程观众,不够管用,因为不知道已经存在的这几个远程观众到底存在于哪一个位置,最好的办法就是每新创建一个渲染层的时候,首先遍历这三个区域,只要遍历到一个空的,马上就填补上去,这样还解决了本地摄像头和远程视频块的关系。现在一个操场有三块空地,我如何找到空地呢,不会造成重复添加的问题。那么这三块空地是什么?我又通过什么去记录这三块空地的实时状态。
方法2:A观众关闭摄像头—主播和B和C观众接收到通知回调—主播获取新的远程视频数组—对照旧的远程视频数组—三种情况:1、新旧的视频数组一样不刷新2、新数组是父数组的子数组,直接移除旧数组而新数组没有的渲染层,不用刷新3、新的数组包含旧的视频数组,需要重新请求远程视频
直接移除A的渲染层---B和C观众的渲染层----新加一个远程观众——判断有两个远程观众——渲染层的区域计算到了C观众渲染层头上——
主播(一大三小,一本地三远程)
解决方案——重新请求远程视频—-移除原来的所有远程观众渲染层,只留下自己的本地视频预览—重新布置远程观众位置---总之就是移除其中一个观众,其它观众位置不变——但是要新添加远程观众时,删除所有远程观众的渲染层,统一重新布局
互动观众(远程主播+两个远程观众AB+本地自己)
自己打开摄像头----判断远程观众个数—添加在远程观众之后——添加新的远程观众---重新请求远程视频数组——判断摄像头是否已经打开决定是否移除本地摄像头渲染层------移除已经存在的所有远程观众渲染层(注意远程主播不移除)——判断摄像头是否打开,首先渲染本地摄像头,然后远程观众全部排在摄像头之后
自己关闭摄像头----移除本地摄像头画面—添加新的远程观众----重新请求远程视频数组----移除已经存在的所有远程观众
A关闭摄像头—直接移除A观众渲染层----新观众打开摄像头——重新请求远程视频——旧的远程数组包括远程主播和B远程观众----
普通观众(远程主播+三个远程观众ABC)
A关闭摄像头---获取新的远程视频数组—旧数组包含新数组——直接移除A观众渲染层----新观众打开摄像头———获取新的远程视频数组—新数组包含旧数组----重新请求远程视频——移除除主播以外的所有远程观众渲染层
同时删除B和C的渲染层—然后根据顺序重新从右上角排列
观众:1、主播远程全屏2、本地摄像头右上角固定区域3、远程观众摄像头区域下面
远程视频首先要知道数组———确定视频的ID是否是自己—————确定视频的来源------------分析是否需要刷新
减不管增刷新
新增远程观众----重新请求远程视频----1、主播移除所有远程观众渲染层,即远程视频数组的所有视频2、观众判断摄像头开关,移除本地视频渲染层,移除远程观众(注意保留远程主播),即远程视频数组除第一个以外的所有视频数组—— 1、主播重新排版所有远程观众2、观众首先判断摄像头开关添加本地渲染层,依次排版所有远程观众渲染层
新增本地摄像头——移除所有的远程观众渲染层----添加本地摄像头渲染层-------重新排版所有远程观众
主播邀请观众A—观众B没有接收到观众A的远程画面——主播邀请观众B—观众B看到观众A视频——观众A也看到观众B ——观众B关闭摄像头——观众B本来显示观众A视频现在消失——
主播邀请观众B—观众A无法看到观众B---主播邀请观众A——观众B看到观众A(但是A本地和B远程间隔过大)——观众A也能看到观众B(同样间距过大)——
主播断开观众A——观众A无法看到观众B—观众A发生卡顿
观众A打开摄像头——观众B监听到有人打开摄像头——获取新的远程视频数组------对照旧数组———需要刷新-----因为观众身份——移除除远程主播以外的所有远程视频——新来的远程观众不包含在远程视频数组——为远程观众添加渲染层——判断本地摄像头是否打开——1、已经打开,本地渲染层是否添加设为NO,从1开始 2、没有本地摄像头,从0开始添加远程视频渲染层
观众A打开摄像头——移除除远程主播外的所有远程观众视频——添加本地摄像头渲染层----远程观众视频重新添加渲染层——判断摄像头已经打开——直接从1开始排版
观众A关闭摄像头----移除本地摄像头渲染层——开关置为NO——发送群消息----接收关闭摄像头回调——重新请求远程视频——
远程主播已经和远程观众A互动———远程观众B进入房间——远程观众A占据了主播位置,主播反而在观众A的位置———观众A断开视频——观众B的主播界面黑屏——观众A重新打开——观众A的远程画面覆盖了当初远程主播占据的远程观众位置———当远程观众A以外退出——观众B的远程观众A呈静止状态
一句话根据远程视频ID判断是否是主播:观众进入房间—---观众获取主播的ID号———判断视频帧的ID是否等于主播ID-----大画面展示
第一次远程观视频进入——判断个数为1———全屏展示------二次远程主播视频进入————判断个数为2———根据是否开摄像头开始布局
观众——远程观众和普通观众——判断第一个远程视频为远程观众-------移除远程视频数组----主播----只有远程观众
打开摄像头,现在需要判断摄像头打开的区域1、一开始就打开2、出现一个远程观众后在打开3、出现两个远程观众后再打开,总之打开摄像头就必须将新的远程观众视频渲染在图像下方,只要把本地摄像头当成一个远程视频就行。如果除了主播还存在其它远程观众的视频,需要根据存在视频源的观众数组来判断有多少个
AVSDK中的协议?
协议我就知道作者想告诉我什么,都是回调和套路,当然我调用SDK的方法和属性的时候需要传入什么呢?通过协议告诉来告诉SDK对于事情的处理结果,属性则是在调用方法前给这个属性赋值,属性不同的值完全导致不同的结果,没有方法的时候,直接设置一个属性就可以达到实现功能的目的。SDK注册结果回调单独一个h文件,登录结果单独一个h文件,这些方法都是直接返回了一个模型对象回来。.h文件就是想要帮我做事情,做事情自然就有结果,但是不是做事情的人直接告诉我,而是有专门的秘书来帮我接收事情的处理结果,自然需要设定一个专门的.h文件声明一个秘书来榜我的方法事情处理结果,而且我很疑惑几乎是一乐类事情就是一个秘书。绝对不重复,另外就算是一个秘书,不同的事情处理结果也会有不同的结果回调。当然除了设置秘书来帮我汇报事情结果,同时还需要设置一个模型白领的岗位,专门帮我整理资料成文件夹,然后床底到方法里面,开始执行,执行结果告诉我的秘书,当然有的属性为了更加便于清除知道所代表的含义,需要写出枚举来为属性赋值。腾讯登录SDK的本质太简单了,就是个方法列和各种各样的秘书里面都是前篇一律声明了一个协议,满足这个协议的对象必须实现里面的几个方法。这些方法就是秘书所要告诉我的事情。
声明protocol的意义?
腾讯连接网络的SDK,同样是protocol当秘书来回调做事情的结果,只是不再将秘书分得这么细,一个.h文件protocol了多个秘书。这就类似于一个类文件里面通过interface声明了多个model,自然就是需要一个太节省类文件了,这不经不用一个秘书一个protocol声明多个接口,又不需要一个interface声明多个model,自然就是既分清了主次又不会太多类。
Xcode的文件详解?
.bundle是对图片的一些XIB的压缩包。.framework则是静态库,负责呈现出作者想要别人知道的属性和方法,甚至协议不外乎系统库,共同库,搭建APP,做界面,写模型,构请求,展示数据交互数据
IM登录帮助单例类?
一个帮助单例类,简单两个属性,就是类方法创建单例对象,同时可以通过APPID,账号类型、版本号返回唯一的实例对象,对象可以设置登录超时时间、是否打印登录日志、获取登录SDK的版本信息、获取所有登录该APP的账号列表,获取最后一次登录信息模型、编辑历史登录账号、判断登录状态、获取本地已保存的签名TLS、刷新本地的TLS委托回调结果、输入密码返回票据、输入验证码返回票据、不输入啥直接获取TLS就是匿名登录、根据手机号获取TLS票据、手机号注册、独立模式的换票、
AVSDK的组成?
Analytics : 灯塔统计SDK 、AVFoundationEx : AVFoundation扩展包、IMCore : IMSDK内部核心包、ImSDK : 即时通信SDK(封装IMCore)、IMSDKBugly : Bugly上报、QALSDK : 联网层SDK、QAVSDK : 音视频核心功能包、TLSSDK : 腾讯的登录服务器
_roomEngine对象能够做的事情?
1、打印日志
2、判断是否正在推流
3、判读是否已经开启了录制功能
4、判断是否是主播
5、判断是否支持美颜
6、检查是否已经开启了麦克风
7、检查是否已经开启了摄像头
8、检查是否已经开启了扬声器
9、获取当前的美颜参数
10、异步开启麦克风
11、异步开启扬声器
12、获取房间模型属性中的主播模型属性
13、获取当前IM登录用户的模型属性
14、观众请求主播画面
15、设置委托回调反馈所有事件的处理结果
16、异步推流旁路直播
_msgHandler消息句柄对象能够做的事情?
1、发送消息
2、改变界面配置属性
_isRoomAlive属性的意义?
主播连摄像头都没打开却调用关闭摄像头的方法,这不找死么,同样观众取消远程视频也好不到哪里去。而且最关键的,有时候房间被销毁了,这些成员都已经没有房子了,还傻乎乎的去请求服务器退出房间,这不闹笑话么,换句话说,国家都灭亡了,你说你要去注销户口,是不是傻。
AVIMMsgHandler类型的msgHandler属性作用?
1、设置roomIMListner委托反馈IM消息事件
2、发送用户进入房间的消息
3、注销消息监听
AVSDK的编程新思想?
通过conformsToProtocol检查传入的user用户模型是否满足某一个协议。
腾讯SDK里面非常好用的公共三方库?
1、BlocksKit,直接通过Block回调Actionsheet的按钮事件
2、分线程延时,HUD显示几秒,就在几秒之后执行调用形参的Block
继承思想的实践体验?
继承要么就是重写父类方法,要么新增父类的方法以及为新增的属性赋值。继承SDK的类,添加新属性,和新方法,初始化方法给父类的属性赋值,将子类的属性写成方法来进行返回,返回的构造是通过父类的属性完成,通过子类工厂方法返回数值,再来一个消息构造类,工厂方法所传参数居然是自身实例,在工厂方法给自己的属性赋值,居然是传一个自身实例进去,可是有什么用呢,唯一的用处就是铜鼓欧工程方法直接将得到的最终值赋值给传进去的实例对象,对象调用一个方法返回一个值,如何把这个值赋值给对象的属性,但是如果这个方法有返回值的前提是传入这个对象的某一个属性,既然传一个属性值返回的值又作为属性值,为什么不直接输入对象作为参数呢,在输入的同时就讲返回值赋值给了属性,至于为什么采用工厂方法,最有说服力的理由是简单,能用工厂方法绝不用对象方法。建造一个A类,但是并没有声明也没有实现A类,反而声明并实现了B、C、D、E、F类,还有声明的B、C、D、E、F类都是NSObject的子类,但是例外在于有的子类满足某一个协议声明,这个满足某一协议的子类与普通只是继承自NSObject的子类有什么区别?这就类似一个整体model里面有很多小的model,这样特别清晰,可是调用的时候确保之间的关联,如何实现?
直播画面渲染和滤镜?
CPUImageVedioCamera、CPUImageFilter、采集、压缩、缓存、处理、显示
粒子动画CAEmitterLayer?
四种图片—四个发射器——添加在同一位置——问题关键在于发射源只管发从不停,每个一段时间就发一次,根本停不下来,要想停下来除了置空cell数组还有什么办法————点击屏幕—响应事件——出生率变为1-—一秒发一个———如何停下来——出生率变为1----不管用——干脆把数组置空———正在显示的所有粒子都没有了—————出生率变为0为什么无效——这时候cell已经在数组之中——能改变的只能是发射器——
出生率为1----就是一秒生一个------但是出现多个———在不同位置-----那么问题就是初始速度——变为0-————不仅一个----没有初速度----造成直线上升没有动画感------而且即使没有初速度也依然是多个cell同时出现——同时出现——什么问题——受cell的生命周期影响——
问题2-----cell显示过程中改变图片-----造成正在动画的cell发生改变------其实只是想要换一个最新的图片而已————一个发射源一个cell———四个图片四种发射源——这多麻烦-----还有不清楚
设置容器View——容器View的layer层添加CAEmitter的对象——设置CAEmitter对象————renderMode渲染模式控制图片如何混合状态、position发射位置、emitterShape发射源形状、emitterSize发射源尺寸、emmitterZposition发射源Z坐标、scale粒子缩放比例、spin粒子旋转角度、velocity粒子速度、———设置单个粒子CAEmitterCell的对象———包括content、birthRate出生数量每秒每点、lifetime、color图片叠加时的颜色、alphaSpeed透明度渐变的速度、velocity初始速度、velocityRange速度范围、emissionRange发射的角度范围、yAcceleration的Y方向加速度分量、spinRange旋转范围、、——-----设置好的CAEmitterCell对象作为数组元素赋值给CAEmitter对象的emitterCells属性
emitterSize和emitterPosition冲突何以解决?———大小和位置不一致———位置是中心点———
emitterShape和emissionRange相矛盾,发射源的形状和发射粒子的范围—————形状相当于所有可以出水的地方——发射的粒子范围相当于屏蔽不能出水的地方
现在问题在于—--在Blok动画的中间阶段进行粒子动画---但是Block动画还没执行到中间阶段就把粒子动画给执行了----推测一开始就遍历了所有的Block动画----通过分线程延时解决这个问题
礼物界面CollectionView的设置?
背景View—pageControl——UICollectionViewFlowLayout—scrollDirection方向—itemSize大小---sectionInset内边距---minimumLineSpacing行列间距—delegate委托——设置多少组——设置组员数——设置item详情——item的点击事件——UICollectionView设置分页效果--—scrollView滚动page更新——水平瀑布流---如何确定行数——默认一行—如何修改为两行—高度所占比决定显示几行——新增的item决定从哪行短的开始添加一行的水平瀑布流——我没有设置contentsize-----自动添加所有item----然后自动计算出了contensize——同样道理——当我添加两行item----会怎么排列——假设先排完第一行----那么有多少个item能够排列——返回item个数是14——全排第一行了—--怎么将数据添加到第二行——分组—每一组都是collection那么大-----如何保证每一个组的布局大小——当我设置组数3组员数8那么总共3页,每页8个,上下各4个----改变itemSize改变大小——不知道排完第一个,第二个到底出现在第一个右边还是第一个的下边——现在已经设置好Cell----下一步刷洗数据—包括图片、波劵数以及点击后的图片选中状态-----首先就是图片的数组,然后波豆的数组——传入一个序号进来决定当前到底显示数组中的哪一个元素——点击事件改变图片—点击另一个,图片复原,现在已经回调回了字典—返回字典—字典数组转成模型数组----波豆数量刷新View——模型数组刷新View设置一个字典用来缓存礼物列表和用户的波劵——将服务器返回的字典存储起来——刷新数据—---自动缓存——第一次打开APP—按钮事件通过委托方法回调—声明协议——创建属性—回调事件---礼物发送请求通过indexPath取出Cell —— 改变Cell上面的图片—— 一旦reloadata----造成index path为空——只是为何?第一组8个元素,第二组6个元素,那么模型数组几个元素—分页处理又是如何处理,模型数组假设装满14个模型—水平滚动—如何排版—首先分组—一组就相当于一页——每页的组元素个数确定后-----平均分配区域——为什么是两行而不是一行----如何确定内边距。
瀑布流?
继承UICollectionViewLayout---prepareLayout方法开始布局—懒加载一个包含许多布局属性对象的可变数组——遍历每一个item的indexPath——调用layoutAttributesForItemAtIndexPath
方法,通过类方法输入item的indexPath,返回item的布局属性对象——赋值frame属性给布局属性对象—--将返回的布局属性对象添加到可变数组————将装满布局属性对象的数组传递给layoutAttributesForElementsInRect方法——这个数组记录了每一个Item的Frame——布局时拿着layout的layoutArray布局位置和dataArray的模型对象刷新数据——
根据indexPath类方法实例化一个布局属性对象——设定X,Y,W,H给布局属性对象的frame属性——W = (collectionView属性的W - CollectionView属性的左右EdgeInsets - (列数-1)*列间距)/ 列数 ———— H = 委托方法返回值(前提输入indexpath和W)——— X = CollectionView的左边距EdgeInsets + 最短列序号 *(W + CollectionView的右边距) —— Y = 高度数组的最小值即最小高度 +CollectionView的上边距(当然是非第一行)----如何判断不是第一行—— 最小列高度等于懒加载的列高度初始值即是第一行
//懒加载存储整型数据的的数组,初始化提添加3个初始列高度,初始列高度通常就是CollectionView的上边距。
找出collectionView的最短列的序号——-遍历数组找到最小元素记录下来—继续对比,知道没有比记录值更小----同时刷新最小记录值的时候也要实时记录当前列数---减少一次比较次数,首先将数组的第一个元素赋值给最小高度—---从1开始比较是否还有更小值————现在已经知道最小列的序号和最小列的高度——设定了新添加的item的frame-----当然这个frame不经确定了X,Y。还确定了W,H——添加完frame之后,高度数组中的最小高度值发生变化,应该原本的最小高度值加上行间距和frame高度-----当然可以通过CGRectGetMaxY方法一键获取列高度——根据列序号更新高度数组的列高度
设定存储比较值得字典—字典初始化一次——>同时初始化一个放列高度的数组—>接着比较最大列高和最小列高存放进字典——>同时存储最小列高的index——>问题关键如何更新数组中最小高度的值
cocoapod导入问题总结?
删除target 'xiubo-tx’ do-----cocopod无法识别-------必须重新添加回去———升级pod吧------早晚需要升级
新建一个工程———初始化git —————提示重复初始化git仓库————如何删除---rmdir .git------Directory not empty---rm -fr .git———成功删除————git init————提示已经成功初始化了一个仓库-------
podfile.lock—————锁定项目依赖库版本的文件—————第一次pod install时生成————Manifest.lock是Podfile.lock的副本———每次编译时检查两者是否一致---------不一致必须更新———保证依赖库的版本一致---------避免程序崩溃和失败—————如果将Podfile.lock加入忽略文件———对方克隆下来没有Podfile.lock将会pod install重新生成----------同时生成副本Manifest.lock———对方上传新生成的Podfile.lock————对比我自己的Manifest.lock----------发现不一样————因为依赖库的版本不确定--------解决方案就是严谨对待Podfile语法,明确依赖库的版本—————多用install—————优先遵循Podfile的依赖库版本,其次遵循对方更新过的Podfile.lock的依赖库版本——————如果使用update-----------完全不理睬对方更新过的Podfile.lock的依赖库版本,一切以Podfile的依赖库版本为准,生成一个新的可能与对方完全不一样的Podfile.lock
未知领域?
各版本特点?
物联网项目经验?
数据库原理?SQL语句?
iOS库的实现原理?
内存溢出?
版本兼容?
Apple Pay 是什么?它的大概工作流程是怎样的?
iOS 的沙盒目录结构是怎样的? App Bundle 里面都有什么?
iOS 的签名机制大概是怎样的?
Objective-C 的 class 是如何实现的?Selector 是如何被转化为 C 语言的函数调用的?
+load 和 +initialize 的区别是什么?
NSOperation 相比于 GCD 有哪些优势?
[UIView animateWithDuration:animations:completion:] 内部大概是如何实现的?
什么是 Runloop?
UIWebView 有哪些性能问题?有没有可替代的方案。
为什么 NotificationCenter 要 removeObserver? 如何实现自动 remove?
当 TableView 的 Cell 改变时,如何让这些改变以动画的形式呈现?
什么是 Method Swizzle,什么情况下会使用?
什么时候会使用 ,有什么注意事项么?
设计一个可以无限滚动并且支持自动滚动的 SlideShow。
设计一个进度条。
设计一套大文件(如上百M的视频)下载方案。
如果让你来实现 dispatch_once,你会怎么做?