其他一丢丢#iOS#HeminWon

iOS 中 Category 的一些事

2017-02-16  本文已影响179人  天空中的球

前几天遇到了一个由于分类引起的 Bug, 折腾了好久才找到根源点,过程甚是纠结。于是对分类这块有一些想法啦,想想平常是否对这块遗漏了什么?特此总结下。

一、分类 和 继承 的区别

昨天在写一个类的扩展的时候,我第一反应是用了继承,然后事后被小伙伴说此处已经有了写好的分类了,然后我就在想为什么我第一反应会用继承呢?为什么不是用分类的呢?也就自然想到了我们面试经典题: 分类 和 继承 的区别。

网上的答案:
  • 区别:
    1、分类是对方法的扩展,不能添加成员变量。继承可以在原来父类的成员变量的基础上,添加新的成员变量
    2、分类只能添加新的方法,不能修改和删除原来的方法。继承可以增加、修改和删除方法。
    3、分类不提倡对原有的方法进行重载。继承可以通过使用super对原来方法进行重载。
    4、分类可以被继承,如果一个父类中定义了分类,那么其子类中也会继承此分类。

而我个人感觉用时的简单感受就是: ** 继承修改老方法,分类添加新方法**。

#import <UIKit/UIKit.h>

@interface PQTextField : UITextField

@property (nonatomic, assign) CGFloat leftPadding; // 有左边View 离边框的距离
@property (nonatomic, assign) CGFloat rightPadding; // 有右边View 离边框的距离
@property (nonatomic, assign) CGFloat leftPlaceholderNormalPadding; //代表(没有View)离左边的距离

@end
#import "PQTextField.h"

@implementation PQTextField

- (instancetype)init {
    if (self = [super init]) {
        // 设置默认的一些情况
        _leftPadding = 8;
        _rightPadding = 8;
        _leftPlaceholderNormalPadding = 10;
    }
    return self;
}

// 左边View 距离边界的距离
- (CGRect)leftViewRectForBounds:(CGRect)bounds {
    CGRect leftRect = [super leftViewRectForBounds:bounds];
    leftRect.origin.x += _leftPadding;
    return leftRect;
}

// 右边View 距离边界的距离
- (CGRect)rightViewRectForBounds:(CGRect)bounds {
    CGRect rightRect = [super rightViewRectForBounds:bounds];
    rightRect.origin.x -= _rightPadding;
    return rightRect;
}

//UITextField 文字与输入框的距离
- (CGRect)textRectForBounds:(CGRect)bounds{
    if (self.leftView) {
        return CGRectInset(bounds, _leftPadding * 2 + self.leftView.frame.size.width, 0);
    }
    return CGRectInset(bounds, _leftPlaceholderNormalPadding, 0);
    
}

//控制编辑文本的位置
- (CGRect)editingRectForBounds:(CGRect)bounds{
    if (self.leftView) {
        return CGRectInset(bounds, _leftPadding * 2 + self.leftView.frame.size.width, 0);
    }
    return CGRectInset(bounds, _leftPlaceholderNormalPadding, 0);
}

@end

上面这个就是我用到的一个继承的例子,对 UITextField 进行扩展,重写该类的方法。

二、分类常用的写法

#import <UIKit/UIKit.h>

typedef void (^PQClickButtonHandler)(UIButton *button);

@interface UIButton (PQHandler)

- (void)pq_addClickHandler:(PQClickButtonHandler)handler;

@end

#import "UIButton+PQHandler.h"
#import <objc/runtime.h>

NSString const *pq_button_handler = @"pq_button_handler";

@implementation UIButton (PQHandler)

- (void)pq_addClickHandler:(PQClickButtonHandler)handler {
    objc_setAssociatedObject(self, &pq_button_handler, handler, OBJC_ASSOCIATION_COPY_NONATOMIC);
    [self addTarget:self action:@selector(clickAction:) forControlEvents:UIControlEventTouchUpInside];
}

