objc物件說明

weixin_34234823發表於2018-10-06

objc_object 物件

/// 表示類的例項物件
struct objc_object {
    //isa 為指向類的指標
    Class _Nonnull isa;
};

什麼是class,這就是class

/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;
struct objc_class {
    //isa 指向該類的元類
    Class _Nonnull isa;
    //父類
    Class _Nullable super_class;
    //例項物件大小
    long instance_size;
    //屬性列表
    struct objc_ivar_list * _Nullable ivars;
    //例項方法列表
    struct objc_method_list * _Nullable * _Nullable methodLists;
    //物件快取
    struct objc_cache * _Nonnull cache;
    //協議列表
    struct objc_protocol_list * _Nullable protocols;

} OBJC2_UNAVAILABLE;

什麼是 Ivar

/// An opaque type that represents an instance variable.
typedef struct objc_ivar *Ivar;

struct objc_ivar {
    //變數名稱
    char * _Nullable ivar_name;
    //變數型別
    char * _Nullable ivar_type          
    //地址偏移量
    int ivar_offset;
    //變數空間大小
    int space;                       
}

對 ivar 的訪問就可以通過 物件地址
+ ivar偏移位元組的方法。但是這又引發一個問題,我們增加了父類的ivar,這個時候佈局就出錯了,我們就不得不重新編譯子類來恢復相容性。使用Non Fragile ivars時,Runtime會進行檢測來調整類中新增的ivar的偏移量。 這樣我們就可以通過物件地址
+ 基類大小 + ivar偏移位元組的方法來計算出ivar相應的地址,並訪問到相應的ivar

ivar 的用法

執行時規定,只能在objc_allocateClassPair與objc_registerClassPair兩個函式之間為類新增變數

//額外空間     未知,通常設定為 0
  Class clazz = objc_allocateClassPair(父類class,類名,額外空間);
  //以NSString*為例
  //變數size sizeof(NSString)
  //對齊     指標型別的為log2(sizeof(NSString*))
  //型別     @encode(NSString*)
  BOOL flag = class_addIvar(clazz,變數名,變數size,對齊,型別);
  objc_registerClassPair(clazz);

Ivar的

//獲取Ivar的名稱
  const char *ivar_getName(Ivar v);
  //獲取Ivar的型別編碼,
  const char *ivar_getTypeEncoding(Ivar v)
  //通過變數名稱獲取類中的例項成員變數
  Ivar class_getInstanceVariable(Class cls, const char *name)
  //通過變數名稱獲取類中的類成員變數
  Ivar class_getClassVariable(Class cls, const char *name)
  //獲取指定類的Ivar列表及Ivar個數
  Ivar *class_copyIvarList(Class cls, unsigned int *outCount)
  //獲取例項物件中Ivar的值
  id object_getIvar(id obj, Ivar ivar) 
  //設定例項物件中Ivar的值
  void object_setIvar(id obj, Ivar ivar, id value)

還能這樣用

//在執行時建立繼承自NSObject的People類
  Class People = objc_allocateClassPair([NSObject class], "People", 0);
  //新增_name成員變數
  BOOL flag1 = class_addIvar(People, "_name", sizeof(NSString*), log2(sizeof(NSString*)), @encode(NSString*));
  if (flag1) {
      NSLog(@"NSString*型別  _name變數新增成功");
  }
  //新增_age成員變數
  BOOL flag2 = class_addIvar(People, "_age", sizeof(int), sizeof(int), @encode(int));
  if (flag2) {
      NSLog(@"int型別 _age變數新增成功");
  }
  //完成People類的建立
  objc_registerClassPair(People);
  unsigned int varCount;
  //拷貝People類中的成員變數列表
  Ivar * varList = class_copyIvarList(People, &varCount);
  for (int i = 0; i<varCount; i++) {
      NSLog(@"%s",ivar_getName(varList[i]));
  }
  //釋放varList
  free(varList);
  //建立People物件p1
  id p1 = [[People alloc]init];
  //從類中獲取成員變數Ivar
  Ivar nameIvar = class_getInstanceVariable(People, "_name");
  Ivar ageIvar = class_getInstanceVariable(People, "_age");
  //為p1的成員變數賦值
  object_setIvar(p1, nameIvar, @"張三");
  object_setIvar(p1, ageIvar, @33);
  //獲取p1成員變數的值
  NSLog(@"%@",object_getIvar(p1, nameIvar));
  NSLog(@"%@",object_getIvar(p1, ageIvar));

什麼是 Property

