objective-c runtime 开发详情
目录
- 概述
- 对象与类的实质
- id与class
- 继承关系与isa
- 总结
- C函数创建一个OC类
- OC类与runtime
- NSObjectProtocol
- NSObject
- NSProxy
一、概述
Objective-C是开发 osx/ios 的主要语言,它是C语言的一个超集。Objective-C继承了C的基本语法特性,并添加了面对象的特性,所以在Objective-C代码里可以直接写C代码的。在Objective-C里面的对象是一个动态类型,只有在运行时才会被确定下来。在运行时你可以为一个类添加新的方法、属性,也可以修改这个类原有的方法、属性。提供动态添加、修改类的api我们管它叫做runtime api。这一篇文章我们就来学runtime api 、runtime与Objective-C的关系。Objective-C的runtime源码可以从这里获得 runtime 。每个版本的runtime实现不一样,本文以objc4-706.tar.gz为标板。
二、对象与类的实质
id与class
在Objective-C里面 id 代表着任意对象,Class代表着任意的类,我们可以看源码的定义:
typedef struct objc_class *Class;
typedef struct objc_object *id;
可以看出id的实质是机构体objc_object的指针,Class的实质是结构体objc_class的指针。我们在查看结构体objc_object、objc_class的源码(隐藏了一些代码)
objc_object:
struct objc_object {
private:
isa_t isa; // 唯一的成员变量 代表当前对象所指向的类的信息
public:
// 返回isa的Calss指针
Class getIsa();
// 下面还有一系列相关的函数
......
}
isa_t:
union isa_t
{
isa_t() { }
isa_t(uintptr_t value) : bits(value) { } Class cls;
uintptr_t bits;
struct {
uintptr_t nonpointer : ;
uintptr_t has_assoc : ;
uintptr_t has_cxx_dtor : ;
uintptr_t shiftcls : ; // MACH_VM_MAX_ADDRESS 0x1000000000
uintptr_t magic : ;
uintptr_t weakly_referenced : ;
uintptr_t deallocating : ;
uintptr_t has_sidetable_rc : ;
uintptr_t extra_rc : ;
};
}
objc_class:
struct objc_class : objc_object {
Class superclass;
const char *name;
uint32_t version;
uint32_t info;
uint32_t instance_size;
struct old_ivar_list *ivars;
struct old_method_list **methodLists;
Cache cache;
struct old_protocol_list *protocols;
// CLS_EXT only
const uint8_t *ivar_layout;
struct old_class_ext *ext; // 下面还有一系列相关函数
......
}
从上面我们可以看出2点
1.objc_object有一个成员变量isa用来保存这个对象指向的类的信息
2.objc_class继承objc_object,意思就是说class也是对象,我们管它叫做类对象
既然类对象也是对象,那么类对象自然也有isa成员变量了,oc里面把类对象的isa成员变量指向的类叫做元类(meta class)
元类是用来实现类方法的,那么元类对象的isa有指向什么呢?oc里面把元类对象的isa指向基类的元类。元类也是有父类...感觉无穷无尽了
我们还是来看图1吧:
我们来说明上图
1.Cat类继承Animal类,Animal类继承NSObject类,cat是Cat的一个实例
2.cat的对象的isa指向 Cat 类
3.Cat类的isa指向Cat类的元类(Cat meta class,相应的Animal类的isa指向Animal类的元类(Animal meta class),NSObject类的isa指向NSObject类的元类(NSObject meta class)
4.Cat类的元类继承Animal类的元类,Animal类的元类继承NSObject类的元类,NSObject类的元类继承NSObject类。
5.Cat meta class、Animal meta class、NSObject meta class这三个元类的isa都指向根元类(root metaclass)。大部分时候根元类就是NSObject meta class。
继承关系与isa
这些继承关系,还有isa构成了oc的对象与类整系统,它们到底怎么发生作用呢,请看下面例子。
@interface Animal : NSObject
+(BOOL)isAnimal; // 是否动物 永远返回ture
-(void)move; // 移动
@end @interface Cat :Animal
-(void)eatFish;
@end
上面是Cat与Animal类的实现,下面代码将展示对象、类怎么调用一个方法的。
Cat *cat = [Cat new]; [cat eatFish]; [cat move]; id newCat = [cat copy]; BOOL isAnimal = [Cat isAnimal];
首先是创建一个Cat的实例
Cat *cat = [Cat new];
分析上面这句代码前我们先确认一件事:实例方法是在类里实现的,类方法是在类的元类里实现的
确认了这些事,我们就可以开始分析了
1.因为new是类方法,所以第一步当然是查询Cat类的isa指向的Cat类的元类(Cat meta class)有没有这个方法,发现Cat meta class 并没有实现new方法。
2.接着查询Cat meta class的父类即Animal类的元类(Animal meta class)有没有这个方法,发现还是new方法。
3.再接着查询Animal meta class的父类既NSObject类的元类(NSObject meta class),发现NSObject meta class是有实现new方法的,所以就调用new方法返回一个实例cat。
[cat eatFish];
这句代码就比较简单了
先查询cat对象的isa指向的Cat类,有没有实现eatFish这个方法,发现Cat类已经实现了,直接调用既可。
[cat move];
这个跟[cat eatFish]类似,
1.先查询cat对象的isa指向的Cat类,发现并没有实现move方法
2.在查询Cat类的父类Animal类,发现Animal类已经实现了move,直接调用
id newCat = [cat copy];
这句代码与[cat move]类似
1.先查询cat对象的isa指向的Cat类,发现并没有实现copy方法
2.再查询Cat类的父类Animal类,发现并没有实现copy方法
3.继续查询Animal类的父类NSObject类,发现NSObject类有实现copy方法,直接调用
BOOL isAnimal = [Cat isAnimal];
这句代码与[Cat new]类似,大家可以试着分析。
总结
在Objectative-c里面一个类的实例是一个对象,一个类也是一个对象叫类对象,对象用结构体struct objc_objectl来描述,对象都有一个isa成员变量用来指向这个对象的类。对象实例的方法都走这个对象的类里实现。
类对象的isa指向这个类对象的类,类对象的类叫做元类。类的方法都在元类里实现。
元类它也是一个对象叫做元类对象。元类对象的isa指向根元类(root metaclass),根元类在大部分时候都是NSObject meta class。
当然,类的成员变量、属性、方法、协议等都由相应的机构体来表示,这里就不一一展开了,下文遇到会展开。
三、C函数创建一个OC类
这节我们用runtime的c函数来创建一个oc类,通过这种方式来加深对oc类的理解。
首先我写了一个方法用于打印一个id对象的类的相关信息,
void rtl_Class(id object){ // 获取一个对象的class
Class cls = object_getClass(object);
// 获取class 名字
const char * className = class_getName(cls);
printf("%20s :[%p (%s)]\n" ,"class Name",cls,className); // 展示类的继承关系
// 获取父类
Class superClass = class_getSuperclass(cls);
printf("%20s :[%p (%s)]" ,"class Inherit",cls,className);
while (superClass) {
printf(" => [%p (%s)]",superClass,class_getName(superClass));
superClass = class_getSuperclass(superClass);
}
printf("\n"); // 展示元类的继承关系
// 获取一个类的元类
Class metaClass = objc_getMetaClass(class_getName(cls));
printf("%20s :[%p (%s)]" ,"meta class Inherit",metaClass,class_getName(metaClass));
metaClass = class_getSuperclass(metaClass);
while (metaClass) {
printf(" => [%p (%s)]",metaClass, class_getName(metaClass));
metaClass = class_getSuperclass(metaClass);
}
printf("\n"); // 展示元类的isa信息
// isa成员变量在arm64下并不是一个Class的指针,所以不能直接这样使用 Class metaClass = cls->isa;但是可以打印cls->isa的指针
metaClass = objc_getMetaClass(class_getName(cls));
while (metaClass) {
printf("%10s meta class isa :%p \n",class_getName(metaClass),metaClass->isa);
metaClass = class_getSuperclass(metaClass);
}
// 展示类变量列表
printf("\nvar List:\n");
unsigned int count = ;
Ivar * varList = class_copyIvarList(cls, &count);
for (int i=; i<count; i++) {
Ivar var = varList[i];
printf("[%30s] [offset:%ld]\n",ivar_getName(var),ivar_getOffset(var));
}
// 展示属性列表
printf("\nproperty List:\n");
count = ;
objc_property_t * propList = class_copyPropertyList(cls, &count);
for (int i=; i<count; i++) {
objc_property_t property = propList[i];
printf("[%30s] [%s]\n",property_getName(property),property_getAttributes(property));
} // 展示类方法
printf("\nclass method List:\n");
Class metaCls = objc_getMetaClass(class_getName(cls));
count = ;
Method * methodList = class_copyMethodList(metaCls, &count);
for (int i = ; i < count; i++) {
Method method = methodList[i];
printf("[%30s]\n",sel_getName(method_getName(method)));
} // 展示实例类方法
printf("\nclass instance method List:\n");
count = ;
methodList = class_copyMethodList(cls, &count);
for (int i = ; i < count; i++) {
Method method = methodList[i];
printf("[%30s]\n",sel_getName(method_getName(method)));
}
printf("\n\n");
}
继续讲之前先,大家应该先记住runtime函数的命名规则
class_xxx --- 与类相关的函数
objc_xxxx --- 与对象相关的函数
object_xxx --- 与类实例相关的函数
property_xxx --- 与属性相关的函数
ivar_xxx --- 与类成员变量相关的函数
method_xxx --- 与方法相关的函数
sel_xxxx --- 与seletor相关的函数
imp_xxx --- 与imp相关的函数
protocol_xxx --- 与协议相关的函数
runtime api函数的的命名比较规范,一看名字就大概知道函数的功能了,后续将不再讲解runtime api,特殊除外。
所有的runtime api 都可以在这里 查到 runtime
下面我们就用runtime api来创建一个Person类,Person类继承Biology类,Biology继承NSObject类。
//
// RuntimeTestVC.m
// IOSTest
//
// Created by 朱国清 on 17/1/16.
// Copyright © 2017年 朱国清. All rights reserved.
// #import "RuntimeTestVC.h"
#import <objc/runtime.h>
#import "runtime_log.h"
#import <objc/message.h> NSString * name(id self,SEL _cmd){
// kvo coding
Ivar nameVar = class_getInstanceVariable([self class], "_name");
return object_getIvar(self,nameVar);
}
void setName(id self,SEL _cmd, NSString *name){
// kvo coding
Ivar nameVar = class_getInstanceVariable([self class], "_name");
object_setIvar(self, nameVar, name);
printf("setName : %s\n",[name UTF8String]);
}
void sayHello(id self,SEL _cmd){
Ivar ageVar = class_getInstanceVariable([self class], "age");
NSNumber * age = object_getIvar(self, ageVar);
Ivar nameVar = class_getInstanceVariable([self class], "_name");
NSString * name = object_getIvar(self, nameVar); printf("[%s][%s] my name is %s age is %d \n",class_getName([self class]),sel_getName(_cmd),[name UTF8String],(int)age.integerValue);
}
void isPerson(){
printf("is person.\n");
}
void add(id self,SEL _cmd,int x, int y){
printf("x + y = %d\n",x+y);
} @interface RuntimeTestVC () @end @implementation RuntimeTestVC - (void)viewDidLoad {
[super viewDidLoad]; [self newClass]; }
-(void)newClass{ // 创建Biology类,继承NSObject类
Class Biology = objc_allocateClassPair([NSObject class], "Biology", );
// 注册Biology类,只有注册了,才能用Biology类
objc_registerClassPair(Biology); // 创建 Person,继承Biology类
Class Person = objc_allocateClassPair(Biology, "Person", ); // 添加 类实例变量
class_addIvar(Person, "age", sizeof(NSInteger), , "i");
class_addIvar(Person, "_name", sizeof(NSString *), , "@"); // 添加 类的属性
objc_property_attribute_t type = {"T", "@\"NSString\""};
objc_property_attribute_t ownership = { "C", "" };
objc_property_attribute_t nonatomic = { "N", "" };
objc_property_attribute_t backingivar = { "V", "_name"};
objc_property_attribute_t attrs[] = {type, ownership,nonatomic, backingivar};
class_addProperty(Person, "name", attrs, ); // 实例方法
// 属性name 的setter getter 方法
class_addMethod(Person, @selector(name), (IMP)name , "v");
class_addMethod(Person, @selector(setName:),(IMP)setName, "v@"); class_addMethod(Person, @selector(say), (IMP)sayHello, "v");
class_addMethod(Person, @selector(addWithX:y:), (IMP)add, "vii"); // 注册Person类
objc_registerClassPair(Person); // 添加类方法
// 只有注册Person后才能通过objc_getMetaClass获取Person类的元类
Class personMetaClass = objc_getMetaClass(class_getName(Person));
class_addMethod(personMetaClass, @selector(isPerson), (IMP)isPerson, "v"); // 创建一个Person的实例
id zhang = [[Person alloc]init];
// 打印zhang的信息
rtl_Class(zhang); // 设置实例变量
Ivar ageVar = class_getInstanceVariable([zhang class], "age");
object_setIvar(zhang, ageVar, @); // 5种调用一个实例的方法
// 1. 调用objc_msgSend函数得把 enable strict checking of objc_msgSend calls 设置为NO
// arm64下,需要显式转换函数原型
((void(*)(id,SEL,int,int))objc_msgSend)(zhang, @selector(addWithX:y:),,);
((void(*)(id,SEL,id))objc_msgSend)(zhang,@selector(setName:),@"zhangsan1"); // 2.performSelector
[zhang performSelector:@selector(setName:) withObject:@"zhangsan2"]; // 3.直接调用函数
setName(zhang, @selector(setName:), @"zhangsan3"); // 4.直接调用Method 需要显式转换函数原型
Method setNameMethod = class_getInstanceMethod([zhang class], @selector(setName:));
((void (*)(id, Method, id)) method_invoke)(zhang, setNameMethod,@"zhangsan4"); // 5.直接调用IMP
IMP setNameIMP = class_getMethodImplementation([zhang class],@selector(setName:));
((void (*)(id, SEL, id))setNameIMP)(zhang,@selector(setName:),@"zhangsan5"); // 带返回值
NSString * name = ((id(*)(id,SEL))objc_msgSend)(zhang, @selector(name));
printf("name = %s\n",[name UTF8String]); [zhang performSelector:@selector(say)]; // 调用类方法与调用实例方法是一样的,只是第一个参数设置为类对象
((void(*)(id,SEL))objc_msgSend)((id)Person,@selector(isPerson)); //
// objc_disposeClassPair(runtimeClass); }
/*
输出 class Name :[0x7f9932da8830 (Person)]
class Inherit :[0x7f9932da8830 (Person)] => [0x7f9932d0b540 (Biology)] => [0x102ab9170 (NSObject)]
meta class Inherit :[0x7f9932da8330 (Person)] => [0x7f9932dc8e80 (Biology)] => [0x102ab9198 (NSObject)] => [0x102ab9170 (NSObject)]
Person meta class isa :0x102ab9198
Biology meta class isa :0x102ab9198
NSObject meta class isa :0x102ab9198
NSObject meta class isa :0x102ab9198 var List:
[ age] [offset:8]
[ _name] [offset:16] property List:
[ name] [T@"NSString",C,N,V_name] class method List:
[ isPerson] class instance method List:
[ addWithX:y:]
[ say]
[ setName:]
[ name] x + y = 4
setName : zhangsan1
setName : zhangsan2
setName : zhangsan3
setName : zhangsan4
setName : zhangsan5
name = zhangsan5
[Person][say] my name is zhangsan5 age is 19
is person. */ @end
代码都注释的很清楚了,耐心看完的理解应该没有问题。有几点需要说明一下
1.在arc下如果需要调用objc_msgSend等一些函数,必须做两件事。第一引入objc/runtime.h,第二把编译选项enable strict checking of objc_msgSend calls 设置为NO。
2.在ram64下调用objc_msgSend函数、method_invoke函数、IMP变量时必须显式地转化成相应的函数原型,才能正确的调用相应的函数。
3.上面代码的输出结果片段1证明图1的正确性。
4.通过objc_getMetaClass函数来获取一个Class的元类是,被获取的Class必须已经被linked进来,否则返回空。
还有两点需要解释
1.就是在runtime api中 类成员变量、给函数的参数、返回值怎么用字符串表示。具体看表1
Code |
Meaning |
c |
A char |
i |
An int |
s |
A short |
l |
A long l is treated as a 32-bit quantity on 64-bit programs. |
q |
A long long |
C |
An unsigned char |
I |
An unsigned int |
S |
An unsigned short |
L |
An unsigned long |
Q |
An unsigned long long |
f |
A float |
d |
A double |
B |
A C++ bool or a C99 _Bool |
v |
A void |
* |
A character string (char *) |
@ |
An object (whether statically typed or typed id) |
# |
A class object (Class) |
: |
A method selector (SEL) |
[array type] |
An array |
{name=type...} |
A structure |
(name=type...) |
A union |
bnum |
A bit field of num bits |
^type |
A pointer to type |
? |
An unknown type (among other things, this code is used for function pointers) |
2.就是怎么表示一个类的属性,我们先来看代码
// 添加 类的属性
objc_property_attribute_t type = {"T", "@\"NSString\""};
objc_property_attribute_t ownership = { "C", "" };
objc_property_attribute_t nonatomic = { "N", "" };
objc_property_attribute_t backingivar = { "V", "_name"};
objc_property_attribute_t attrs[] = {type, ownership,nonatomic, backingivar};
class_addProperty(Person, "name", attrs, );
用一个objc_property_attribute_t数组来描述,objc_property_attribute_t是一个结构体
typedef struct {
const char *name; /**< The name of the attribute */
const char *value; /**< The value of the attribute (usually empty) */
} objc_property_attribute_t;
有一个name与一个value。name 可以取下表2的值,value根据需要填,不需要可以填空字符串。
Code |
Meaning |
R |
The property is read-only (readonly). |
C |
The property is a copy of the value last assigned (copy). |
& |
The property is a reference to the value last assigned (retain). |
N |
The property is non-atomic (nonatomic). |
G<name> |
The property defines a custom getter selector name. The name follows the G (for example, GcustomGetter,). |
S<name> |
The property defines a custom setter selector name. The name follows the S (for example, ScustomSetter:,). |
D |
The property is dynamic (@dynamic). |
W |
The property is a weak reference (__weak). |
P |
The property is eligible for garbage collection. |
t<encoding> |
Specifies the type using old-style encoding. |
需要注意这个objc_property_attribute_t数组必须以name为T的objc_property_attribute_t开头,name 为T 的item表示这个属性的类型。可以通过@encode(<#type-name#>)返回一个类型的字符串表示。
必须以name为V的objc_property_attribute_t为结尾。name为V的item的value表示这个属性的名字。
四、OC类与runtime
一般来说,NSObject类是大多数的OC类的基类(少部分是继承自NSProxy,后面会介绍)。通过继承NSObject类,oc类获得了runtime系统接口和OC对象的能力。所以说NSObject是OC类与runtime的桥梁。
NSObjectProtocol
值得注意的是OC定义了一个名叫NSObject的协议,这个协议符号与NSObject类是完全同名的。NSObject协议定义了一系列的方法,看下列代码
@protocol NSObject - (BOOL)isEqual:(id)object;
@property (readonly) NSUInteger hash; @property (readonly) Class superclass;
- (Class)class OBJC_SWIFT_UNAVAILABLE("use 'anObject.dynamicType' instead");
- (instancetype)self; - (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2; - (BOOL)isProxy; - (BOOL)isKindOfClass:(Class)aClass;
- (BOOL)isMemberOfClass:(Class)aClass;
- (BOOL)conformsToProtocol:(Protocol *)aProtocol; - (BOOL)respondsToSelector:(SEL)aSelector; - (instancetype)retain OBJC_ARC_UNAVAILABLE;
- (oneway void)release OBJC_ARC_UNAVAILABLE;
- (instancetype)autorelease OBJC_ARC_UNAVAILABLE;
- (NSUInteger)retainCount OBJC_ARC_UNAVAILABLE; - (struct _NSZone *)zone OBJC_ARC_UNAVAILABLE; @property (readonly, copy) NSString *description;
@optional
@property (readonly, copy) NSString *debugDescription; @end
正如前面所说的,OC语言有两个基类,一个是NSObject,另一个是NSProxy,这两个基类都实现是NSObject协议。
@interface NSObject<NSObject>
@interface NSProxy<NSObject>
所以在OC里所有类都能直接用NSObject协议的所有方法,因为基类NSObject类、NSProxy类已经实现了。
NSObject
NSObject不仅是OC类的基础还是OC类与runtime交互的桥梁。接下来我们直接来学习NSObject重要的方法,来加深对runtime的理解。
load 函数
+ (void)load;
- 一个类、一个类别被添加到runtime的时候被调用
- 父类的load在子类的load之前被调用,类别的load在类别的宿主类的load之后被调用
initialize 函数
+ (void)initialize;
- 在给一个类发第一条消息时,会先于第一条消息执行initialize函数。
- 父类的initialize在子类initialize之前被调用
- 如果子类没事覆盖initialize函数,会调用父类的initialize函数,所以说一个父类的initialize有可以会被调用多次。这个问题可以通过下列代码来解决
+ (void)initialize {
if (self == [ClassName self]) {
// ... do the initialization ...
}
}
alloc 函数
+ (instancetype)alloc OBJC_SWIFT_UNAVAILABLE("use object initializers instead");
- 初始化isa,把成员变量内存都置为0,并返回一个实例
一般来说在调用alloc之后,还必须调用init函数来完成整个初始化过程。
TheClass *newObject = [[TheClass alloc] init];
performSelector:函数
- (id)performSelector:(SEL)sel {
if (!sel) [self doesNotRecognizeSelector:sel];
return ((id(*)(id, SEL))objc_msgSend)(self, sel);
} - (id)performSelector:(SEL)sel withObject:(id)obj {
if (!sel) [self doesNotRecognizeSelector:sel];
return ((id(*)(id, SEL, id))objc_msgSend)(self, sel, obj);
} - (id)performSelector:(SEL)sel withObject:(id)obj1 withObject:(id)obj2 {
if (!sel) [self doesNotRecognizeSelector:sel];
return ((id(*)(id, SEL, id, id))objc_msgSend)(self, sel, obj1, obj2);
}
- performSelector函数的功能就是给自己发一个消息,接受这个消息的SEL只支持0、1、2个参数。
- performSelector内部是直接调用objc_msgSend这个runtime api。
当给一个对象发送它不支持的消息时,OC是怎么处理的呢?整个过程涉及下面4个函数
+(BOOL)resolveInstanceMethod:(SEL)sel;
-(id)forwardingTargetForSelector:(SEL)aSelector;
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;
-(void)forwardInvocation:(NSInvocation *)anInvocation;
整个过程是处理的
1.OC首先调用的是resolveInstanceMethod函数,给个机会你动态地添加响应这个消息的函数实现,并且把YES当做返回值返回,这时OC会直接调用你新添加的函数。如果返回NO,就执行2。
2.OC会再调用forwardingTargetForSelector:,咨询你是否有其他对象othterObject可以处理这个消息,如果有就把othterObject当住返回值返回,这时OC就会把消息转发给othterObject。如果返回nil,将会执行3。
3.OC调用methodSignatureForSelector:,咨询你是否要响应这个消息,如果响应麻烦返回响应函数的methodSignature,然后会继续执行4。如果返回nil将执行5.
4.OC调用forwardInvocation:这个函数会传入一个NSInvocation,你可以通过NSInvocation来指定由哪个target来执行。就算你不执行NSInvocation,OC也不管你了,它的任务已经完成了。
5.调用doesNotRecognizeSelector抛出异常。
NSProxy
NSProxy到底是什么?有什么用?怎么用?我们这里只要讲个大概,大家有个概念就好。
NSProxy是一个虚类并不能直接用,需要创建一个类并继承它才能使用。从名字上来看,NSProxy应该是一个代理,你可以向NSProxy发送一个NSProxy并不响应的消息,NSProxy把这个消息转发给其他响应这个消息的对象。
那NSProxy有什么用呢,你可以通过NSProxy来实现多继承(OC是单继承关系)。你也可以通过NSProxy来调用远程的类(其他进程类,通过NSconnection来发生,比如 NSDistantObject类)。
创建继承NSProxy类,必须实现两个方法
forwardInvocation: -- 转发消息
methodSignatureForSelector: -- 返回一个消息的响应函数类型
我们来看下列代码,TestNSProxy类是模拟多继承的一个例子
//
// TestNSProxy.h
// IOSTest
//
// Created by 朱国清 on 17/1/22.
// Copyright © 2017年 朱国清. All rights reserved.
// #import <Foundation/Foundation.h> @interface TestNSProxy : NSProxy +(instancetype)testProxy; @end
//
// TestNSProxy.m
// IOSTest
//
// Created by 朱国清 on 17/1/22.
// Copyright © 2017年 朱国清. All rights reserved.
// #import "TestNSProxy.h" @implementation TestNSProxy{
NSString * stringObjc;
NSMutableArray * arrayObjc;
}
+(instancetype)testProxy{
// NSProxy 只能通过静态方法alloc来生成实例
return [[TestNSProxy alloc]init];
}
-(instancetype)init{
// 分别创建两个不同的对象
stringObjc = @"";
arrayObjc = [[NSMutableArray alloc]init];
return self;
}
// forwardInvocation、methodSignatureForSelector这两个函数来处理不存在的message
-(void)forwardInvocation:(NSInvocation *)invocation{
SEL sel = invocation.selector;
if ([stringObjc respondsToSelector:sel]) {
[invocation invokeWithTarget:stringObjc];
return;
}
if ([arrayObjc respondsToSelector:sel]) {
[invocation invokeWithTarget:arrayObjc];
return ;
}
[super forwardInvocation:invocation];
}
-(NSMethodSignature *)methodSignatureForSelector:(SEL)sel{
if ([stringObjc respondsToSelector:sel]) {
return [stringObjc methodSignatureForSelector:sel];
}
if ([arrayObjc respondsToSelector:sel]) {
return [arrayObjc methodSignatureForSelector:sel];
}
return [super methodSignatureForSelector:sel];
}
@end
使用TestNSProxy
TestNSProxy * proxy = (TestNSProxy *)[TestNSProxy testProxy];
// 强制转换为NSStirng * 只是骗过编译器,给TestNSProxy发TestNSProxy并没有实现的stringByAppendingString:方法。
NSString * outStr = [(NSString *)proxy stringByAppendingString:@"append String"];
NSLog(@"strObjc=%@",outStr);
// 输出 strObjc=append String // 强制转换为NSMutableArray * 只是骗过编译器,给TestNSProxy发TestNSProxy并没有实现的addObject:方法。
[(NSMutableArray *)proxy addObject:@"item1"];
[(NSMutableArray *)proxy addObject:@"item2"];
NSString * lastObject = [(NSMutableArray *)proxy lastObject];
NSLog(@"lastObject=%@",lastObject);
// 输出 lastObject=item2
objective-c runtime 开发详情的更多相关文章
- Objective C Runtime 开发介绍
简介 Objective c 语言尽可能的把决定从编译推迟到链接到运行时.只要可能,它就会动态的处理事情.这就意味着它不仅仅需要一个编译器,也需要一个运行时系统来执行变异好的代码.运行时系统就好像是O ...
- iOS 定时器开发详情
目录 概述 NSTimer performSelector GCD timer CADisplayLink 一.概述 在平时的开发任务中,定时器是我们常用的技术.这一节我们来学习iOS怎么使用定时器. ...
- KVO 开发详情
目录 概念 应用KVO的3个步骤 关联属性的KVO 手动管理KVO通知 一.概念 KVO全称是 Key-Value Observing ,是OC的一种消息发送机制.这个机制是指:假设将B对象注册为A对 ...
- KVC 开发详情
目录 概述 KVC基本技术 KVC访问函数 KVC搜索顺序 KVC集合操作 一.概述 kvc全名是Key-value coding,kvc是一种通过字符串间接的访问oc对象的属性的一种技术. 一个oc ...
- GCD 开发详情
目录 一.简介 二.dispatch Queue - 队列 三.dispatch Groups - 组 四.dispatch Semaphores - 信号量 五.dispatch Barriers ...
- iOS开发——技术精华Swift篇&Swift 2.0和Objective-C2.0混编之第三方框架的使用
swift 语言是苹果公司在2014年的WWDC大会上发布的全新的编程语言.Swift语言继承了C语言以及Objective-C的特性,且克服了C语言的兼容性问题.Swift语言采用安全编程模式,且引 ...
- Windows下搭建objective C开发环境
摘自:http://blog.csdn.net/zhanghefu/article/details/18320827 最近打算针对iPhone.iPod touch和iPad开发一些应用,所以,需要开 ...
- Windows在结构objective C开发环境
对于近期打算iPhone.iPod touch和iPad开发一些应用程序,所以.需要开始学习Objective C(苹果推出的类似C语言的开发语言).因为苹果的自我封闭的产业链发展模式(从芯片.机器. ...
- 刨根问底Objective-C Runtime(4)- 成员变量与属性
http://chun.tips/blog/2014/11/08/bao-gen-wen-di-objective[nil]c-runtime(4)[nil]-cheng-yuan-bian-lian ...
随机推荐
- JavaScript中基本知识
变量 每个变量仅仅是一个用于保存值的占位符而已. 用var操作符定义的变量将成为定义该变量的作用域中的局部变量. 省略var操作符可以定义一个全局变量.但是不推荐这种做法,因为在局部作用域中定义的全局 ...
- java基础1.5版后新特性 自动装箱拆箱 Date SimpleDateFormat Calendar.getInstance()获得一个日历对象 抽象不要生成对象 get set add System.arrayCopy()用于集合等的扩容
8种基本数据类型的8种包装类 byte Byte short Short int Integer long Long float Float double Double char Character ...
- 太阳地球月亮运行动画(使用@keyframes)
闲来无事的demo <!DOCTYPE html> <html> <head> <meta charset="utf-8"> < ...
- Windosw系统——常见的问题
1. 写在某些软件后就无法打开网页,但可以上QQ. 在卸载了一些VPN或USB无线设备后,发现自己网页打不开,但是ping能ping通,也可以登录QQ. 解决办法: (1): 开始运行——regedi ...
- Windows下安装Python数据库模块--MySQLdb
## 1.下载MySQLdb [去官网](http://pypi.python.org/pypi/MySQL-python/) 下载对应的编译好的版本(现在官网最新版本为1.2.5): MySQL-p ...
- Python__关于列表的引用 以append操作为例
对于列表这样的可变类型来说,对它操作是不会改变内存地址的. 若列表里面存的元素是整数这样的不可变类型,若修改这个元素那地址还是会改变,如: >>> a = [,,] >> ...
- 易语言制作的QQ聊天中常用的GIF图片【带源码下载】
该软件调用网页实现表情包制作,使用了精益模块. 最近比较火的王境泽.张学友.切格瓦拉.为所欲为.今天星期五.黑人问号脸.偷电瓶车.诸葛孔明.金坷垃等都可以通过此软件在线制作属于你的表情包. 太困了懒得 ...
- 数据库DDL
自己对数据库的整理,也是对自己知识的梳理 SQL ( Structure query language ) 结构化查询语言 SQL语言分为4个部分 1.DDL(Data Definition Lang ...
- No module named 'PyQt5.sip'
使用pyinstaller打包python文件为windows可执行程序可能遇到的问题 pyinstaller yourprogram.py打包的程序双击打开一闪而过,提示上面标题的错误 把pycom ...
- 【笔记】objdump命令的使用
---恢复内容开始--- objdump命令是Linux下的反汇编目标文件或者可执行文件的命令,它还有其他作用,下面以ELF格式可执行文件test为例详细介绍: objdump -f test 显示t ...