日常开发过程中我们经常碰到要给分类添加自定义属性和变量的,下面通过关联对象给分类添加属性,也是runtime的实际应用之一,非常实用。(关于runtime的使用还有很多,这里只简单记录一下利用runtime给分类添加属性)。
关联Runtime提供了下面几个接口:
关联对象
void objc_setAssociatedObject(id object, const void * key, id value, objc_AssociationPolicy policy)
获取关联对象
id objc_getAssociatedObject(id object, const void * key)
移除关联对象
void objc_removeAssociatedObjects(id object)
参数解释:
id object;被关联的对象
const void * key:关联的key,要求唯一
id value:关联的对象
objc_AssociationPolicy policy:内存管理策略
1.给UILabel添加一个 竖直方向的verticalText,如下图所示:

竖直方向的label.png
新建一个继承UILabel的Category,并添加一个成员变量
.h文件
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface UILabel (HXVertical)
@property (nonatomic) NSString * verticalText;
@end
NS_ASSUME_NONNULL_END
m.文件
#import "UILabel+HXVertical.h"
#import "objc/Runtime.h"
@implementation UILabel (HXVertical)
- (NSString *)verticalText{
    // 获取关联对象
    return objc_getAssociatedObject(self, @selector(verticalText));
}
- (void)setVerticalText:(NSString *)verticalText{
    //关联对象
    objc_setAssociatedObject(self, &verticalText, verticalText, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    NSMutableString *str = [[NSMutableString alloc] initWithString:verticalText];
    NSInteger count = str.length;
    for (int i = 1; i < count; i ++) {
        [str insertString:@"\n" atIndex:i*2-1];
    }
    self.text = str;
    self.numberOfLines = 0;
}
@end
调用时就直接使用点语法:
_textLabel.verticalText = @"厉害了,我的国";
2.给UIButton添加一个扩大点击区域的实例方法
新建一个继承UIButton的Category,并添加一个实例方法
.h文件
#import <UIKit/UIKit.h>
@interface UIButton (HXEnlargeTouchArea)
- (void)setTouchExpandEdgeWithTop:(CGFloat)top right:(CGFloat)right bottom:(CGFloat)bottom left:(CGFloat)left;
@end
.m文件
#import "UIButton+HXEnlargeTouchArea.h"
#import <objc/runtime.h>
@implementation UIButton (HXEnlargeTouchArea)
static char topRegion;
static char rightRegion;
static char bottomRegion;
static char leftRegion;
- (void)setTouchExpandEdgeWithTop:(CGFloat)top right:(CGFloat)right bottom:(CGFloat)bottom left:(CGFloat)left {
    //关联对象
    objc_setAssociatedObject(self, &topRegion, [NSNumber numberWithFloat:top], OBJC_ASSOCIATION_COPY_NONATOMIC);
    objc_setAssociatedObject(self, &rightRegion, [NSNumber numberWithFloat:right], OBJC_ASSOCIATION_COPY_NONATOMIC);
    objc_setAssociatedObject(self, &bottomRegion, [NSNumber numberWithFloat:bottom], OBJC_ASSOCIATION_COPY_NONATOMIC);
    objc_setAssociatedObject(self, &leftRegion, [NSNumber numberWithFloat:left], OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (CGRect)expandRect
{
    
    //获取关联对象
    NSNumber* topEdge = objc_getAssociatedObject(self, &topRegion);
    NSNumber* rightEdge = objc_getAssociatedObject(self, &rightRegion);
    NSNumber* bottomEdge = objc_getAssociatedObject(self, &bottomRegion);
    NSNumber* leftEdge = objc_getAssociatedObject(self, &leftRegion);
    if (topEdge && rightEdge && bottomEdge && leftEdge)
    {
        return CGRectMake(self.bounds.origin.x - leftEdge.floatValue,self.bounds.origin.y - topEdge.floatValue,self.bounds.size.width + leftEdge.floatValue + rightEdge.floatValue,self.bounds.size.height + topEdge.floatValue + bottomEdge.floatValue);
    }
    else
    {
        return self.bounds;
    }
}
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent*)event
{
    CGRect rect = [self expandRect];
    if (CGRectEqualToRect(rect, self.bounds))
    {
        return [super hitTest:point withEvent:event];
    }
    return CGRectContainsPoint(rect, point) ? self : nil;
}
@end
创建button并调用:
[button setTouchExpandEdgeWithTop:30 right:30 bottom:30 left:30];
点击button的上下左右各30pt,button也会有响应。