/// Defines a property attribute
typedef struct {
    //屬性名
    const char * _Nonnull name;           /**< The name of the attribute */
    //屬性值
    const char * _Nonnull value;          /**< The value of the attribute (usually empty) */
} objc_property_attribute_t;

特性相關編碼
屬性的特性字串 以 T@encode(type) 開頭, 以 V例項變數名稱 結尾,中間以特性編碼填充,通過property_getAttributes即可檢視

特性編碼 具體含義

R readonly

C copy

& retain

N nonatomic

G(name) getter=(name)

S(name) setter=(name)

D @dynamic

W weak

P 用於垃圾回收機制

為類新增Property

  BOOL class_addProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount)

Property的相關操作

//替換類中的屬性
  void class_replaceProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount)
  //獲取類中的屬性
  objc_property_t class_getProperty(Class cls, const char *name)
  //拷貝類中的屬性列表
  objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
  //獲取屬性名稱
  const char *property_getName(objc_property_t property)
  //獲取屬性的特性
  const char *property_getAttributes(objc_property_t property) 
  //拷貝屬性的特性列表
  objc_property_attribute_t *property_copyAttributeList(objc_property_t property, unsigned int *outCount)
  //拷貝屬性的特性的值
  char *property_copyAttributeValue(objc_property_t property, const char *attributeName)

Property其他用法

Class People = objc_allocateClassPair([NSObject class], "People", 0);
  objc_registerClassPair(People);
  //T@
  objc_property_attribute_t attribute1;
  attribute1.name = "T";
  attribute1.value=@encode(NSString*);
  //Noatomic
  objc_property_attribute_t attribute2 = {"N",""};//value無意義時通常設定為空
  //Copy
  objc_property_attribute_t attribute3 = {"C",""};
  //V_屬性名
  objc_property_attribute_t attribute4 = {"V","_name"};
  //特性陣列
  objc_property_attribute_t attributes[] ={attribute1,attribute2,attribute3,attribute4};
  //向People類中新增名為name的屬性,屬性的4個特性包含在attributes中
  class_addProperty(People, "name", attributes, 4);
  //獲取類中的屬性列表
  unsigned int propertyCount;
  objc_property_t * properties = class_copyPropertyList(People, &propertyCount);
  for (int i = 0; i<propertyCount; i++) {
      NSLog(@"屬性的名稱為 : %s",property_getName(properties[i]));
      NSLog(@"屬性的特性字串為: %s",property_getAttributes(properties[i]));
  }
  //釋放屬性列表陣列
  free(properties);

關於 Method

Method 定義

/// Defines a method
struct objc_method_description {
    SEL _Nullable name;               /**< The name of the method */
    char * _Nullable types;           /**< The types of the method arguments */
};
typedef struct objc_method *Method;
 
struct objc_method {
    SEL method_name;  // 方法名
    char *method_types;
    IMP method_imp;  // 方法實現
}

IMP, IMP實際上是一個函式指標,指向方法實現的首地址

相關操作

id (*IMP)(id, SEL, ...)
// 呼叫指定方法的實現
id method_invoke ( id receiver, Method m, ... );
 
// 呼叫返回一個資料結構的方法的實現
void method_invoke_stret ( id receiver, Method m, ... );
 
// 獲取方法名
SEL method_getName ( Method m );
 
// 返回方法的實現
IMP method_getImplementation ( Method m );
 
// 獲取描述方法引數和返回值型別的字串
const char * method_getTypeEncoding ( Method m );
 
// 獲取方法的返回值型別的字串
char * method_copyReturnType ( Method m );
 
// 獲取方法的指定位置引數的型別字串
char * method_copyArgumentType ( Method m, unsigned int index );
 
// 通過引用返回方法的返回值型別字串
void method_getReturnType ( Method m, char *dst, size_t dst_len );
 
// 返回方法的引數的個數
unsigned int method_getNumberOfArguments ( Method m );
 
// 通過引用返回方法指定位置引數的型別字串
void method_getArgumentType ( Method m, unsigned int index, char *dst, size_t dst_len );
 
// 返回指定方法的方法描述結構體
struct objc_method_description * method_getDescription ( Method m );
 
// 設定方法的實現
IMP method_setImplementation ( Method m, IMP imp );
 
// 交換兩個方法的實現
void method_exchangeImplementations ( Method m1, Method m2 );

未完,待續。。。。。

Protocol

objc_ivar_list

objc_method_list

objc_symtab

objc_cache

相關文章