iOS runtimeはbuttonの繰り返しクリックの問題を解決します

3942 ワード

runtimeとは?
OCはランタイム言語であり、OCは最下位のC言語apiを提供し、コンパイラは最終的にOCコードをランタイムコードに変換します.端末コマンド:clang-rewrite-objc.mは後に便利なものが見える.cpp(c++ファイル).呼び出し方法の本質はruntimeが提供するobjc_を利用することである.msgSend()はメッセージを送信します.
runtimeは何ができますか?
例えば、a動的交換の2つのメソッドの実装(特に交換システムが持参するメソッド)b動的にオブジェクトを追加するメンバー変数とメンバーメソッドcあるクラスのすべてのメンバーメソッド、メンバー変数を取得する
runtimeの応用
1、いくつかのOCコードを実行時コードに変えて、blockの実現原理などの下層を探究する.2、ブロックシステムが持参した方法呼び出し;3、catagory(分類では属性を設定できない)を実現し、runtimeが提供する方法でも属性を増やすことができる.setメソッド:objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy); value:新しい値、object:オブジェクトを格納し、key:keyで値を取得できます.policy:ポリシーassigncopyretian(strong)を格納します.getメソッド:objc_getAssociatedObject(id object, const void *key); 4、NSCordingの自動アーカイブと自動アーカイブを実現する;5、辞書とモデルの自動変換を実現する.
実用的な例
ユーザーの動作が制御できないため、ボタンを連続的にクリックすると、ボタンの繰り返しイベントが呼び出される可能性があります.潤runtimeメカニズムを使用して、ボタンに制御可能な連続クリック間隔を追加し、ボタンをクリックした後、クリックできず、再びクリック可能な状態に戻る時間を制御することができれば、問題は解決します.そこで、UIButtonにリストを追加し、eventTimeIntervalプロパティを追加してこの問題を解決することを選択しました.
コードは次のとおりです.
//  UIButton+Click.h

#import 

@interface UIButton (Click)
//    objc_getAssociatedObject、objc_setAssociatedObject
@property (nonatomic, assign) NSTimeInterval eventTimeInterval;
@end


//UIButton+QYBtnTimer.m

#import "UIButton+Click.h"
#import 
#define defaultInterval 0.0003  //      ,    ,           

@interface UIButton ()
/**
 *  bool YES          NO       
 */
@property (nonatomic, assign) BOOL isIgnoreEvent;
@end

@implementation UIButton (Click)

static const char *UIControl_eventTimeInterval   = "UIControl_eventTimeInterval";
static const char *UIControl_enventIsIgnoreEvent = "UIControl_enventIsIgnoreEvent";

+ (void)load {
    // Method Swizzling
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        //  selector  Method
        SEL selA = @selector(sendAction:to:forEvent:);
        SEL selB = @selector(new_sendAction:to:forEvent:);
        Method methodA = class_getInstanceMethod(self,selA);
        Method methodB = class_getInstanceMethod(self, selB);
        //       
        BOOL isAdd = class_addMethod(self, selA, method_getImplementation(methodB), method_getTypeEncoding(methodB));
        
        if (isAdd) {//    ->  
            class_replaceMethod(self, selB, method_getImplementation(methodA), method_getTypeEncoding(methodA));
        }else{//     ->  
            //            methodB   ,      methodA methodB IMP      。
            method_exchangeImplementations(methodA, methodB);
        }
    });
}

- (void)new_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event {
    self.eventTimeInterval = self.eventTimeInterval == 0 ? defaultInterval : self.eventTimeInterval;
    if (self.isIgnoreEvent){//          
        return;
    }else if (self.eventTimeInterval > 0){//     ,  eventTimeInterval ,        
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.eventTimeInterval * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            [self setIsIgnoreEvent:NO];
        });
    }
    
    self.isIgnoreEvent = YES;//           
    //                ,          sendAction:to:forEvent:   ,
    //      sendAction:to:forEvent:  ,          。
    [self new_sendAction:action to:target forEvent:event];
}

// runtime        
- (BOOL)isIgnoreEvent{
    return [objc_getAssociatedObject(self, UIControl_enventIsIgnoreEvent) boolValue];
}

- (void)setIsIgnoreEvent:(BOOL)isIgnoreEvent {
    objc_setAssociatedObject(self, UIControl_enventIsIgnoreEvent, @(isIgnoreEvent), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (NSTimeInterval)eventTimeInterval {
    return [objc_getAssociatedObject(self, UIControl_eventTimeInterval) doubleValue];
}

- (void)setEventTimeInterval:(NSTimeInterval)eventTimeInterval {
    objc_setAssociatedObject(self, UIControl_eventTimeInterval, @(eventTimeInterval), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

@end