一.Class
在Objective-C的runtime中有个类型是Class(只在Runtime环境中使用),用来表示Object-C中的类,定义为:
Class类型是一个指针,指向struct objc_class,而struct objc_class才是保存真正数据的地方,再看struct objc_class的声明
struct objc_class {
Class isa;
#if !__OBJC@__
Class super_class
const char *name
long version
long info
long instance_size
struct objc_ivar_list *ivars
struct objc_method_list *methodLists
struct objc_cache *cache
struct objc_protocol_list *protocls
#endif
}OBJC2_UNAVAILABLE;
二.Method
是runtime内部定义的方法,用来代表一个方法:
typedef struct objc_method *Method;
struct objc_method的声明如下:
struct objc_method {
SEL method_name
char *method_types
IMP method_imp
}
SEL和IMP代表什么?
1.根据Class和Method的定义来理解Objcetive-C中的消息机制:
1)objc_class中method list在新runtime里的定义:
typedef struct method_list_t {
uint32_t entsize_NEVER_USE;
uint32_t count;
struct method_t first ;
}method_list_t;
typedef struct method_t {
SEL name ;
const char *types;
IMP imp;
}method_t ;
SEL相当于char*,可以认为objc_class中method list保存了一个SEL<->IMP的映射
调用一个类方法[bird fly];
其中对fly的调用,其实是由编译器出入了一段代码,根据SEL找到IMP(fly就是方法名SEL),从而进行调用,插入的代码如下
runtime中msg相关函数
id objc_msgSend(id theReceiver, SEL theSelector, ...)
这个函数发送消息给theReceiver,并将返回返回值.编译器其实是将[bird fly]转化成对objc_msgSend的调用,从而实现消息机制.objec_msgSend()函数将会使用theReciver的isa指针来找到theReceiver的类空间结构,并在类空间结构中查找theSelector所对应的方法.如果没有找到,那么将使用父类的指针(指向父类的isa指针)找到父类空间结构进行theSelector的查找.如果仍然没找到,就继续去父类的父类一直到,一直到找到或者找到NSObjcet,有则调用,没有则报错undefind.
三.Ivar
Ivar(instance variable)(实例变量,跟某个对象关联,不能被静态方法使用,与之对应的是class variable)声明如下:
typedef struct objc_ivar *Ivar ;
struct objc_ivar声明如下:
struct objc_ivar {
char *ivar_name
char *ivar_type
int ivar_offset
#ifdef __LP64__
int space
#endif
}
四.Cagegory使用场景
Runtime中用来表示Category的声明为:
typedef struct objc_category *Category;
struct objc_category的定义为runtime.h文件中
Category可以动态地为已经存在的类添加新的行为.这样可以保证类的原始设计规模较小,功能增加时再逐步扩展.使用Category对类进行扩展式,不需要访问其源码,也不需要创建子类.Category使用简单的方式,实现了类的相关方法的模块化,把不同的类方法分配到不同的分类文件中.
例子:
SomeClass.h
@interface SomeClass : NSObjcet {
}
- (void)print
@end
这是类SomeClass的声明文件,其中包含方法print.如果我们不想修改原始类,也不需要添加子类的情况下,为该类增加一个hello 方法,只需要简单的定义两个文件SomeClass+Hello.h/.m,在声明文件中用"()"把Category的名称括起来.
声明如下:
#Import "SomeClass.h"
@interface SomeClass (Hello)
- (void)hello ;
@end
实现文件如下:
#Import "SomeClass+Hello.h"
@implementationSomeClass (Hello)
- (void)hello {
NSLog(@"name:%@",@"Jcaky");
}
@end
Hello是Category的名称,用原类名+Category的方式命名
#Import "SomeClass+hello.h"
SomeClass * sc = [[SomeClass alloc]init];
[sc hello];
- 当定义类的时候,在某些情况下(例如需求变更),可能想要为其中某个类或几个类中添加方法.
- 一个类中包含许多不同方法需要实现,而这些方法需要不同团队的成员实现.
- 当在使用基础类库中的类时,可能希望这些类实现一些你需要的方法
遇到以上这些需求,category可以帮助你解决问题.使用Category也有些问题需要注意
- Category可以访问原始类的实例变量,但不能添加变量,如果想添加变量,可以考虑通过继承创建子类.
- Category可以重载原始类的方法,但不推荐这么做,这么做的后果是你再也不能访问原来的方法.如果确实要重载,正确的选择是创建子类.
- 和普通接口所有区别的是,在分类的实现文件中可以不必实现所有声明的方法,只要你不去调用它.
五.SEL
Runtime中用来表示一个method selector,也是一个结构体
六.IMP
IMP是一个函数指针,每一个方法都有一个对应的IMP指针指向方法实现为:
id (*IMP)(id, SEL, ...)
其所指向的方法,返回一个id,需要传入的第一个参数是self(指向某个对象,或者一个类),第二个参数是方法的SEL.(objc_property_t、objc_method_list、objc_cache、objc_protocol_list)
1.Method Swizzing
void method_exchangeImplementations(Method m1, Method m2)
对系统方法进行替换实现一些特殊需求,运用到Category重写原始方法会覆盖的特性
配置运行时:
1)实例方法的替换:
load方法只会在程序启动时调用一次,可以不用单例
或用block实现(可不对builtsetting中的进行配置,直接调用)
2)类方法的替换
七.id
在Objective-C中id类型的对象可以转换为任何一种对象。