Objective-C Runtime 学习笔记之Objective-C的元素认知

什么是Runtime

Objective-C语言是一门动态语言,它将很多静态语言在编译和链接时期做的事放到了运行时来处理。这种动态语言的优势在于:我们写代码时更具灵活性,如我们可以把消息转发给我们想要的对象,或者随意交换一个方法的实现等.
我们将C++和Objective进行对比,虽然C++和Objective-C都是在C的基础上加入面向对象的特性扩充而成的程序设计语言,但二者实现的机制差异很大。C++是基于静态类型,而Objective-C是基于动态运行时类型。也就是说用C++编写的程序编译时就直接编译成了可令机器读懂的机器语言;用Objective-C编写的程序不能直接编译成可令机器读懂的机器语言,而是在程序运行的时候,通过Runtime把程序转为可令机器读懂的机器语言。Runtime是Objective不可缺少的重要一部分。
**传送门->runtime源码

类和对象

<pre>
/// An opaque type that represents an Objective-C class.
typedef struct objc_class Class;
typedef struct objc_object id;
/// Represents an instance of a 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;
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;

struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
</pre>

  • Class是一个指向objc_class结构体的指针,而id是一个指向objc_object结构体的指针,其中的isa是一个指向objc_class结构体的指针。其中的id就是我们所说的对象,Class就是我们所说的类。
  • 类与对象的区别就是类比对象多了很多特征成员,方法列表,父类,缓存,协议等信息。
  • isa:objc_object(实例对象)中isa指针指向的类结构称为class(也就是该对象所属的类),其中存放着普通成员变量与动态方法(“-”开头的方法);类对象中isa指针指向的类结构称为metaclass,其中存放着static类型的成员变量与static类型的方法(“+”开头的方法)。
  • super_class: 指向该类的父类的指针,如果该类是根类(如NSObject或NSProxy),那么super_class就为nil。
  • 类与对象的继承层次关系如下图所示,非常经典的一张图


    类与对象的继承层次关系图

成员变量和属性

  • 定义

<pre>
//成员变量 Ivar

  • typedef struct objc_ivar *Ivar;
    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

}
</pre>

<pre>
typedef struct objc_property *objc_property_t;
typedef struct {
const char *name; // 名称
const char *value; // 值(通常是空的)
} objc_property_attribute_t;
</pre>

  • 常用方法

<pre>
// 获取类中指定名称实例成员变量的信息
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 );
</pre>

<pre>
// 获取指定的属性
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 );
</pre>

方法(SEL,IMP,METHOD)

  • Method代表类中的某个方法的类型
    <pre>
    typedef struct objc_method *Method;
    struct objc_method {
    SEL method_name OBJC2_UNAVAILABLE; // 方法名
    char *method_types OBJC2_UNAVAILABLE; // 方法类型
    IMP method_imp OBJC2_UNAVAILABLE; // 方法实现
    }
    </pre>

  • Sel 代表方法名
    <pre>
    typedef struct objc_selector *SEL;
    struct objc_selector {
    *name; OBJC2_UNAVAILABLE;// 名称
    char *types; OBJC2_UNAVAILABLE;// 类型
    };
    </pre>

  • IMP 表示方法实现,实际是一个函数指针
    <pre>
    typedef id (*IMP)(id, SEL, ...);
    </pre>

  • 常用方法
    <pre>
    // 添加类方法
    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 );
    </pre>

除了以上类型外,还有objc_protocol_list,cache,version等
上面基本介绍了runtime中类和对象的基本知识,为了加深印象,这里做一个例子,手动创建一个类,添加方法并直接调用,遍历其中的成员变量和属性

runtime实践

<pre>
/**

  • 创建一个方法,并用两种方法获取成员变量的值
  • @param self <#self description#>
  • @param _cmd <#_cmd description#>
  • @param text <#text description#>
    */
    void sayHelloFunction(id self,SEL _cmd,id text)
    {
    NSLog(@"%@个轮子的%@%@",object_getIvar(self, class_getInstanceVariable([self class], "_num")),[self valueForKey:@"_name"],text);
    }

void test_runtime_class()
{
//动态创建一个继承自NSObject的Car类
Class Car = objc_allocateClassPair([NSObject class], "Car", 0);

//动态添加一个NSString *_name的成员变量
class_addIvar(Car, "_name", sizeof(NSString*), log2(sizeof(NSString*)), @encode(NSString*));
//动态添加一个int _num成员变量
class_addIvar(Car, "_num", sizeof(int), sizeof(int), @encode(int));

//注册方法名为sayHello的方法名
SEL sel = sel_registerName("sayHello:");
//动态添加sayHello的方法
class_addMethod(Car, sel, (IMP)sayHelloFunction, "v@:@");

//注册Car类
objc_registerClassPair(Car);

//创建Car的实例对象
id carInstance = [[Car alloc] init];

//从类中获取成员变量,并为成员变量赋值
Ivar nameIvar = class_getInstanceVariable(Car, "_name");
object_setIvar(carInstance, nameIvar, @"法拉利🚗");
Ivar numIvar = class_getInstanceVariable(Car, "_num");
object_setIvar(carInstance, numIvar, @4);

//直接调用方法
((void (*)(id,SEL,id)) objc_msgSend)(carInstance,sel,@"跑起来了");

//销毁类
objc_disposeClassPair(Car);

}
</pre>
<pre>
//
// People.m
// runtimeDemo
//
// Created by aaron on 16/6/5.
// Copyright © 2016年 aaron. All rights reserved.
// 遍历获取类对象的成员变量和属性列表

import "People.h"

import <objc/runtime.h>

@implementation People

  • (instancetype)initWithName:(NSString *)name reName:(NSString *)reName
    {
    self = [super init];
    if (self) {
    _name = name;
    _reName = reName;
    }

    return self;
    }

  • (NSDictionary *)allIvar
    {
    NSMutableDictionary *ivarDict = [NSMutableDictionary dictionary];

    unsigned int count;
    Ivar *ivarList = class_copyIvarList([self class], &count);
    for (int i = 0; i < count; i++) {

      Ivar ivar = ivarList[i];
      NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];
      id value = [self valueForKey:ivarName];
      if (value) {
          ivarDict[ivarName] = value;
      } else {
          ivarDict[ivarName] = [NSString stringWithFormat:@"key:%@对应的值为空",ivarName];
      }
    

    }
    free(ivarList);

    return ivarDict;
    }

  • (NSDictionary *)allProperty
    {
    NSMutableDictionary *propertyDict = [NSMutableDictionary dictionary];

    unsigned int count;
    objc_property_t *propertyList = class_copyPropertyList([self class], &count);
    for (int i = 0; i < count; i++) {

      objc_property_t property = propertyList[i];
      NSString *name = [NSString stringWithUTF8String:property_getName(property)];
      id value = [self valueForKey:name];
      if (value) {
          propertyDict[name] = value;
      } else {
          propertyDict[name] = [NSString stringWithFormat:@"key:%@对应的值为空",name];
      }
    

    }
    free(propertyList);

    return propertyDict;
    }

@end
</pre>

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,392评论 5 470
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,258评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,417评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,992评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,930评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,199评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,652评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,327评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,463评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,382评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,432评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,118评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,704评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,787评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,999评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,476评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,057评论 2 341

推荐阅读更多精彩内容