Runtime奇技淫巧之objc_setAssociatedObject,objc_getAssociatedObject

讲交换方法时,我们说过下节课讲给分类加属性,我猜你一点都不期待,因为你肯定会。

引言

关于分类加属性我们尝试这种方式:

  • 创建分类,直接添加,引入文件,直接调用,一步到位,双击666!!!

创建分类:

@interface Person (Character)
@property(nonatomic,strong)NSString* name;
@end
@implementation Person (Character)
@end

潇洒调用:

Person* person = [[Person alloc] init];
person.name = @"伍丽娟";

结果爆炸:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Person setName:]: unrecognized selector sent to instance 0x60000000c6f0'

原理分析

其实作为一个饱受套路的👨‍💻‍来说,肯定不会像上面这样天真烂漫的写代码。可能很多人也都知道用 objc_setAssociatedObjectobjc_getAssociatedObject进行添加,我知道你不关心其中的原理,但是我偏要说!


我们作如下操作:

  • 我们创建一个分类,并在分类中声明一个实例变量,如图:

    你会发现报错了,系统告诉你在分类中不能添加实例变量,也就是说,Person类的结构体中的实例变量链表(ivars)不可扩展,Objective-C不支持往已存在的类中添加实例变量,因此不管是系统库提供的提供的类,还是我们自定义的类,都无法动态添加成员变量。有人说我这是扯淡,系统明明有class_addIvar这个方法啊!

    这个方法的用处是:我们通过运行时来创建一个类时,我们才可以使用class_addIvar函数添加实例变量。但是这个方法也只能在objc_allocateClassPair函数与objc_registerClassPair之间调用。另外,这个类也不能是元类。

    现在我们知道分类中添加实例变量是不可能的了,于是我们还是沿着属性这条路发展才有一线生机,按照引言中的方法直接声明属性,到.m中查看,你就会发现Xcode的良心所在:


哎呦喂,它告诉你你要实现settergetter方法,虽然ivars链表不能扩展,但是methodLists可以啊。但是之前的settergetter都是结合实例变量实现的,现在该怎么办呢?
现在我们的主角才是时候闪亮登场,下面两个方法用来把一个对象与另一个对象进行关联,并不会像属性自动生成的实例变量一样在当前类开辟空间。

/*
* object  源对象
* key  关键字
* value  被关联的对象
* policy  关联策略
*/
OBJC_EXPORT void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
    OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0);
OBJC_EXPORT id objc_getAssociatedObject(id object, const void *key)
    OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0);

于是,我们的settergetter方法可以这么写:

-(NSString *)name {
    return objc_getAssociatedObject(self, @"name");
}

