Objective-C Runtime 學習筆記之Objective-C的元素認知

什么是Runtime

Objective-C語言是一門動態(tài)語言,它將很多靜態(tài)語言在編譯和鏈接時期做的事放到了運行時來處理。這種動態(tài)語言的優(yōu)勢在于:我們寫代碼時更具靈活性,如我們可以把消息轉發(fā)給我們想要的對象,或者隨意交換一個方法的實現(xiàn)等.
我們將C++和Objective進行對比,雖然C++和Objective-C都是在C的基礎上加入面向對象的特性擴充而成的程序設計語言,但二者實現(xiàn)的機制差異很大。C++是基于靜態(tài)類型,而Objective-C是基于動態(tài)運行時類型。也就是說用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就是我們所說的類。
  • 類與對象的區(qū)別就是類比對象多了很多特征成員,方法列表,父類,緩存,協(xié)議等信息。
  • isa:objc_object(實例對象)中isa指針指向的類結構稱為class(也就是該對象所屬的類),其中存放著普通成員變量與動態(tài)方法(“-”開頭的方法);類對象中isa指針指向的類結構稱為metaclass,其中存放著static類型的成員變量與static類型的方法(“+”開頭的方法)。
  • super_class: 指向該類的父類的指針,如果該類是根類(如NSObject或NSProxy),那么super_class就為nil。
  • 類與對象的繼承層次關系如下圖所示,非常經(jīng)典的一張圖


    類與對象的繼承層次關系圖

成員變量和屬性

  • 定義

<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; // ?基地址偏移字節(jié)

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; // 方法實現(xiàn)
    }
    </pre>

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

  • IMP 表示方法實現(xiàn),實際是一個函數(shù)指針
    <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 );
    // 獲取所有方法的數(shù)組
    Method * class_copyMethodList ( Class cls, unsigned int *outCount );
    // 替代方法的實現(xiàn)
    IMP class_replaceMethod ( Class cls, SEL name, IMP imp, const char *types );
    // 返回方法的具體實現(xiàn)
    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中類和對象的基本知識,為了加深印象,這里做一個例子,手動創(chuàng)建一個類,添加方法并直接調用,遍歷其中的成員變量和屬性

runtime實踐

<pre>
/**

  • 創(chuàng)建一個方法,并用兩種方法獲取成員變量的值
  • @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()
{
//動態(tài)創(chuàng)建一個繼承自NSObject的Car類
Class Car = objc_allocateClassPair([NSObject class], "Car", 0);

//動態(tài)添加一個NSString *_name的成員變量
class_addIvar(Car, "_name", sizeof(NSString*), log2(sizeof(NSString*)), @encode(NSString*));
//動態(tài)添加一個int _num成員變量
class_addIvar(Car, "_num", sizeof(int), sizeof(int), @encode(int));

//注冊方法名為sayHello的方法名
SEL sel = sel_registerName("sayHello:");
//動態(tài)添加sayHello的方法
class_addMethod(Car, sel, (IMP)sayHelloFunction, "v@:@");

//注冊Car類
objc_registerClassPair(Car);

//創(chuàng)建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>

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容