@CoDancer
2018-04-25T14:29:51.000000Z
字数 3286
阅读 1414
SDK
源码阅读
介绍:IGListKit是由Instagram在Instagram创建的数据驱动的UICollectionView框架。 通过这个框架,您可以提供一系列在UICollectionView中显示的对象。 对于每种类型的对象,适配器创建一个名为section controller的内容,该控件具有创建单元格的所有细节。
可能出现的问题
Error: SwiftLint not installed!Download from https://github.com/realm/SwiftLint,or brew install swiftlint.
打开终端,输入brew install swiftlint即可
在手机上运行后,首页是这个样子的。
很明显这是一个只有一个section的视图,包含cell。
在DemosViewController中需要实现ListAdapterDataSource(数据源协议),
并且一个UIcollection对象和一个ListAdapter对象,并且把UIcollection对象作为ListAdapter对象的视图属性,并在该控制器中实现代理方法。
思考:原生的ios是在控制器中通过数据去初始化界面,而这个IGListKit是通过适配器对象,实现对象中的代理方法,并传入视图对象。在实现代理方法中需要传入特定的对象数组;在方法中返回ListSectionController的对象,而具体完成对视图Cell的数据配置,点击事件;和返回当视图是空的视图对象。
let demos: [DemoItem]
func objects(for listAdapter: ListAdapter) -> [ListDiffable] {
return demos
}
而IGListDiffable是协议,demos是DemoItem的对象数组,这时候需要
extension DemoItem: ListDiffable作为ListDiffable的扩展,
这儿的extension在swift的用法可以参考,同时需要实现协议中的方法。
在代理方法中返回ListSectionController的对象,自定义的类继承ListSectionController并重写里面的方法。
override func sizeForItem(at index: Int) -> CGSize
override func cellForItem(at index: Int) -> UICollectionViewCell
override func didUpdate(to object: Any)
主要是视图的构建和点击cell的响应。
这个是有tail loading的界面,在上面的基础之上,另外实现了scrollViewDelegate的代理方法,而在这个控制器中主要包含两种数据对象和对应下的ListSectionController,在我们上拉加载的时候会执行
let distance = scrollView.contentSize.height - (targetContentOffset.pointee.y + scrollView.bounds.height)
if !loading && distance < 200 {
loading = true
adapter.performUpdates(animated: true, completion: nil)
DispatchQueue.global(qos: .default).async {
// fake background loading task
sleep(2)
DispatchQueue.main.async {
self.loading = false
let itemCount = self.items.count
self.items.append(contentsOf: Array(itemCount..<itemCount + 5))
self.adapter.performUpdates(animated: true, completion: nil)
}
}
}
而这个adapter.performUpdates(animated: true, completion: nil)
相关于reloadData。
DispatchQueue.global(qos: .default).async {
sleep(2)
DispatchQueue.main.async {
}
}
在一个异步队列中读取数据, 然后再返回主线程更新 UI.
这里开启一个异步队列去处理后台的可能获取数据的网络耗时操作,这样防止主线程被堵塞,当获得数据后再获取主线程更新UI。
然后这里看到了关于block的一些知识,刚好之前在面试的时候被问到关于block的底层原理的知识,当时还真不知道,在这儿就记录一下block的原理。
int main() {
int count = 10;
void (^ blk)() = ^(){
NSLog(@"In Block:%d", count);
};
blk();
}
当然这是一个很简单的block的方法,但是经过clang后,可以看到
static void __main_block_func_0(
struct __main_block_impl_0 *__cself) {
int count = __cself->count; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_64_vf2p_jz52yz7x4xtcx55yv0r0000gn_T_main_d2f8d2_mi_0,
count);
}
主要是__main_block_impl_0这个block的方法,在来看这个方法的底部结构:
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc; //描述Block大小、版本等信息
int count;
//构造函数函数
__main_block_impl_0(void *fp,
struct __main_block_desc_0 *desc,
int _count,
int flags=0) : count(_count) {
impl.isa = &_NSConcreteStackBlock; //在函数栈上声明,则为_NSConcreteStackBlock
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
首先有一个impl的__block_impl的结构体,一个__main_block_desc_0描述Block大小、版本等信息。
block其实是一个对象,而这个对象的定义是这样的
struct __block_impl {
void *isa;//指明对象的Class
int Flags;
int Reserved;
void *FuncPtr;
};
观察上节代码中__main_block_impl_0结构体(main栈上Block的结构体)的构造函数可以看到,栈上的变量count以参数的形式传入到了这个构造函数中,此处即为变量的自动截获。
因此可以这样理解:__block_impl结构体已经可以代表Block类了,但在栈上又声明了__main_block_impl_0结构体,对__block_impl进行封装后才来表示栈上的Block类,就是为了获取Block中使用到的栈上声明的变量(栈上没在Block中使用的变量不会被捕获),变量被保存在Block的结构体实例中。
所以在blk()执行之前,栈上简单数据类型的count无论发生什么变化,都不会影响到Block以参数形式传入而捕获的值。但这个变量是指向对象的指针时,是可以修改这个对象的属性的,只是不能为变量重新赋值。