- (void)clickAction:(UIButton *)button {
    PQClickButtonHandler handler = objc_getAssociatedObject(self, &pq_button_handler);
    if (handler) {
        handler(button);
    }
}

@end
#import <UIKit/UIKit.h>

@interface UIButton (PQTouchExtraInsets)

@property (nonatomic, assign) UIEdgeInsets pq_touchExtraInsets;

@end
#import "UIButton+PQTouchExtraInsets.h"
#import <objc/runtime.h>

@implementation UIButton (PQTouchExtraInsets)

- (void)setPq_touchExtraInsets:(UIEdgeInsets)pq_touchExtraInsets {
    objc_setAssociatedObject(self, @selector(pq_touchExtraInsets), [NSValue valueWithUIEdgeInsets:pq_touchExtraInsets], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (UIEdgeInsets)pq_touchExtraInsets {
    return [objc_getAssociatedObject(self, _cmd) UIEdgeInsetsValue];
}

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
    UIEdgeInsets touchAreaInsets = self.pq_touchExtraInsets;
    CGRect bounds = self.bounds;
    bounds = CGRectMake(bounds.origin.x - touchAreaInsets.left,
                        bounds.origin.y - touchAreaInsets.top,
                        bounds.size.width + touchAreaInsets.left + touchAreaInsets.right,
                        bounds.size.height + touchAreaInsets.top + touchAreaInsets.bottom);
    return CGRectContainsPoint(bounds, point);
}

@end

#import <UIKit/UIKit.h>

@interface UIButton (PQBackgroundColor)

- (void)pq_setBackgroundColor:(UIColor *)color state:(UIControlState)state;

@end
#import "UIButton+PQBackgroundColor.h"

@implementation UIButton (PQBackgroundColor)

- (void)pq_setBackgroundColor:(UIColor *)color state:(UIControlState)state {
    [self setBackgroundImage:[self pq_imageWithColor:color] forState:state];
}

- (UIImage *)pq_imageWithColor:(UIColor *)color {
    CGRect rect = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f);
    UIGraphicsBeginImageContext(rect.size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetFillColorWithColor(context, [color CGColor]);
    CGContextFillRect(context, rect);

    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    
    return image;
}

@end
#import "UIView+PQViewController.h"

@implementation UIView (PQViewController)

- (UIViewController *)viewController {
    //通过响应者链,取得此视图所在的视图控制器
    UIResponder *next = self.nextResponder;
    do {//判断响应者对象是否是视图控制器类型
        if ([next isKindOfClass:[UIViewController class]]) {
            return (UIViewController *)next;
        }
        next = next.nextResponder;
    }while(next != nil);
    return nil;
}

@end
#import "UIViewController+PQDealloc.h"
#import <objc/runtime.h>

@implementation UIViewController (PQDealloc)

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = [self class];
        Method setTextMethod = class_getInstanceMethod(class, NSSelectorFromString(@"dealloc"));
        Method replaceSetTextMethod = class_getInstanceMethod(class, NSSelectorFromString(@"pq_dealloc"));
        method_exchangeImplementations(setTextMethod, replaceSetTextMethod);
    });
}

- (void)pq_dealloc {
    NSLog(@"%@--->>>>已经释放了",[self class]);
    [self pq_dealloc];
}

@end

上述需要注意的确实 Runtime。

在此特别推荐一下 JKCategories,里面中的分类真的超多,上述有几个例子就是从此处学习的。另外,Objective-C 基础类的一些实用 Category 这里面推荐的也可以看看。

三、使用分类时遇到的BUG

总的来说,一些莫名其妙的问题,例如根本不知道 崩 在什么地方情况下,就得考虑下是否是分类中有问题,或者说哪里运用到了一些 Runtime 的方案。

PS: Category 进一步理解
备注参考:

http://www.cnblogs.com/williamliuwen/p/5370155.html
https://github.com/shaojiankui/JKCategories
http://www.jianshu.com/p/1c7d34dbf671

上一篇 下一篇

猜你喜欢

热点阅读