目录

  • 概述
  • 对象与类的实质
    • 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 开发详情的更多相关文章

  1. Objective C Runtime 开发介绍

    简介 Objective c 语言尽可能的把决定从编译推迟到链接到运行时.只要可能,它就会动态的处理事情.这就意味着它不仅仅需要一个编译器,也需要一个运行时系统来执行变异好的代码.运行时系统就好像是O ...

  2. iOS 定时器开发详情

    目录 概述 NSTimer performSelector GCD timer CADisplayLink 一.概述 在平时的开发任务中,定时器是我们常用的技术.这一节我们来学习iOS怎么使用定时器. ...

  3. KVO 开发详情

    目录 概念 应用KVO的3个步骤 关联属性的KVO 手动管理KVO通知 一.概念 KVO全称是 Key-Value Observing ,是OC的一种消息发送机制.这个机制是指:假设将B对象注册为A对 ...

  4. KVC 开发详情

    目录 概述 KVC基本技术 KVC访问函数 KVC搜索顺序 KVC集合操作 一.概述 kvc全名是Key-value coding,kvc是一种通过字符串间接的访问oc对象的属性的一种技术. 一个oc ...

  5. GCD 开发详情

    目录 一.简介 二.dispatch Queue - 队列 三.dispatch Groups - 组 四.dispatch Semaphores - 信号量 五.dispatch Barriers ...

  6. iOS开发——技术精华Swift篇&Swift 2.0和Objective-C2.0混编之第三方框架的使用

    swift 语言是苹果公司在2014年的WWDC大会上发布的全新的编程语言.Swift语言继承了C语言以及Objective-C的特性,且克服了C语言的兼容性问题.Swift语言采用安全编程模式,且引 ...

  7. Windows下搭建objective C开发环境

    摘自:http://blog.csdn.net/zhanghefu/article/details/18320827 最近打算针对iPhone.iPod touch和iPad开发一些应用,所以,需要开 ...

  8. Windows在结构objective C开发环境

    对于近期打算iPhone.iPod touch和iPad开发一些应用程序,所以.需要开始学习Objective C(苹果推出的类似C语言的开发语言).因为苹果的自我封闭的产业链发展模式(从芯片.机器. ...

  9. 刨根问底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 ...

随机推荐

  1. JavaScript中基本知识

    变量 每个变量仅仅是一个用于保存值的占位符而已. 用var操作符定义的变量将成为定义该变量的作用域中的局部变量. 省略var操作符可以定义一个全局变量.但是不推荐这种做法,因为在局部作用域中定义的全局 ...

  2. 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 ...

  3. 太阳地球月亮运行动画(使用@keyframes)

    闲来无事的demo <!DOCTYPE html> <html> <head> <meta charset="utf-8"> < ...

  4. Windosw系统——常见的问题

    1. 写在某些软件后就无法打开网页,但可以上QQ. 在卸载了一些VPN或USB无线设备后,发现自己网页打不开,但是ping能ping通,也可以登录QQ. 解决办法: (1): 开始运行——regedi ...

  5. Windows下安装Python数据库模块--MySQLdb

    ## 1.下载MySQLdb [去官网](http://pypi.python.org/pypi/MySQL-python/) 下载对应的编译好的版本(现在官网最新版本为1.2.5): MySQL-p ...

  6. Python__关于列表的引用 以append操作为例

    对于列表这样的可变类型来说,对它操作是不会改变内存地址的. 若列表里面存的元素是整数这样的不可变类型,若修改这个元素那地址还是会改变,如: >>> a = [,,] >> ...

  7. 易语言制作的QQ聊天中常用的GIF图片【带源码下载】

    该软件调用网页实现表情包制作,使用了精益模块. 最近比较火的王境泽.张学友.切格瓦拉.为所欲为.今天星期五.黑人问号脸.偷电瓶车.诸葛孔明.金坷垃等都可以通过此软件在线制作属于你的表情包. 太困了懒得 ...

  8. 数据库DDL

    自己对数据库的整理,也是对自己知识的梳理 SQL ( Structure query language ) 结构化查询语言 SQL语言分为4个部分 1.DDL(Data Definition Lang ...

  9. No module named 'PyQt5.sip'

    使用pyinstaller打包python文件为windows可执行程序可能遇到的问题 pyinstaller yourprogram.py打包的程序双击打开一闪而过,提示上面标题的错误 把pycom ...

  10. 【笔记】objdump命令的使用

    ---恢复内容开始--- objdump命令是Linux下的反汇编目标文件或者可执行文件的命令,它还有其他作用,下面以ELF格式可执行文件test为例详细介绍: objdump -f test 显示t ...