-(void)setName:(NSString *)name {
    objc_setAssociatedObject(self, @"name", name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

你会发现,完美解决,但是这样的属性不会生成实例变量,不要总是妄想用_name取到对应实例变量,它只是有对应的settergetter方法而已。

伍丽娟是这样理解的:

前几回我们说到,你找到了伍丽娟,并且顺利搞走了你们之间的绊脚石无敌三道杠,你觉得你的幸福生活即将开始,但是,不幸的事又发生了:
当初上帝在造人时,都有特定的特性,比如你的性别(readonly),你的朋友(readwrite),你的事业(readwrite),你的老师同学(readwrite),等等... ... ,只要属于你的,你都可以随心所欲尝试你可以自主改变的部分。但是,上帝并没有给你可以拥有女票的特性,也就说你一辈子都是单身狗,你和吴丽娟根本就不可能,因为你根本就没这个功能!
你颓废,你沮丧,你的生活没有了方向,你没有办法和她有半点联系,也没有办法让别人通过你的女票就想到她,在被人眼里,你的生命中就没有女票这个概念。但是,你发现你的思想,你的行动是独立的,你可以做自己想做的任何事,但这并不能改变你是单身狗的事实。于是你决定,哪怕不做情侣,能默默关注也好,能让别人看到你们之间还是有关系你也就心满意足了,于是你主动出击搭建你们之间的某种联系,但是她永远都不属于你,她永远无法走入你的生命!你只能单纯的关注她,你的心永远无法为他开辟出一席之地。人们也只能间接的通过你侧面的了解伍丽娟,但是在伍丽娟的角度,你啥么都不是。

代码封装

感谢上一位选手的情感真挚的发言!如果我们每一个都去像上面这样写,你自己都会觉得自己是个逗逼吧(但很多人乐此不疲)。封装一下,内附用法:

//
//  YSAssociated.h
//  RuntimeSkill
//
//  Created by ys on 2016/5/11.
//  Copyright © 2016年 ys. All rights reserved.
//

#import <objc/runtime.h>
// 添加id类型属性
#define ASSOCIATED(propertyName, setter, type, objc_AssociationPolicy)\
- (type)propertyName {\
return objc_getAssociatedObject(self, _cmd);\
}\
\
- (void)setter:(type)object\
{\
objc_setAssociatedObject(self, @selector(propertyName), object, objc_AssociationPolicy);\
}

// 添加BOOL类型属性
#define ASSOCIATED_BOOL(propertyName, setter)\
- (BOOL)propertyName {\
NSNumber *value = objc_getAssociatedObject(self, _cmd); return value.boolValue;\
}\
\
- (void)setter:(BOOL)object\
{\
objc_setAssociatedObject(self, @selector(propertyName), @(object), OBJC_ASSOCIATION_RETAIN_NONATOMIC);\
}

// 添加NSInteger类型属性
#define ASSOCIATED_NSInteger(propertyName, setter)\
- (NSInteger)propertyName {\
NSNumber *value = objc_getAssociatedObject(self, _cmd); return value.integerValue;\
}\
\
- (void)setter:(NSInteger)object\
{\
objc_setAssociatedObject(self, @selector(propertyName), @(object), OBJC_ASSOCIATION_RETAIN_NONATOMIC);\
}

// 添加float类型属性
#define ASSOCIATED_float(propertyName, setter)\
- (float)propertyName {\
NSNumber *value = objc_getAssociatedObject(self, _cmd); return value.floatValue;\
}\
\
- (void)setter:(float)object\
{\
objc_setAssociatedObject(self, @selector(propertyName), @(object), OBJC_ASSOCIATION_RETAIN_NONATOMIC);\
}

// 添加double类型属性
#define ASSOCIATED_double(propertyName, setter)\
- (double)propertyName {\
NSNumber *value = objc_getAssociatedObject(self, _cmd); return value.doubleValue;\
}\
\
- (void)setter:(double)object\
{\
objc_setAssociatedObject(self, @selector(propertyName), @(object), OBJC_ASSOCIATION_RETAIN_NONATOMIC);\
}

// 添加long long类型属性
#define ASSOCIATED_longlong(propertyName, setter)\
- (long long)propertyName {\
NSNumber *value = objc_getAssociatedObject(self, _cmd); return value.longLongValue;\
}\
\
- (void)setter:(long long)object\
{\
objc_setAssociatedObject(self, @selector(propertyName), @(object), OBJC_ASSOCIATION_RETAIN_NONATOMIC);\
}
//在类别中添加属性
//使用方法如下:
/**
 .h
 #import <Foundation/Foundation.h>
 @interface Person (AssociatedTest)
 
 @property (nonatomic, strong) NSString *name;
 @property (nonatomic, weak) id delegate;
 @property (nonatomic, assign) BOOL isOK;
 
 @end
 
.m
 #import "Person + AssociatedTest.h"
 
 @implementation NSObject (AssociatedTest)
 
 ASSOCIATED(name, setName, NSString *, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
 ASSOCIATED(delegate, setDelegate, id, OBJC_ASSOCIATION_ASSIGN)
 ASSOCIATED_BOOL(isOK, setIsOK)
 
 @end
 
 */

根据我上面的使用方法,你应该能够更加快速稳定的为扩展类添加属性了(提供一种思路:很多时候,不要只局限于你理解中的对象,你可以给系统类添加Block类型属性,把代码块作为参数进行操作),但是在有些情况下,我们并不需要如此复杂的添加一个属性,临时链接一下就可以,因为我发现这样的死心眼还挺多。理解原理,理性封装之后你用起来是不是更放得开手脚了,如果你有感到一点点爽,请给伍丽娟点个赞!!!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容