MJRefresh 源碼閱讀

1、Runtime
1.1 關(guān)聯(lián)對(duì)象
該框架為UIScrollView添加了兩個(gè)“成員變量”,headerfooter,這是在分類中實(shí)現(xiàn)的。因?yàn)槭墙oUIScrollView及其子類UITableView和UICollectionView添加,不能通過繼承實(shí)現(xiàn)向其添加headerfooter。所以作者采用了分類的方法。
但是我們通常會(huì)把成員變量放在類聲明的頭文件里,或者放在類實(shí)現(xiàn)的@implementation 前面。我們不能在分類中添加成員變量,編譯器會(huì)報(bào)錯(cuò)。Objective-C針對(duì)這一問題,提供了一種解決方案:關(guān)聯(lián)對(duì)象(Associated Object)。其定義是這樣的:

/** 
 * Sets an associated value for a given object using a given key and association policy.
 * 
 * @param object The source object for the association.
 * @param key The key for the association.
 * @param value The value to associate with the key key for object. Pass nil to clear an existing association.
 * @param policy The policy for the association. For possible values, see “Associative Object Behaviors.”
 * 
 * @see objc_setAssociatedObject
 * @see objc_removeAssociatedObjects
 */
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)

我們可以給對(duì)象關(guān)聯(lián)很多其他對(duì)象,通過const void *key來區(qū)分,是一個(gè)唯一的指針,并且還有相應(yīng)的內(nèi)存管理策略,并且有對(duì)應(yīng)等效的@property屬性(當(dāng)該關(guān)聯(lián)對(duì)象成為屬性時(shí))。
作者在此時(shí)用到的就是這個(gè)辦法,

static const char MJRefreshHeaderKey = '\0';
- (void)setHeader:(MJRefreshHeader *)header
{
    if (header != self.header) {
        // 刪除舊的,添加新的
        [self.header removeFromSuperview];
        [self addSubview:header];
        
        // 存儲(chǔ)新的
        [self willChangeValueForKey:@"header"]; // KVO
       //  添加關(guān)聯(lián)對(duì)象
        objc_setAssociatedObject(self, &MJRefreshHeaderKey,
                                 header, OBJC_ASSOCIATION_ASSIGN);
        
        [self didChangeValueForKey:@"header"]; // KVO
    }
}

- (MJRefreshHeader *)header
{
     //  取出關(guān)聯(lián)對(duì)象
    return objc_getAssociatedObject(self, &MJRefreshHeaderKey);
}

1.2 self 和 super
該框架定義子控件的Frame,調(diào)用的是- (void)layoutSubviews;,但是閱讀框架的時(shí)候發(fā)現(xiàn)只有MJRefreshComponent 實(shí)現(xiàn)了這個(gè)方法:

- (void)layoutSubviews
{
    [super layoutSubviews];
    
    [self placeSubviews];
}

其他子類調(diào)用的就是這個(gè)方法,這就要區(qū)分一下self 和 super 的區(qū)別了。
這個(gè)文檔講的不錯(cuò) http://www.cocoachina.com/ios/20141224/10740.html 。
1.3 方法交換

//當(dāng)類加載到內(nèi)存的時(shí)候,調(diào)用
+ (void)load
{
    [self exchangeInstanceMethod1:@selector(reloadData) method2:@selector(mj_reloadData)];
}
+ (void)exchangeInstanceMethod1:(SEL)method1 method2:(SEL)method2
{
    method_exchangeImplementations(class_getInstanceMethod(self, method1), class_getInstanceMethod(self, method2));
}

+ (void)exchangeClassMethod1:(SEL)method1 method2:(SEL)method2
{
    method_exchangeImplementations(class_getClassMethod(self, method1), class_getClassMethod(self, method2));
}

2 header
該框架最基礎(chǔ)的類是MJRefreshComponent,包含了header 和 footer 共有的屬性和方法,包括刷新狀態(tài)控制方法,初始化等共有方法。
創(chuàng)建header的方法作者提供了兩個(gè):

/** 創(chuàng)建header */
+ (instancetype)headerWithRefreshingBlock:(MJRefreshComponentRefreshingBlock)refreshingBlock;
/** 創(chuàng)建header */
+ (instancetype)headerWithRefreshingTarget:(id)target refreshingAction:(SEL)action;

初始化最終是調(diào)用父類的方法:

- (instancetype)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame]) {
        // 準(zhǔn)備工作
        [self prepare]; //哪個(gè)對(duì)象調(diào)用,就用該對(duì)象isa指向的類中的方法。
        
        // 默認(rèn)是普通狀態(tài)
        self.state = MJRefreshStateIdle;
    }
    return self;
}

之后,當(dāng)header添加到父視圖上時(shí),調(diào)用了- (void)willMoveToSuperview:(UIView *)newSuperview;方法,設(shè)置一些屬性,添加監(jiān)聽事件(KVO),監(jiān)聽父視圖的滾動(dòng)。
其中最關(guān)鍵的方法是- (void)scrollViewContentOffsetDidChange:(NSDictionary *)change;,監(jiān)聽contentOffset的變化,來判斷header相應(yīng)的state 。
根據(jù)MJRefreshHeader這個(gè)類,繼承它得到了MJRefreshStateHeader(里面包含了提示文字和最后刷新時(shí)間),MJRefreshNormalHeader(里面有活動(dòng)指示器),MJRefreshGifHeader (圖片),當(dāng)然我們也可以繼承MJRefreshHeader來創(chuàng)造我們自己需要的下拉刷新視圖,擴(kuò)展性強(qiáng)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容