类与对象
Objective-C Runtime 运维时之风姿罗曼蒂克:类与目的,objective-cruntime
Objective-C语言是一门动态语言,它将多数静态语言在编写翻译和链接时代做的事放到了运行时来拍卖。这种动态语言的优势在于:大家写代码时更具灵活性,如大家能够把音讯转载给大家想要的目的,也许私自沟通一个主意的完成等。
这种特性意味着Objective-C不唯有须求三个编写翻译器,还须要三个运维时系统来进行编写翻译的代码。对于Objective-C来讲,那些运行时系统有如一个操作系统相近:它让具有的做事得以健康的周转。那一个运转时系统即Objc Runtime。Objc Runtime其实是二个Runtime库,它基本上是用C和汇编写的,那一个库使得C语言有了面向对象的技术。
Runtime库首要做下边几件事:
Objective-C runtime近日有八个版本:Modern runtime和Legacy runtime。Modern Runtime 覆盖了62人的Mac OS X Apps,还会有 iOS Apps,Legacy Runtime 是早期用来给33人 Mac OS X Apps 用的,约等于足以不用管便是了。
在这里后生可畏星罗棋布文章中,大家将介绍runtime的主导工作原理,以致哪些行使它让咱们的次序变得越来越灵活。在本文中,咱们先来介绍一下类与对象,这是面向对象的底蕴,大家看看在Runtime中,类是怎么样贯彻的。
类与指标功底数据结构
Class
Objective-C类是由Class类型来表示的,它实质上是叁个指向objc_class结构体的指针。它的概念如下:
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;
/// Represents an instance of a class.
struct objc_object {
Class isa;
};
/// A pointer to an instance of a class.
typedef struct objc_object *id;
一言以蔽之,Class是一个指向objc_class结构体的指针,而id是一个指向objc_object结构体的指针,其成员isa是一个指向objec_class结构体的指针。
查看objc/runtime.h中objc_class结构体的概念如下:
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE; // 父类
const char *name OBJC2_UNAVAILABLE; // 类名
long version OBJC2_UNAVAILABLE; // 类的版本信息,默认为0
long info OBJC2_UNAVAILABLE; // 类信息,供运行期使用的一些位标识
long instance_size OBJC2_UNAVAILABLE; // 该类的实例变量大小
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; // 该类的成员变量链表
struct objc_method_list **methodLists OBJC2_UNAVAILABLE; // 方法定义的链表
struct objc_cache *cache OBJC2_UNAVAILABLE; // 方法缓存
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; // 协议链表
#endif
} OBJC2_UNAVAILABLE;
在此个概念中,上边多少个字段是大家感兴趣的
本着cache,大家用上面例子来注明其实行进程:
NSArray *array = [[NSArray alloc] init];
其流程是:
objc_object与id
objc_object是代表七个类的实例的结构体,它的定义如下(objc/objc.h):
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
typedef struct objc_object *id;
可以旁观,这一个结构体独有四个字体,即指向其类的isa指针。那样,当我们向一个Objective-C对象发送新闻时,运维时库会依据实例对象的isa指针找到那几个实例对象所属的类。Runtime库会在类的方式列表及父类的方式列表中去查究与音信对应的selector指向的法子。找到后即运营这么些艺术。
当创设二个特定类的实例对象时,分配的内部存款和储蓄器满含一个objc_object数据结构,然后是类的实例变量的数码。NSObject类的alloc和allocWithZone:方法运用函数class_createInstance来创建objc_object数据结构。
别的还会有大家广阔的id,它是三个objc_object结构类型的指针。它的留存能够让大家得以实现相近于C++中泛型的有的操作。该项目标目的足以调换为别的生龙活虎种对象,有一点点相同于C语言中void *指针类型的功力。
objc_cache
地点提到了objc_class结构体中的cache字段,它用于缓存调用过的情势。这些字段是叁个指向objc_cache结构体的指针,其定义如下:
struct objc_cache {
unsigned int mask /* total = mask + 1 */ OBJC2_UNAVAILABLE;
unsigned int occupied OBJC2_UNAVAILABLE;
Method buckets[1] OBJC2_UNAVAILABLE;
};
该结构体的字段描述如下:
元类(Meta Class)
在上头大家关系,全体的类本身也是一个指标,大家得以向那一个目的发送信息(即调用类方法)。如:
NSArray *array = [NSArray array];
这几个事例中,+array音信发送给了NSArray类,而以此NSArray也是一个指标。既然是目的,那么它也是三个objc_object指针,它满含二个指向其类的三个isa指针。那么那么些就有三个主题材料了,这些isa指针指向哪些吗?为了调用+array方法,这一个类的isa指针必得指向二个满含这一个类措施的叁个objc_class结构体。那就引出了meta-class的定义
meta-class是一个类对象的类。
当大家向二个对象发送新闻时,runtime会在此个目的所属的那一个类的方法列表中寻找方法;而向多少个类发送音信时,会在此个类的meta-class的点子列表中搜索。
meta-class之所以首要,是因为它存储着三个类的全部类方法。每种类都会有一个独立的meta-class,因为各种类的类方式基本不只怕完全相像。
再深刻一下,meta-class也是叁个类,也得以向它发送二个音信,那么它的isa又是指向哪些吗?为了不让这种结构Infiniti延伸下去,Objective-C的设计者让具有的meta-class的isa指向基类的meta-class,以此作为它们的所属类。即,任何NSObject世襲连串下的meta-class都施用NSObject的meta-class作为和睦的所属类,而基类的meta-class的isa指针是指向它和睦。这样就变成了二个统筹的闭环。
透过地点的描述,再增多对objc_class结构体中super_class指针的解析,大家就能够形容出类及相应meta-class类的叁个世袭种类了,如下图所示:
对此NSObject世襲种类来讲,其实例方法对系统中的全体实例、类和meta-class都以立竿见影的;而类措施对于种类内的全部类和meta-class都以可行的。
讲了这样多,咱们照旧来写个例证吗:
#import "ViewController.h"
#import <objc/runtime.h>
@interface ViewController ()
@end
void TextMetaCLass(id self,SEL _cmd);
@implementation ViewController
void TextMetaCLass(id self,SEL _cmd){
NSLog(@"This Object is %p",self);
NSLog(@"Class is %@, super class is %@",[self class],[self superclass]);
Class currentClass = [self class];
for (int i = 0; i < 4; i++) {
NSLog(@"Following the isa pointer %d times gives %p", i ,currentClass);
/**
* 获取类对象
*
* @param object 想要获取的类
*
* @return 类对象或nil
*/
currentClass = objc_getClass((__bridge void *)currentClass);
}
NSLog(@"NSObject's class is %p", [NSError class]);
NSLog(@"NSObject's meta class is %p", objc_getClass((__bridge void *)[NSError class]));
}
- (void)viewDidLoad {
[super viewDidLoad];
/**
* 创建一个新的类
*
* @param superclass 作为新类的父类,若为空,则为根类
* @param name 新类的名字
* @param extraBytes 为类或元类对象分配字节数,通常都是为0
*
* @return 新类或为空nil(如果创建不成功:新的类名已经存在)
*/
Class newClass = objc_allocateClassPair([NSError class], "TestClass", 0);
/**
* 为新类添加新方法(注意:不可同名)
*
* @param newClass 要添加方法的类
* @param testMetaClass 将要添加的方法名字
* @param imp 函数方法的声明 ,且该函数至少有两个参数对象,分别为self 和 _cmd.
* @param types 字符数组用于描述方法中的参数类型,因为方法中必须有self 和 _cmd 这两个参数,所以第二个跟第三个字符必须是“@:”
* @return YES 添加方法成功 NO 添加方法失败
*/
class_addMethod(newClass, @selector(testMetaClass), (IMP)TextMetaCLass, "[email protected]:");
/**
* 为类添加新的实例变量(注意:不支持为现有的类、元类添加实例变量)
*
* @param cls 要添加实例变量的类对象
* @param name 变量名字
* @param size 为变量分配内存空间
* @param alignment
* @param types 变量的类型
*
* @return YES 添加实例变量成功 NO 添加实例变量失败
*/
//class_addIvar(<#__unsafe_unretained Class cls#>, <#const char *name#>, <#size_t size#>, <#uint8_t alignment#>, <#const char *types#>)
/**
* 注册通过方法objc_allocateClassPair创建的类
*
* @param cls 即开发者创建的类
*/
objc_registerClassPair(newClass);
id instance = [[newClass alloc] initWithDomain:@"some domain" code:0 userInfo:nil];
[instance performSelector:@selector(testMetaClass)];
}
那一个事例是在运作时创制了三个NSError的子类TestClass,然后为那个子类增多三个艺术testMetaClass,这一个艺术的落到实处是TestMetaClass函数。
运营后,打字与印刷结果是
2016-08-11 14:47:55.559 Runtime1-类与对象[27720:1858642] This Object is 0x7fcbd8d4dc20
2016-08-11 14:47:55.560 Runtime1-类与对象[27720:1858642] Class is TestClass, super class is NSError
2016-08-11 14:47:55.560 Runtime1-类与对象[27720:1858642] Following the isa pointer 0 times gives 0x7fcbd8d276d0
2016-08-11 14:47:55.560 Runtime1-类与对象[27720:1858642] Following the isa pointer 1 times gives 0x0
2016-08-11 14:47:55.560 Runtime1-类与对象[27720:1858642] Following the isa pointer 2 times gives 0x0
2016-08-11 14:47:55.560 Runtime1-类与对象[27720:1858642] Following the isa pointer 3 times gives 0x0
2016-08-11 14:47:55.560 Runtime1-类与对象[27720:1858642] NSObject's class is 0x106854a88
2016-08-11 14:47:55.560 Runtime1-类与对象[27720:1858642] NSObject's meta class is 0x0
我们在for循环中,我们通过objc_getClass来获取对象的isa,并将其打印出来,依此一贯回溯到NSObject的meta-class。剖判打字与印刷结果,能够见见最终指针指向的地点是0x0,即NSObject的meta-class的类地点。
此间要求注意的是:大家在二个类对象调用class方法是无法拿到meta-class,它只是再次回到类而已。
类与对象操作函数
runtime提供了汪洋的函数来操作类与对象。类的操作方法当先四分之二是以class为前缀的,而目的的操作方法半数以上是以objc或object_为前缀。上边大家将依靠那些格局的用场来分类探讨这么些方法的使用。
类相关操作函数
大家得以回过头去拜访objc_class的概念,runtime提供的操作类的方式首要就是针对这一个结构体中的种种字段的。上边大家独家介绍那朝气蓬勃部分的函数。并在结尾以实例来演示这几个函数的现实性用法。
类名(name)
类名操作的函数首要有:
// 获取类的类名
const char * class_getName ( Class cls );
● 对于class_getName函数,假诺传入的cls为Nil,则赶回三个字字符串。
父类(super_class)和元类(meta-class)
父类和元类操作的函数重要有:
// 获取类的父类
Class class_getSuperclass ( Class cls );
// 判断给定的Class是否是一个元类
BOOL class_isMetaClass ( Class cls );
● class_getSuperclass函数,当cls为Nil或然cls为根类时,再次回到Nil。可是普通大家得以选取NSObject类的superclass方法来达到相近的目标。
● class_isMetaClass函数,假若是cls是元类,则赶回YES;即便否或许传播的cls为Nil,则赶回NO。
实例变量大小(instance_size)
实例变量大小操作的函数有:
// 获取实例大小
size_t class_getInstanceSize ( Class cls );
分子变量(ivars)及质量
在objc_class中,全数的积极分子变量、属性的音信是放在链表ivars中的。ivars是叁个数组,数组中各种成分是指向Ivar(变量音信)的指针。runtime提供了丰硕的函数来操作这一字段。大要上能够分成以下几类:
1.成员变量操作函数,首要饱含以下函数:
// 获取类中指定名称实例成员变量的信息
Ivar class_getInstanceVariable ( Class cls, const char *name );
// 获取类成员变量的信息
Ivar class_getClassVariable ( Class cls, const char *name );
// 添加成员变量
BOOL class_addIvar ( Class cls, const char *name, size_t size, uint8_t alignment, const char *types );
// 获取整个成员变量列表
Ivar * class_copyIvarList ( Class cls, unsigned int *outCount );
● class_getInstanceVariable函数,它回到叁个针对富含name钦点的成员变量新闻的objc_ivar结构体的指针(Ivar)。
● class_getClassVariable函数,如今从未找到有关Objective-C中类变量的音信,平日认为Objective-C不帮衬类变量。注意,重返的列表不分包父类的成员变量和品质。
● Objective-C不帮衬往已存在的类中增加实例变量,因而无论是是系统库提供的类,还是大家自定义的类,都不可能动态增加成员变量。但少年老成旦大家经过运行时来成立二个类的话,又应该什么给它增多成员变量呢?这时候我们就能够使用class_addIvar函数了。不过供给注意的是,那个措施只可以在objc_allocateClassPair函数与objc_registerClassPair之间调用。其余,那个类也不能够是元类。成员变量的按字节最小对齐量是1<<alignment。那有赖于ivar的门类和机械的架构。假设变量的项目是指针类型,则传递log2(sizeof(pointer_type))。
● class_copyIvarList函数,它回到二个照准成员变量音信的数组,数组中种种成分是指向该成员变量音信的objc_ivar结构体的指针。这么些数组不包罗在父类中声称的变量。outCount指针再次来到数组的尺寸。需求小心的是,我们一定要利用free()来刑满释放那一个数组。
2.属性操作函数,主要含有以下函数:
// 获取指定的属性
objc_property_t class_getProperty ( Class cls, const char *name );
// 获取属性列表
objc_property_t * class_copyPropertyList ( Class cls, unsigned int *outCount );
// 为类添加属性
BOOL class_addProperty ( Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount );
// 替换类的属性
void class_replaceProperty ( Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount );
那生机勃勃种艺术也是指向ivars来操作,但是只操作这个是性质的值。大家在后面介绍属性时会再境遇这么些函数。
3.在MAC OS X系统中,大家得以应用垃圾回笼器。runtime提供了多少个函数来规定三个对象的内存区域是不是足以被垃圾回笼器扫描,以拍卖strong/weak援引。那多少个函数定义如下:
const uint8_t * class_getIvarLayout ( Class cls );
void class_setIvarLayout ( Class cls, const uint8_t *layout );
const uint8_t * class_getWeakIvarLayout ( Class cls );
void class_setWeakIvarLayout ( Class cls, const uint8_t *layout );
但平日情形下,大家无需去主动调用那几个主意;在调用objc_registerClassPair时,会扭转合理的布局。在那不详细介绍那么些函数。
方法(methodLists)
方法操作首要有以下函数:
// 添加方法
BOOL class_addMethod ( Class cls, SEL name, IMP imp, const char *types );
// 获取实例方法
Method class_getInstanceMethod ( Class cls, SEL name );
// 获取类方法
Method class_getClassMethod ( Class cls, SEL name );
// 获取所有方法的数组
Method * class_copyMethodList ( Class cls, unsigned int *outCount );
// 替代方法的实现
IMP class_replaceMethod ( Class cls, SEL name, IMP imp, const char *types );
// 返回方法的具体实现
IMP class_getMethodImplementation ( Class cls, SEL name );
IMP class_getMethodImplementation_stret ( Class cls, SEL name );
// 类实例是否响应指定的selector
BOOL class_respondsToSelector ( Class cls, SEL sel );
class_addMethod的兑现会覆盖父类的情势达成,但不会代替本类中已存在的落到实处,假使本类中包括八个同名的落到实处,则函数会再次回到NO。假设要改过已存在落到实处,可以应用method_setImplementation。叁个Objective-C方法是叁个轻巧易行的C函数,它最少含有五个参数—self和_cmd。所以,我们的贯彻函数(IMP参数指向的函数)最少须要八个参数,如下所示:
void myMethodIMP(id self, SEL _cmd)
{
// implementation ....
}
与成员变量区别的是,大家得感到类动态增加方法,不管那么些类是或不是已存在。
此外,参数types是三个呈报传递给艺术的参数类型的字符数组,那就涉及到项目编码,大家将在后头介绍。
● class_getInstanceMethod、class_getClassMethod函数,与class_copyMethodList区别的是,那七个函数都会去寻找父类的贯彻。
● class_copyMethodList函数,重临满含全部实例方法的数组,若是供给得到类形式,则足以选取class_copyMethodList(object_getClass(cls), &count)(叁个类的实例方法是概念在元类里面)。该列表不带有父类完结的措施。outCount参数重临方法的个数。在收获到列表后,我们须求使用free()方法来释放它。
● class_replaceMethod函数,该函数的一言一行能够分为二种:假如类中不设有name钦赐的主意,则临近于class_addMethod函数同样会助长方法;假设类中已存在name钦定的不二等秘书籍,则近似于method_setImplementation同样代替原方法的落到实处。
● class_getMethodImplementation函数,该函数在向类实例发送消息时会被调用,并重临叁个照准方法达成函数的指针。那一个函数会比method_getImplementation(class_getInstanceMethod(cls, name))更加快。重返的函数指针或然是二个指向runtime内部的函数,而不鲜明是方法的实际上贯彻。比方,如若类实例不恐怕响应selector,则赶回的函数指针将是运作时音讯转运载飞机制的风姿浪漫局地。
● class_respondsToSelector函数,大家普通使用NSObject类的respondsToSelector:或instancesRespondToSelector:方法来达到同等指标。
协议(objc_protocol_list)
情商相关的操作包罗以下函数:
// 添加协议
BOOL class_addProtocol ( Class cls, Protocol *protocol );
// 返回类是否实现指定的协议
BOOL class_conformsToProtocol ( Class cls, Protocol *protocol );
// 返回类实现的协议列表
Protocol * class_copyProtocolList ( Class cls, unsigned int *outCount );
class_conformsToProtocol函数能够使用NSObject类的conformsToProtocol:方法来取代。
● class_copyProtocolList函数重临的是叁个数组,在运用后大家需求运用free()手动释放。
版本(version)
本子相关的操作包罗以下函数:
// 获取版本号
int class_getVersion ( Class cls );
// 设置版本号
void class_setVersion ( Class cls, int version );
其它
runtime还提供了多少个函数来供CoreFoundation的tool-free bridging使用,即:
Class objc_getFutureClass ( const char *name );
void objc_setFutureClass ( Class cls, const char *name );
平时我们不直接使用这三个函数。
实例(Example)
地点列举了大气类操作的函数,上边我们写个实例,来看看这一个函数的实例效果:
//-----------------------------------------------------------
// MyClass.h
@interface MyClass : NSObject <NSCopying, NSCoding>
@property (nonatomic, strong) NSArray *array;
@property (nonatomic, copy) NSString *string;
- (void)method1;
- (void)method2;
+ (void)classMethod1;
@end
//-----------------------------------------------------------
// MyClass.m
#import "MyClass.h"
@interface MyClass () {
NSInteger _instance1;
NSString * _instance2;
}
@property (nonatomic, assign) NSUInteger integer;
- (void)method3WithArg1:(NSInteger)arg1 arg2:(NSString *)arg2;
@end
@implementation MyClass
+ (void)classMethod1 {
}
- (void)method1 {
NSLog(@"call method method1");
}
- (void)method2 {
}
- (void)method3WithArg1:(NSInteger)arg1 arg2:(NSString *)arg2 {
NSLog(@"arg1 : %ld, arg2 : %@", arg1, arg2);
}
@end
//-----------------------------------------------------------
// main.h
#import "MyClass.h"
#import "MySubClass.h"
#import <objc/runtime.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
MyClass *myClass = [[MyClass alloc] init];
unsigned int outCount = 0;
Class cls = myClass.class;
// 类名
NSLog(@"class name: %s", class_getName(cls));
NSLog(@"==========================================================");
// 父类
NSLog(@"super class name: %s", class_getName(class_getSuperclass(cls)));
NSLog(@"==========================================================");
// 是否是元类
NSLog(@"MyClass is %@ a meta-class", (class_isMetaClass(cls) ? @"" : @"not"));
NSLog(@"==========================================================");
Class meta_class = objc_getMetaClass(class_getName(cls));
NSLog(@"%s's meta-class is %s", class_getName(cls), class_getName(meta_class));
NSLog(@"==========================================================");
// 变量实例大小
NSLog(@"instance size: %zu", class_getInstanceSize(cls));
NSLog(@"==========================================================");
// 成员变量
Ivar *ivars = class_copyIvarList(cls, &outCount);
for (int i = 0; i < outCount; i++) {
Ivar ivar = ivars[i];
NSLog(@"instance variable's name: %s at index: %d", ivar_getName(ivar), i);
}
free(ivars);
Ivar string = class_getInstanceVariable(cls, "_string");
if (string != NULL) {
NSLog(@"instace variable %s", ivar_getName(string));
}
NSLog(@"==========================================================");
// 属性操作
objc_property_t * properties = class_copyPropertyList(cls, &outCount);
for (int i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
NSLog(@"property's name: %s", property_getName(property));
}
free(properties);
objc_property_t array = class_getProperty(cls, "array");
if (array != NULL) {
NSLog(@"property %s", property_getName(array));
}
NSLog(@"==========================================================");
// 方法操作
Method *methods = class_copyMethodList(cls, &outCount);
for (int i = 0; i < outCount; i++) {
Method method = methods[i];
NSLog(@"method's signature: %s", method_getName(method));
}
free(methods);
Method method1 = class_getInstanceMethod(cls, @selector(method1));
if (method1 != NULL) {
NSLog(@"method %s", method_getName(method1));
}
Method classMethod = class_getClassMethod(cls, @selector(classMethod1));
if (classMethod != NULL) {
NSLog(@"class method : %s", method_getName(classMethod));
}
NSLog(@"MyClass is%@ responsd to selector: method3WithArg1:arg2:", class_respondsToSelector(cls, @selector(method3WithArg1:arg2:)) ? @"" : @" not");
IMP imp = class_getMethodImplementation(cls, @selector(method1));
imp();
NSLog(@"==========================================================");
// 协议
Protocol * __unsafe_unretained * protocols = class_copyProtocolList(cls, &outCount);
Protocol * protocol;
for (int i = 0; i < outCount; i++) {
protocol = protocols[i];
NSLog(@"protocol name: %s", protocol_getName(protocol));
}
NSLog(@"MyClass is%@ responsed to protocol %s", class_conformsToProtocol(cls, protocol) ? @"" : @" not", protocol_getName(protocol));
NSLog(@"==========================================================");
}
return 0;
}
这段程序的输出如下:
2014-10-22 19:41:37.452 RuntimeTest[3189:156810] class name: MyClass
2014-10-22 19:41:37.453 RuntimeTest[3189:156810] ==========================================================
2014-10-22 19:41:37.454 RuntimeTest[3189:156810] super class name: NSObject
2014-10-22 19:41:37.454 RuntimeTest[3189:156810] ==========================================================
2014-10-22 19:41:37.454 RuntimeTest[3189:156810] MyClass is not a meta-class
2014-10-22 19:41:37.454 RuntimeTest[3189:156810] ==========================================================
2014-10-22 19:41:37.454 RuntimeTest[3189:156810] MyClass's meta-class is MyClass
2014-10-22 19:41:37.455 RuntimeTest[3189:156810] ==========================================================
2014-10-22 19:41:37.455 RuntimeTest[3189:156810] instance size: 48
2014-10-22 19:41:37.455 RuntimeTest[3189:156810] ==========================================================
2014-10-22 19:41:37.455 RuntimeTest[3189:156810] instance variable's name: _instance1 at index: 0
2014-10-22 19:41:37.455 RuntimeTest[3189:156810] instance variable's name: _instance2 at index: 1
2014-10-22 19:41:37.455 RuntimeTest[3189:156810] instance variable's name: _array at index: 2
2014-10-22 19:41:37.455 RuntimeTest[3189:156810] instance variable's name: _string at index: 3
2014-10-22 19:41:37.463 RuntimeTest[3189:156810] instance variable's name: _integer at index: 4
2014-10-22 19:41:37.463 RuntimeTest[3189:156810] instace variable _string
2014-10-22 19:41:37.463 RuntimeTest[3189:156810] ==========================================================
2014-10-22 19:41:37.463 RuntimeTest[3189:156810] property's name: array
2014-10-22 19:41:37.463 RuntimeTest[3189:156810] property's name: string
2014-10-22 19:41:37.464 RuntimeTest[3189:156810] property's name: integer
2014-10-22 19:41:37.464 RuntimeTest[3189:156810] property array
2014-10-22 19:41:37.464 RuntimeTest[3189:156810] ==========================================================
2014-10-22 19:41:37.464 RuntimeTest[3189:156810] method's signature: method1
2014-10-22 19:41:37.464 RuntimeTest[3189:156810] method's signature: method2
2014-10-22 19:41:37.464 RuntimeTest[3189:156810] method's signature: method3WithArg1:arg2:
2014-10-22 19:41:37.465 RuntimeTest[3189:156810] method's signature: integer
2014-10-22 19:41:37.465 RuntimeTest[3189:156810] method's signature: setInteger:
2014-10-22 19:41:37.465 RuntimeTest[3189:156810] method's signature: array
2014-10-22 19:41:37.465 RuntimeTest[3189:156810] method's signature: string
2014-10-22 19:41:37.465 RuntimeTest[3189:156810] method's signature: setString:
2014-10-22 19:41:37.465 RuntimeTest[3189:156810] method's signature: setArray:
2014-10-22 19:41:37.466 RuntimeTest[3189:156810] method's signature: .cxx_destruct
2014-10-22 19:41:37.466 RuntimeTest[3189:156810] method method1
2014-10-22 19:41:37.466 RuntimeTest[3189:156810] class method : classMethod1
2014-10-22 19:41:37.466 RuntimeTest[3189:156810] MyClass is responsd to selector: method3WithArg1:arg2:
2014-10-22 19:41:37.467 RuntimeTest[3189:156810] call method method1
2014-10-22 19:41:37.467 RuntimeTest[3189:156810] ==========================================================
2014-10-22 19:41:37.467 RuntimeTest[3189:156810] protocol name: NSCopying
2014-10-22 19:41:37.467 RuntimeTest[3189:156810] protocol name: NSCoding
2014-10-22 19:41:37.467 RuntimeTest[3189:156810] MyClass is responsed to protocol NSCoding
2014-10-22 19:41:37.468 RuntimeTest[3189:156810] ==========================================================
动态创立类和目的
runtime的强硬之处在于它能在运营时成立类和对象。
动态创造类
动态创立类涉及到以下几个函数:
// 创建一个新类和元类
Class objc_allocateClassPair ( Class superclass, const char *name, size_t extraBytes );
// 销毁一个类及其相关联的类
void objc_disposeClassPair ( Class cls );
// 在应用中注册由objc_allocateClassPair创建的类
void objc_registerClassPair ( Class cls );
objc_allocateClassPair函数:就算大家要创设二个根类,则superclass钦命为Nil。extraBytes平时钦命为0,该参数是分配给类和元类对象尾部的索引ivars的字节数。
为了创立四个新类,我们要求调用objc_allocateClassPair。然后使用诸如class_addMethod,class_addIvar等函数来为新创造的类增加方法、实例变量和属性等。完结那几个后,大家须要调用objc_registerClassPair函数来注册类,之后这几个新类就可以在前后相继中央银行使了。
实例方法和实例变量应该加上到类自己上,而类格局应该加上到类的元类上。
● objc_disposeClassPair函数用于销毁多少个类,但是必要小心的是,假如程序运转中还留存类或其子类的实例,则不可能调用针对类调用该方法。
在最近介绍元类时,大家曾经有接触到那多少个函数了,在这大家再举个实例来探视那么些函数的接纳。
Class cls = objc_allocateClassPair(MyClass.class, "MySubClass", 0);
class_addMethod(cls, @selector(submethod1), (IMP)imp_submethod1, "[email protected]:");
class_replaceMethod(cls, @selector(method1), (IMP)imp_submethod1, "[email protected]:");
class_addIvar(cls, "_ivar1", sizeof(NSString *), log(sizeof(NSString *)), "i");
objc_property_attribute_t type = {"T", "@"NSString""};
objc_property_attribute_t ownership = { "C", "" };
objc_property_attribute_t backingivar = { "V", "_ivar1"};
objc_property_attribute_t attrs[] = {type, ownership, backingivar};
class_addProperty(cls, "property2", attrs, 3);
objc_registerClassPair(cls);
id instance = [[cls alloc] init];
[instance performSelector:@selector(submethod1)];
[instance performSelector:@selector(method1)];
前后相继的出口如下:
2014-10-23 11:35:31.006 RuntimeTest[3800:66152] run sub method 1
2014-10-23 11:35:31.006 RuntimeTest[3800:66152] run sub method 1
动态创立对象
动态创制对象的函数如下:
// 创建类实例
id class_createInstance ( Class cls, size_t extraBytes );
// 在指定位置创建类实例
id objc_constructInstance ( Class cls, void *bytes );
// 销毁类实例
void * objc_destructInstance ( id obj );
class_createInstance函数:创立实例时,会在暗许的内部存款和储蓄器区域为类分配内部存款和储蓄器。extraBytes参数表示分配的额外字节数。那么些额外的字节可用来存款和储蓄在类定义中所定义的实例变量之外的实例变量。该函数在ARC遭遇下不可能运用。
调用class_createInstance的功能与+alloc方法相通。不过在利用class_createInstance时,我们需求适宜的掌握大家要用它来做哪些。在底下的事例中,大家用NSString来测验一下该函数的实效:
id theObject = class_createInstance(NSString.class, sizeof(unsigned));
id str1 = [theObject init];
NSLog(@"%@", [str1 class]);
id str2 = [[NSString alloc] initWithString:@"test"];
NSLog(@"%@", [str2 class]);
输出结果是:
2014-10-23 12:46:50.781 RuntimeTest[4039:89088] NSString
2014-10-23 12:46:50.781 RuntimeTest[4039:89088] __NSCFConstantString
能够看见,使用class_createInstance函数获取的是NSString实例,并非类簇中的暗中同意占位符类__NSCFConstantString。
● objc_constructInstance函数:在钦点的职位(bytes)创造类实例。
● objc_destructInstance函数:销毁叁个类的实例,但不会放出并移除任何与其相关的引用。
实例操作函数
实例操作函数根本是针对性大家创造的实例对象的一文山会海操作函数,我们得以应用那组函数来从实例对象中赢得大家想要的有的新闻,如实例对象中变量的值。那组函数能够分成三小类:
1.针对全部对象开展操作的函数,那类函数包罗
// 返回指定对象的一份拷贝
id object_copy ( id obj, size_t size );
// 释放指定对象占用的内存
id object_dispose ( id obj );
有诸如此比豆蔻年华种现象,假如大家有类A和类B,且类B是类A的子类。类B通过增多一些万分的习性来扩张类A。未来大家创制了八个A类的实例对象,并期望在运作时将以此指标转换为B类的实例对象,那样能够拉长数据到B类的性质中。这种场合下,大家从不章程直接转变,因为B类的实例会比A类的实例越来越大,未有丰富的上空来放置对象。那个时候,大家将在以使用上述几个函数来处理这种情形,如下代码所示:
NSObject *a = [[NSObject alloc] init];
id newB = object_copy(a, class_getInstanceSize(MyClass.class));
object_setClass(newB, MyClass.class);
object_dispose(a);
2.指向性对象实例变量举行操作的函数,那类函数包罗
// 修改类实例的实例变量的值
Ivar object_setInstanceVariable ( id obj, const char *name, void *value );
// 获取对象实例变量的值
Ivar object_getInstanceVariable ( id obj, const char *name, void **outValue );
// 返回指向给定对象分配的任何额外字节的指针
void * object_getIndexedIvars ( id obj );
// 返回对象中实例变量的值
id object_getIvar ( id obj, Ivar ivar );
// 设置对象中实例变量的值
void object_setIvar ( id obj, Ivar ivar, id value );
如果实例变量的Ivar已经清楚,那么调用object_getIvar会比object_getInstanceVariable函数快,相似意况下,object_setIvar也比object_setInstanceVariable快。
3.指向性对象的类实行操作的函数,那类函数包含:
// 返回给定对象的类名
const char * object_getClassName ( id obj );
// 返回对象的类
Class object_getClass ( id obj );
// 设置对象的类
Class object_setClass ( id obj, Class cls );
取得类定义
Objective-C动态运维库会自动注册大家代码中定义的享有的类。大家也得以在运作时创立类定义并应用objc_addClass函数来注册它们。runtime提供了黄金年代层层函数来收获类定义相关的消息,这一个函数主要不外乎:
// 获取已注册的类定义的列表
int objc_getClassList ( Class *buffer, int bufferCount );
// 创建并返回一个指向所有已注册类的指针列表
Class * objc_copyClassList ( unsigned int *outCount );
// 返回指定类的类定义
Class objc_lookUpClass ( const char *name );
Class objc_getClass ( const char *name );
Class objc_getRequiredClass ( const char *name );
// 返回指定类的元类
Class objc_getMetaClass ( const char *name );
objc_getClassList函数:获取已注册的类定义的列表。我们不能够要是从该函数中拿走的类对象是后续自NSObject类其余,所以在这里些类上调用方法是,都应超越检查实验一下那些方法是还是不是在这里个类中完毕。
上边代码演示了该函数的用法:
int numClasses;
Class * classes = NULL;
numClasses = objc_getClassList(NULL, 0);
if (numClasses > 0) {
classes = malloc(sizeof(Class) * numClasses);
numClasses = objc_getClassList(classes, numClasses);
NSLog(@"number of classes: %d", numClasses);
for (int i = 0; i < numClasses; i++) {
Class cls = classes[i];
NSLog(@"class name: %s", class_getName(cls));
}
free(classes);
}
出口结果如下:
2014-10-23 16:20:52.589 RuntimeTest[8437:188589] number of classes: 1282
2014-10-23 16:20:52.589 RuntimeTest[8437:188589] class name: DDTokenRegexp
2014-10-23 16:20:52.590 RuntimeTest[8437:188589] class name: _NSMostCommonKoreanCharsKeySet
2014-10-23 16:20:52.590 RuntimeTest[8437:188589] class name: OS_xpc_dictionary
2014-10-23 16:20:52.590 RuntimeTest[8437:188589] class name: NSFileCoordinator
2014-10-23 16:20:52.590 RuntimeTest[8437:188589] class name: NSAssertionHandler
2014-10-23 16:20:52.590 RuntimeTest[8437:188589] class name: PFUbiquityTransactionLogMigrator
2014-10-23 16:20:52.591 RuntimeTest[8437:188589] class name: NSNotification
2014-10-23 16:20:52.591 RuntimeTest[8437:188589] class name: NSKeyValueNilSetEnumerator
2014-10-23 16:20:52.591 RuntimeTest[8437:188589] class name: OS_tcp_connection_tls_session
2014-10-23 16:20:52.591 RuntimeTest[8437:188589] class name: _PFRoutines
......还有大量输出
获取类定义的法子有八个:objc_lookUpClass, objc_getClass和objc_getRequiredClass。借使类在运转时未注册,则objc_lookUpClass会返回nil,而objc_getClass会调用类管理回调,并再次确认类是不是注册,即使确认未注册,再回来nil。而objc_getRequiredClass函数的操作与objc_getClass相同,只可是若无找到类,则会杀死进度。
● objc_getMetaClass函数:如若内定的类未有登记,则该函数会调用类管理回调,并再一次肯定类是不是注册,假使承认未注册,再回去nil。可是,各类类定义都必须要有三个得力的元类定义,所以那几个函数总是会回来七个元类定义,不管它是或不是可行。
正文转自
Runtime 运维时之后生可畏:类与对象,objective-cruntime Objective-C语言是一门动态语言,它将过多静态语言在编写翻译和链接时代做的事放到了运...
本文由今晚开什么码发布于计算机网络,转载请注明出处:类与对象
关键词:
下一篇:没有了