1行代碼快速集成按鈕延時處理(hook實戰)

KriLorenzin 8年前發布 | 13K 次閱讀 IOS iOS開發 移動開發

1. 按鈕延時處理事件有什么應用場景?

  • 如果你做的是一個帶有輕微社交功能的APP,這類APP一般都會有類似“收藏”、“點贊”、“喜愛”的功能。

  • 這些功能其實載體是一個UIButton,如果你在每次用戶點贊的時候都發請求給服務器,假如有些用戶“手便宜”,在那里重復的點擊,就會造成一個請求還沒回來,有連續發送出去好幾個請求。

  • 出現這種情況,第一,可能造成服務器不必要的壓力,這簡直是必然的;第二,由于你不確定請求回調什么時候回來,假如用戶把這個控制器銷毀了,你的應用就可能奔潰。

  • 這個場景就可以采用按鈕延時處理事件來輕松應對。

2.實例分析?

像下面的demo里寫的這樣:

1行代碼快速集成按鈕延時處理(hook實戰)


收藏這類功能的事件鏈是:用戶點擊-->處理點擊 -->發送請求

  • 正常情況,用戶點擊按鈕,響應用戶點擊, 發送請求。
  • 當使用延時處理以后(我這里設定延時時長為1.0Second),當用戶點擊按鈕以后,響應用戶點擊,但是不是立即發送請求,而是先檢查一下兩次點擊之間時間差有沒有1秒,如果有,再發送請求,如果沒有,不發送請求。

3、動態添加方法和屬性(hook)?

3.1 runtime是什么?

  • runtime簡稱運行時。OC就是運行時機制,也就是在運行時候的一些機制,其中最主要的是消息機制。
  • Objective-C 的 Runtime 是一個運行時庫(Runtime Library),它是一個主要使用 C 和匯編寫的庫,為 C 添加了面相對象的能力并創造了 Objective-C。這就是說它在類信息(Class information) 中被加載,完成所有的方法分發,方法轉發,等等。Objective-C runtime 創建了所有需要的結構體,讓 Objective-C 的面相對象編程變為可能。

3.2 動態添加方法和屬性是什么?

  • 比如說,我要給一個人動態添加一個“吹牛逼”的屬性,方法是這樣的。先給人添加一個分類(Category),然后在分類里添加一個屬性。
  • 注意,分類是專門用來添加方法的,在分類里使用關鍵字@property添加屬性,系統是不會幫我們生成setter-getter方法的。
  • 所以我們要自己實現setter-getter方法。
    在setter方法里使用runtime的以下方法動態添加屬性。
    void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
    在getter方法里使用runtime的以下方法動態獲取屬性值。
    id objc_getAssociatedObject(id object, const void *key)

     

3.3 方法交換是什么?

  • 記得我們的每一個OC對象都有一個isa指針嗎?這個isa就是指向創建實例對象的類。
  • 對象方法保存到類里面,每個類里面都有一個方法列表。
  • 當調用對象方法的時候,系統都會來到這個表里查找對應的方法和實現。

1行代碼快速集成按鈕延時處理(hook實戰)

  • 所謂的方法交換,也就是hook,就是把兩個方法的實現給交換了。就像下面這張圖一,你調用eat方法的時候,就會去找run方法的實現。
    1行代碼快速集成按鈕延時處理(hook實戰)

4.思路分析?

我們知道UIButton繼承自UIControl,UIButton的所有處理事件的能力都是它的父類UIControl傳給它的。UIControl有這樣一個方法:

// send the action. the first method is called for the event and is a point at which you can observe or override behavior. it is called repeately by the second.

  • (void)sendAction:(SEL)action to:(nullable id)target forEvent:(nullable UIEvent )event;</code></pre>

    官方的解釋翻譯過來是這樣的:這個方法用以傳遞事件消息,是監聽到事件后最先調用的方法,并且它是隨著事件的重復產生而頻繁調用的。

    所以我們要實現攔截事件傳遞,重寫這個方法是最優解。

    5.代碼實現?

    • 首先為UIControl添加創建分類,并且在.h文件里添加屬性。
    #import <UIKit/UIKit.h>
    @interface UIControl (JPBtnClickDelay)
    /** 延遲時間 /
    @property(nonatomic)NSTimeInterval jp_acceptEventInterval;
    /* 是否接受延遲 /
    @property(nonatomic)BOOL jp_ignoreEvent;
    @end</code></pre> 
    
    • 接下來來到.m文件
    #import "UIControl+JPBtnClickDelay.h"
    #import <objc/runtime.h>

@implementation UIControl (JPBtnClickDelay) -(void)jp_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event{

if (self.jp_ignoreEvent) return;

if (self.jp_acceptEventInterval > 0) { self.jp_ignoreEvent = YES; [self performSelector:@selector(setJp_ignoreEvent:) withObject:@(NO) afterDelay:self.jp_acceptEventInterval]; } [self jp_sendAction:action to:target forEvent:event]; }

-(void)setJp_ignoreEvent:(BOOL)jp_ignoreEvent{ objc_setAssociatedObject(self, @"jp_ignoreEvent", @(jp_ignoreEvent), OBJC_ASSOCIATION_ASSIGN); }

-(BOOL)jp_ignoreEvent{ return [objc_getAssociatedObject(self, @"jp_ignoreEvent") integerValue]; }

-(void)setJp_acceptEventInterval:(NSTimeInterval)jp_acceptEventInterval{ objc_setAssociatedObject(self, @"jp_acceptEventInterval", @(jp_acceptEventInterval), OBJC_ASSOCIATION_ASSIGN); }

-(NSTimeInterval)jp_acceptEventInterval{ return [objc_getAssociatedObject(self, @"jp_acceptEventInterval") doubleValue]; }

+(void)load{ Method sys_Method = class_getInstanceMethod(self, @selector(sendAction:to:forEvent:));

Method add_Method = class_getInstanceMethod(self, @selector(jp_sendAction:to:forEvent:));

method_exchangeImplementations(sys_Method, add_Method);

} @end</code></pre>

6. 分類的使用?

這里有兩個UIButton的實例對象:

[self.normalBtn addTarget:self action:@selector(normalBtnClick) forControlEvents:UIControlEventTouchUpInside];

[self.delayBtn addTarget:self action:@selector(delayBtnClick) forControlEvents:UIControlEventTouchUpInside]; self.delayBtn.jp_acceptEventInterval = 1.0f;</code></pre>

  • normalBtn不需要有延時,就什么也不用管,就和使用系統原生的一樣。
  • delayBtn需要延時,給它的jp_acceptEventInterval設定一個延時值,它自動就會生效。

7. Demo下載?

請點擊這里去往Github

8. One more thing ?

如果您對“hook技術”感興趣,或許可以參見我的另外一篇文章“0行代碼集成非死book和推ter的Modal動畫”。我在這篇文章使用hook成功的做到了0行代碼集成一個功能。


來自:http://www.jianshu.com/p/e791b7927f32

 本文由用戶 KriLorenzin 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!