SVPullToRefresh是基于UIScrollView的扩展,动态添加了下拉刷新的视图。下面来简单看一下添加视图的实现过程。

首先在类别中声明一个SVPullToRefreshView的property。

@interface UIScrollView (SVPullToRefresh)
...
@property (nonatomic, strong, readonly) SVPullToRefreshView *pullToRefreshView;
...
@end

然而Category并不支持为类添加属性和成员变量,所以需要通过关联对象的方法来动态添加。我们发现在.m文件中引入了#import <objc/runtime.h>头文件,并用@dynamic来声明pullToRefreshView,@dynamic的作用是告诉编译器手动实现setter和getter方法。关于objective-C的其它保留字可以参考唐巧的博文

#import <objc/runtime.h>

static char UIScrollViewPullToRefreshView;

@implementation UIScrollView (SVPullToRefresh)
...

@dynamic pullToRefreshView;

- (void)setPullToRefreshView:(SVPullToRefreshView *)pullToRefreshView {
[self willChangeValueForKey:@"SVPullToRefreshView"];
objc_setAssociatedObject(self, &UIScrollViewPullToRefreshView,
pullToRefreshView,
OBJC_ASSOCIATION_ASSIGN);
[self didChangeValueForKey:@"SVPullToRefreshView"];
}

- (SVPullToRefreshView *)pullToRefreshView {
return objc_getAssociatedObject(self, &UIScrollViewPullToRefreshView);
}

...
@end

核心部分的setter方法中,使用了objc_setAssociatedObject ,其作用就是为一个对象动态添加没有声明的变量。以下是SO上的详细解释。

objc_setAssociatedObject adds a key value store to each Objective-C object. It lets you store additional state for the object, not reflected in its instance variables.

It’s really convenient when you want to store things belonging to an object outside of the main implementation. One of the main use cases is in categories where you cannot add instance variables. Here you use objc_setAssociatedObject to attach your additional variables to the self object.

When using the right association policy your objects will be released when the main object is deallocated.

这样一个自己实现的下拉刷新视图就可以动态添加到UIScrollView上。之后SVPullToRefreshView 以弱引用保有其所在的scrollView变量,通过KVO侦听UIScrollViewcontentOffset的变化,实现了自己的一套状态逻辑,最终达到我们所见的效果。具体实现细节还请参考源码