转:Object-Runtime的基本数据类型

Class

Objective-C是支持反射的,先来了解一下其如何表达一个类。在Objective-C的Runtime中有个类型是Class(只在Runtime环境中使用),用来表示Objective-C中的类,其定义为:

typedef struct objc_class *Class;

可以看出,其实Class类型是一个指针,指向struct  objc_class,而struct  objc_class才是保存真正数据的地方,再看struct  objc_class的声明(from http://www.opensource.apple.com/source/objc4/objc4-493.9/runtime/runtime.h):

struct objc_class {
    Class isa;

#if !__OBJC2__
    Class super_class                                        OBJC2_UNAVAILABLE;
    const char *name                                         OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    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;

其中包含了方法列表、父类等信息,详细的可以稍后再看。

Method

是Runtime内部定义的方法,用来代表一个方法,其声明如下:

typedef struct objc_method *Method;

而struct  objc_method的声明如下:

struct objc_method {
    SEL method_name                                          OBJC2_UNAVAILABLE;
    char *method_types                                       OBJC2_UNAVAILABLE;
    IMP method_imp                                           OBJC2_UNAVAILABLE;
}

SEL和IMP代表什么需要看下面的内容。如果你已经了解了SEL和IMP的含义,可以看看下面这段:根据Class和Method的定义来理解Objective C中的消息机制:

先看看objc_class中method list的在runtime(http://opensource.apple.com/source/objc4/objc4-437/runtime/objc-runtime-new.h)里的定义:

typedef struct method_list_t {
    uint32_t entsize_NEVER_USE;  // low 2 bits used for fixup markers
    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 * aBird = [[Bird alloc] init];

[aBird fly];

其中对fly的调用,其实是由编译器插入了一些代码,根据SEL([aBird fly] 中的fly就是SEL)找到了IMP,从而进行调用的。下面看编译器插入了什么样的代码。我们来看Objective C runtime中跟msg相关的函数:

id objc_msgSend(id theReceiver, SEL theSelector, ...)

这个函数发送消息给theReceiver,并将返回值返回。编译器其实就是将[aBird fly]转化成了对objc_msgSend的调用,从而实现消息机制的。objec_msgSend()函数将会使用theReceiver的isa指针来找到theReceiver的类空间结构并在类空间结构中查找theSelector所对应的方法。如果没有找到,那么将使用指向父类的指针找到父类空间结构进行theSelector的查找。如果仍然没有找到,就继续往父类的父类一直找,直到找到为止。如果找不到怎么办呢?关于消息机制,有一篇引用文章,介绍的更加详细,这里就不赘述。

Ivar

Runtime中用来表示instance variable(实例变量,跟某个对象关联,不能被静态方法使用,与之想对应的是class variable),其声明如下:

typedef struct objc_ivar *Ivar;

而struct objc_ivar的声明如下:

struct objc_ivar {
    char *ivar_name                                          OBJC2_UNAVAILABLE;
    char *ivar_type                                          OBJC2_UNAVAILABLE;
    int ivar_offset                                          OBJC2_UNAVAILABLE;
#ifdef __LP64__
    int space                                                OBJC2_UNAVAILABLE;
#endif
}                                                            OBJC2_UNAVAILABLE;

Category

Runtime中用来表示Category( link ?),其声明为:

typedef struct objc_category *Category;

struct objc_category 的定义也在runtime.h文件中。

[]Catagory可以动态地为已经存在的类添加新的行为。这样可以保证类的原始设计规模较小,功能增加时再逐步扩展。使用Category对类进行扩展时,不需要访问其源代码,也不需要创建子类。Category使用简单的方式,实现了类的相关方法的模块化,把不同的类方法分配到不同的分类文件中。下面看一个例子:

SomeClass.h
@interface SomeClass : NSObject{
}
-(void) print;
@end 

这是类SomeClass的声明文件,其中包含一个实例方法print。如果我们想在不修改原始类、不增加子类的情况下,为该类增加一个hello的方法,只需要简单的定义两个文件SomeClass+Hello.h和SomeClass+Hello.m,在声明文件和实现文件中用“()”把Category的名称括起来即可。声明文件代码如下:

#import "SomeClass.h"

@interface SomeClass (Hello)
-(void)hello;
@end

实现文件代码如下:

#import "SomeClass+Hello.h"
@implementationSomeClass (Hello)
-(void)hello{
    NSLog (@"name:%@ ", @"Jacky");
}
@end

其中Hello是Category的名称,如果你用XCode创建Category,那么需要填写的内容包括名称和要扩展的类的名称。这里还有一个约定成俗的习惯,将声明文件和实现文件名称统一采用“原类名+Category”的方式命名。
调用也非常简单,毫无压力,首先引入Category的声明文件,然后正常调用即可:

#import "SomeClass+Hello.h"

SomeClass * sc =[[SomeClass alloc] init];
[sc hello] 

执行结果是:
name:Jacky

Category的使用场景:
1、当你在定义类的时候,在某些情况下(例如需求变更),你可能想要为其中的某个或几个类中添加方法。
2、一个类中包含了许多不同的方法需要实现,而这些方法需要不同团队的成员实现
3、当你在使用基础类库中的类时,你可能希望这些类实现一些你需要的方法。

遇到以上这些需求,Category可以帮助你解决问题。当然,使用Category也有些问题需要注意,
1、Category可以访问原始类的实例变量,但不能添加变量,如果想添加变量,可以考虑通过继承创建子类。
2、Category可以重载原始类的方法,但不推荐这么做,这么做的后果是你再也不能访问原来的方法。如果确实要重载,正确的选择是创建子类。
3、和普通接口有所区别的是,在分类的实现文件中可以不必实现所有声明的方法,只要你不去调用它。

用好Category可以充分利用Objective-C的动态特性,编写出灵活简洁的代码。

SEL

Runtime中用来表示一个method selector,其声明为:

typedef struct objc_selector     *SEL;

没有找到struct objc_selector的定义,有人说是编译器定义的,GCC 和MacOSX的实现方式还不一样,不想花时间找GCC的代码,而且也没那么重要,所以就先姑且相信这个说法吧。

IMP

IMP是一个函数指针,指向方法的实现,其定义为:

id (*IMP)(id, SEL, ...)

其所指向的方法,返回一个id(Cocoa 对象),需要传入的第一个参数是self(指向某个对象,或者一个类),第二个参数是方法的SEL。

objc_property_t

objc_method_list

objc_cache

objc_protocol_list

id

在 Objective-C中id类型的对象可以转换为任何一种对象,有点类似与void *指针类型的作用。下面简要介绍一下id类型。

id标志符:通用对象类型。id类型是一个独特的数据类型,可以转换为任何数据类型,即id类型的变量可以存放任何数据类型的对象。id在objc.h中的定义为:

typedef struct objc_object {
    Class isa;
} *id;

从上面的介绍,我们已经知道Class是struct  objc_class的指针别名,所以id可以指向一个第一个元素是Class的struct;那么它为什么可以指向NSObject对象呢?下面看NSObject的定义:

@interface NSObject <NSObject> {
    Class    isa;
}

可以看出NSObject的第一个对象是Class类型的isa。因为第一个元素相同,也就意味着可以互相cast而不损失信息,下面是用C语言来演示的其实现原理:

#include <stdio.h>
#include <stdlib.h>
struct objc_class
{
    int count;
    char * name;
};

typedef struct objc_class * Class;
typedef struct objc_obj0
{
    Class isa;
}*id;

typedef struct objc_obj1
{
    Class isa;
    int a;
}*id1;

typedef struct objc_obj2
{
    Class isa;
    char *b;
}*id2;

int main(int argc, char **argv)
{
    // id 的第一个元素与id1是一样的,所以可以用id指向id1的元素,而不损失任何信息,不过后续使用的时候应该使用其实际类型
    id a = (struct objc_obj1 *)malloc(sizeof(struct objc_obj1));
    id b = (struct objc_obj1 *)malloc(sizeof(struct objc_obj1));
}

实施上,通常而言,这样使用时编译器是要report warning的,我们可以在.m文件中加入下面的代码来验证:

typedef struct objc_object *id2;

id2 = [[NSNumber alloc] initWithInt:(i*3)];

这时是会报incompatible pointer types initializing ‘id2‘ (aka ‘struct objc_object *‘) with an expression of type ‘NSNumber *‘ 的,但id2和id的定义相同,为什么使用id时不会有这个warning呢?因为编译器对id做了特殊处理,不报warning。这下对id有了更多了解了吧。后续而来的问题就是,为什么可以在id类型上调用一些NSNumber上才有的方法呢?这一部分留到Dynamic Typing and Dynamic binding时再说吧。

http://unixjunkie.blogspot.com/2008/03/id-vs-nsobject-vs-id.html

http://www.cppblog.com/kesalin/archive/2011/08/15/objc_message.html

http://www.cnblogs.com/chijianqiang/archive/2012/06/22/objc-category-protocol.html

转:Object-Runtime的基本数据类型

时间: 2024-10-06 22:30:50

转:Object-Runtime的基本数据类型的相关文章

【IOS 开发】Object - C 入门 之 数据类型详解

作者 : 韩曙亮 转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/38544659 1. 数据类型简介及输出 (1) 数据类型简介 数据类型简介 : Object - C 数据类型 分为 基本数据类型, 构造类型 和 指针类型; -- 基本数据类型 : 整型, 字符型, 浮点型 (float 和 double), 枚举型; -- 构造类型 : 数组类型, 结构体类型, 共用体类型; -- 指针类型 : 最终要的数据类型, 所有

【IOS 开发】Object - C 入门 之 数据类型具体解释

作者 : 韩曙亮 转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/38544659 1. 数据类型简单介绍及输出 (1) 数据类型简单介绍 数据类型简单介绍 : Object - C 数据类型 分为 基本数据类型, 构造类型 和 指针类型; -- 基本数据类型 : 整型, 字符型, 浮点型 (float 和 double), 枚举型; -- 构造类型 : 数组类型, 结构体类型, 共用体类型; -- 指针类型 : 终于要的数据

QVariant(相当于是Java里面的Object,起到一个数据类型“擦除”的作用,可以使用Q_DECLARE_METATYPE进行注册)

=QVariant= [%这个类型相当于是Java里面的Object,它把绝大多数Qt提供的数据类型都封装起来,起到一个数据类型“擦除”的作用.比如我们的 table单元格可以是string,也可以是int,也可以是一个颜色值,那么这么多类型怎么返回呢?于是,Qt提供了这个QVariant类型,你可 以把这很多类型都存放进去,到需要使用的时候使用一系列的to函数取出来即可.比如你把int包装成一个QVariant,使用的时候要用 QVariant::toInt()重新取出来.这里需要注意的是,Q

Object C中的数据类型表

类型 例子 NSLog chars char 'a', '\n'  %c short int   — %hi, %hx, %ho unsigned short int   %hu, %hx, %ho int  12, -97, 0xFFE0, 0177 %i, %x, %o unsigned int 12u, 100U, 0XFFu %u, %x, %o long int 12L, -2001, 0xffffL %li, %lx, %lo unsigned long int 12UL, 100u

runtime介绍及基本使用

1. 概念 runtime(运行时系统),是一套基于C语言API,包含在 <objc/runtime.h>和<objc/message.h>中,运行时系统的功能是在运行期间(而不是编译期或其他时机)通过代码去动态的操作类(获取类的内部信息和动态操作类的成员),如创建一个新类.为某个类添加一个新的方法或者为某个类添加实例变量.属性,或者交换两个方法的实现.获取类的属性列表.方法列表等和Java中的反射技术类似. 2. 探索 程序最终运行的是二进制的可执行文件,编译器需要将OC代码转换

java中Object转换成int或String类型方法

转载: http://www.cnblogs.com/1020182600HENG/p/6137206.html Object obj = getObject(); if(obj instanceof Integer) int value = (Integer)obj; 1 String转换为int类型的方法: 1. Integer.parseInt([String]) 2.Integer.valueOf([String]).intValue(); 3.Integer.decode([Strin

数据类型及数据类型的检测

基本数据类型 number  string  boolean  underfined  null: 引用数据类型 Object  Array  RegExp: typeof 检测数据类型 但是不能检测(对象.数组.正则) instanceof/constructor检测某一个实例是否属于一个类: 我们的constructor可以避免instanceof检测的时候,用Object也是true的问题: console.log([].constructor === Object);//->false

Javascript数据类型共有六种

/* var box; alert(typeof box); // box是Undefined类型,值是undefined,类型返回的字符串是undefined var box = true; alert(typeof box); // box是Boolean类型,值是true,类型返回的字符串是boolean var box = 'a'; alert(typeof box); // box是String类型,值是'a',类型返回的字符串是string var box = 123; alert(

Javascript数据类型之Undefined

Javascrip中的数据类型分为原始数据类型(primitive type)和对象数据类型(object type). 原始数据类型 原始数据类型包括:数字.字符串.布尔值.null.undefined. 对象数据类型 对象数据类型是Javascript中的数据类型中除了数字.字符串.布尔值之外的数据类型就都是对象类型了. 关于Javascript中的数据类型的简单介绍可参考:http://www.easy-dis.net/?p=161,这里就不详细介绍了.这里主要说一下undefined.

JS检测数据类型

如果你要判断的是基本数据类型或JavaScript内置对象,使用toString: 如果要判断的时自定义类型,请使用instanceof. 1.typeof typeof操作符返回的是类型字符串,它的返回值有6种取值: typeof 3 // "number" typeof "abc" // "string" typeof {} // "object" typeof true // "boolean" ty