1、UITableVIew和UICollectionView的区别?
UICollectionView是tableview的一个升级版,能展示更自定义化的样式。
比如,如果瀑布流的视频列表,左边顺势一列视频,右边一列,左右是不对齐的,没法把整个一行作为一个cell来实现。针对这类情况,提出的CollectionView。
-
区别:
- CollectionView它不是以行来区分的,一行可以展示多个视图,row不能准确表示一个视图的位置,用item来表示,以一个独立的item为视图单元,支持横向和纵向的布局,可以通过collectionViewLayout来控制间距。
- 对于collectionCell只有contentView,而tableView的cell系统默认提供了比如标题、副标题、图片等一些基础的视图,collectionViewCell中都由开发者自己实现加到contentView。
- UICollectionViewCell 继承自UICollectionReusableView, UITableViewCell 继承自 UIView。collectionViewCell必须先注册才能重用。
-
相同点:
- 都是提供列表展示的容器,内置复用回收池;
- 都是基于DataSource和delegate驱动的。
未解决的疑惑:
- UICollectionViewCell为什么和tableCell的继承来源不同?
- 二者的复用机制有什么不同吗?导致Collection必须先注册才能使用?
2、如何自定义TableViewCell的删除按钮的?
在布局cell的时候,添加了一个button控件,然后设置大小,未选中和选中的title,以及颜色。通过layer设置了一下按钮的圆角和边框。
然后通过target Action给 按钮添加点击时执行的方法。
因为想要实现的效果是,类似实际中的新闻应用通常会在叉掉一个新闻的cell时候,弹出一个浮窗,提供一些选项,比如说不感兴趣,举报,拉黑屏蔽等选项。
-
所以通过在自定义的cell中设置一个协议,把delgegate设置为新闻页面的VC。
-
实现了一个删除视图的类,效果是加载一个灰色半透明的浮层,浮层上有按钮,点击后删除对应的cell,模拟实际应用的效果。
-
在VC中实现协议的方法,方法传入的参数是这个tableViewCell和cell上的删除button。
- 传入cell当然是为了知道要删除哪个cell;
- 然后传入button是为了,弹出的删除按钮的效果是要从点击的cell上的删除按钮处弹出,所以要获取这个删除button的坐标,然后坐标转换一下,传入创建的删除视图,实现一个从点击处弹出的一个动画效果。
-
然后实际上删除cell的逻辑是通过点击浮层中的删除按钮来执行的。也是通过target action给这个button设置点击方法。在删除按钮的视图要删除新闻列表的cell,这又是两个类通信的问题;
- 如果还是使用delegate的话,需要传入cell,然后在删除视图中需要用一个属性来保存这个cell,再加上声明协议,比较麻烦,而且我感觉删除视图不拥有这个cell更合理一点。(当然用协议也可)
- 而在刚刚cell的协议方法中,已经得到了cell。那么通过在删除视图中添加一个block属性,直接把要执行的删除逻辑传给block,然后在图层点击的响应方法中执行这个block。这样就写在了一个方法中。
-
删除的逻辑主要是根据cell的index删除数据源中对应的数据,然后向tableView发送deleteRowsAtIndexPaths消息。
-
实际上删除逻辑还涉及两个问题:
- 因为tableView的数据源最好是NSArray,(视频里讲的,应该是为了防止随意修改数据源导致加载出现错误),所以为了能够删除datalist中的数据,先要变为mutalbeArray,删除之后,再copy为NSArray。涉及到 深浅拷贝;
- block前后分别用到了__weak typeof (self) wself = self;和__strong typeof (self) strongSelf = wself;可我还是好奇,如果不用会怎么样?我试了用self,好像没啥,删除视图还是正常dealloc了。
-
补充思考:
如果从cell加载删除视图不用协议,用block的话,那么block从哪里传入?只能在创建的时候传入block,这样首先创建多个cell是不是就创建了多个block,但是不是每个cell都要删除,block不会用到。其次,删除的逻辑和创建的逻辑写在了一起。反而增加了耦合。。。。感觉是不方便的。
3、为什么要将网络数据封装为类?是如何封装的?本地Cache可能涉及什么问题?如何存储到磁盘cache?
-
为什么要缓存
使用缓存可以有效减少网络流量的消耗,缩短页面的打开时间,提升用户体验,因为对于一个资源的请求,如果资源没有发生变化,那么省去了再次获取数据的时间,从缓存加载即可。 -
多种缓存策略 (get 请求)
- NSURLRequestUseProtocolCachePolicy
这个是默认的缓存机制,当系统第一次请求该网络资源时,本地缓存当中并不存在该资源文件。
于是请求了整个资源文件,并且缓存该文件的缓存信息。
当第二请求该文件时,系统会在请求头添加If-Modified-Since字段或者If-None-Match字段,将缓存信息携带上去。
服务器根据缓存信息判断该资源是否发生了变化,如果没有变化,则返回304消息。 - NSURLRequestReloadIgnoringLocalCacheData
该策略在每次请求资源时都会将忽略本地的缓存内容,每次都会将最新的资源请求下来。 - NSURLRequestReturnCacheDataElseLoad
该策略有缓存就用缓存,没有缓存就重新请求。
在第一次请求资源时,由于本地并不存在缓存资源,会将资源请求下来。
在第二次请求时,由于本地已经存在了缓存内容,客户端根本不再发起网络请求。只有在第一请求资源时才会发送请求。
- NSURLRequestUseProtocolCachePolicy
-
为什么以及如何结构化数据。
json解析出来的网络数据大多数情况是一个字典,比如可能对应key为result ,key为error_code等的键值对,然后所需要的列表数据在result中,也是以字典的形式存储,如果加载列表的时候用字典,不太方便,而且语义不明确,每次都要去寻找对应的回包有什么属性。
如果把这个网络请求的结果进行一个对象化的处理,字典的属性暴露在头文件中,model中提供一个根据传入参数给属性赋值的方法,这样就可以形成一个易于操作的model。
先要将json解析,最后获取到一组 字典,对于每个字典根据key给对于一个model对应属性赋值,形成了一个对象。 -
如何存储cache?
默认情况下,每个沙盒含有3个文件夹:Documents, Library 和 tmp。
通过NSSearchPathForDirectoriesInDomains找到library 的 cache文件夹,在cache路径后拼上要创建的文件名,然后createDirectoryAtPath创建一个文件夹,然后根据创建文件的路径拼接文件名,createFielAtPath创建文件。
我发现这里涉及到的内容其实是”数据持久化“,不是一个简单的如何把文件写如cache文件夹的过程。
-
数据持久化的方式
NSUserDefault 简单数据快速读写
Plist (属性列表)文件存储
Archiver (归档)
SQLite 本地数据库
CoreData -
我实际操作过的是归档
-
首先归档是什么
- 归档在iOS中是一种的序列化的方式,
- 使用:对象归档是通过NSKeyedArchiver实现的,它提供了两个函数 archivedDataWithRootObject根据传入的对象,指定路径写入文件,解档unarchivedObjectOfClasses 或者 unarchivedObjectOfClass。实际上就是提供object和二进制数据之间的转换;
- 但是要求object遵循了NSCoding协议,自己处理好键值对应关系。现在应该遵循NSSecureCoding,更安全的coding方式。
- 遵循协议要实现的两个方法分别是,解档的initWithCoder,根据key把对应的二进制流解码赋值给对应的属性;以及归档的encode,根据key把对应属性编码。
-
我理解的关于这个用key进行归档的有优势在于,可以向前向后兼容,不依赖于顺序。
举个例子,假设一个新闻列表中的item,可能有title date 和文章url这么三个属性,如果直接序列化,比如通过UTF8编码,然后按顺序拼接在一起形成一个01的数据流,可能前面三个000代表title,中间三个001代表date,最后三个111代表url。此时如果发生了更新,在url之前插入了属性:作者,那么反序列化到对象就出现了错误,最后三个111对应到了作者。
如果直接用属性名做key,可能导致这个属性被删除了然后把这个名字给了其他的属性,那么反序列化也会有问题。所以这个变成了自定义key,把解决重复的任务交给开发者自己保证。这样键值对应就不受顺序的限制。 -
什么是NSUserDefault
保存一些用户习惯,应用的偏好设置,用于轻量级数据的持久话。这些信息会以plist文件的形式保存在preference中。
https://blog.csdn.net/jiankeufo/article/details/79347330
4、KVO和通知的区别?进而涉及到对象之间的通信机制,还有block和delegate?
答:
-
通知
- 定义
- 是使用观察者模式来实现的用于跨层传递信息的机制。
- 传递方式是一对多的。
- 在通知机制中需要关注某个通知的所有对象都可以成为注册成为观察者。
通知中心会把该通知以广播的形式发送给相应的观察者.
- 如何实现通知机制
在通知中心,维护一个map,key是通知名称,value是添加的observer。对于同一个key可能有多个观察者,因此应该是一个数组,数组中每一个成员应该包含观察者和对应的回调方法的信息。
- 定义
-
KVO
- 定义
是key value oberserving的缩写,是一种能够获取其他对象的特定属性变化的通知机制。 - 实现机制:
当某个类A的属性对象第一次被观察时,系统就会在运行期动态地创建该类的一个子类,NSKVONotifying_A,然后改写类A 的isa指针指向子类,在这个子类中重写基类中被观察属性的setter 方法。
在被重写的setter方法内实现真正的通知机制,先调用willChangeValueForKey,然后调用父类的set,然后did,在didChangeValueForKey中会给观察者发送observeValueForKeyPath消息,来通知观察者value发生了变化。
- 定义
- (void)setValue:(id)value
{
[self willChangeValueForKey:@"key"];
[super setValue:value];
[self didChangeValueForKey:@"key"];
}
还可能引申出来的是代理的相关问题,以及代理和KVO、通知的比较。
然后还有,什么是KVC,KVC和KVO的比较
-
什么是代理
代理是一种设计模式,以@protocol形式体现,委托方要求代理实现的接口都在协议中,代理方负责具体的实现。委托方通过代理执行方法。
其实代理的使用,我的个人感受,主要是方便了代码上的解耦,职责分工明确,比如tableview的datasource负责数据,viewdelegate只负责展示,分工明确对于以后维护阅读都有好处。
其次,代理是逆向传值的一种解决办法,比如视图A显示的个人资料,点击编辑进入资料编辑视图B,这个时候视图B中改变的值要返回给A展示的话,是一个逆向的传值。如果正向传值,A给B的属性赋值即可,逆向的话代理就是一种解决办法,B通过设置A为代理,把改变的值作为参数传到由代理A实现的方法,这样A就得到了修改后的值,可以更新。 -
代理、通知、KVO的比较
- 代理是代理模式,通知和KVO是观察者模式;
- 代理的消息传递是一对一的,某一个消息委托方只会传给一个代理,而对于通知和KVO 的话,一个通知是会传给多个观察者的,为一对多的模式。
- delegate 可以有返回值,能协同工作,处理业务逻辑;
- KVO 只能作用在value以及变化上; 只返回结果单方向无协同;
- Notification 相比KVO能携带更多的信息(一个事件); 常用于系统级消息; 单方向无协同
- 比较KVO和通知
两者都是观察者模式,不同的是,KVO是被观察者直接发送消息给观察者,而通知则是,消息由被观察者发送到通知中心对象,再由中心对象发给观察者,两者之间并不进行直接的交互。
-
什么是KVC
是key value coding,键值编码技术。
可以通过valueForKey获取和Key同名或者相似名称的一个实例变量的值。
通过setValueForKey设置Key同名或者相似名称的一个实例变量的值。 -
KVC实现逻辑
-
当调用setValue:属性值 forKey:@”name“的代码时,,底层的执行机制如下:
- 程序优先调用set<Key>:属性值方法,代码通过setter方法完成设置。注意,这里的<key>是指成员变量名,首字母大小写要符合KVC的命名规则,下同
- 如果没有找到setKey:方法,KVC机制会检查+ (BOOL)accessInstanceVariablesDirectly方法有没有返回YES,默认该方法会返回YES,如果你重写了该方法让其返回NO的话,那么在这一步KVC会执行setValue:forUndefinedKey:方法,不过一般开发者不会这么做。
- 返回YES的话,KVC机制会则按_key、_isKey、key、isKey查找成员变量 如果上面列出的方法或者成员变量都不存在,系统将会执行该对象的setValue:forUndefinedKey:方法,默认是抛出异常。
- 如果开发者想让这个类禁用KVC,那么重写+ (BOOL)accessInstanceVariablesDirectly方法让其返回NO即可,这样的话如果KVC没有找到set<Key>:属性名时,会直接用setValue:forUndefinedKey:方法。
-
当调用valueForKey:@”name“的代码时,KVC对key的搜索方式不同于setValue:属性值 forKey:@”name“,其搜索方式如下:
-
按先后顺序搜索getKey、key、isKey、_getKey、_key五个方法,若某一个方法被实现,取到的即是方法返回的值,后面的方法不再运行。如果是BOOL或者Int等值类型, 会将其包装成一个NSNumber对象。
-
若这五个方法都没有找到,则会调用+
(BOOL)accessInstanceVariablesDirectly方法判断是否允许取成员变量的值。 若返回NO,直接调用- (nullable id)valueForUndefinedKey:(NSString *)key方法,默认是奔溃。若返回YES,会按先后顺序取_key、_isKey、 key、isKey的值,取到了就赋值。 -
返回YES时,_key、_isKey、 key、isKey的值都没取到,调用- (nullable id)valueForUndefinedKey:(NSString *)key方法。
-
在取值的第一步结束第二步开始之前,还会判断获取的值是否是数组或者集合。
如果是数组(NSArray *)的话,当实现countOf方法和objectInAtIndex或AtIndexes中任意一个方法时则会返回一个数组.
集合需要实现以下三个方法: -countOf<Key> -enumeratorOf<Key> -memberOf:<key>
-
-
KVC和KVO的关系网上并不明确,这篇帖子,作者讨论了自己的想法并进行了实践,是否正确我暂时还不理解。
https://www.jianshu.com/p/a0cf1b450371
网友评论