美文网首页
OC对象的分类

OC对象的分类

作者: dandelionYD | 来源:发表于2018-12-28 20:23 被阅读0次

(本文所运行的Demo是在集成了objc4源码基础上的,详见:gitHub

本文所写的项目详见:OCBasicDemo

基本概念

  • OC对象的分类
    • instance对象(实例对象)
    • class对象(类对象)
    • meta-class对 象(元类对象)

1.instance对象

  • instance对象就是通过alloc出来的对象,每次调用alloc都会开辟新的内存,都会产生新的instance对象(实例对象)
先定义一个Person自定义的类对象
@interface Person : NSObject{
   @public
   int _age;
   int  _number;
}
@end
@implementation Person
@end

---------使用--------
int main(int argc, const char * argv[]) {
   @autoreleasepool {
       //instance对象
       Person *p1 = [[Person alloc]init];
       p1->_age = 10;
       p1->_number = 100;
       
       Person *p2 = [[Person alloc]init];
       p2->_age = 12;
       p2->_number = 90;
       
       NSLog(@"%p",p1);//0x1005222a0
       NSLog(@"%p",p2);//0x1005220b0
       
       NSLog(@"%p",&(p1->_age));    //0x1005222a8
       NSLog(@"%p",&(p1->_number)); //0x1005222ac
       
       NSLog(@"%p",&(p2->_age));    //0x1005220b8
       NSLog(@"%p",&(p2->_number)); //0x1005220bc
   
       NSLog(@"End");
   }
   return 0;
}
  • p1和p2是Person的instance对象(实例化出来的)
  • 他们所占不同的内存空间(根据他们打印的内存地址就可以看出)
    分析:
    • p1的首地址和p2的首地址不一样
    • p1的_age地址和p1的首地址(相差8个字节)(0x1005222a8-0x1005222a0)
    • p1的_number地址在_age地址之后排的(加4个字节)
    • p2的_age地址和p2的首地址(相差8个字节)(0x1005220b0-0x1005220b8)
      查看内存:
      image
      我们发现:上述描述的结论

使用:xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o myMain.cpp
转为C\C++代码:

struct Person_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
    int _age;
    double _height;
};

struct NSObject_IMPL {
    Class isa;
};

我们可以推测出下面的图:(instance对象的初结构)


image
  • instance对象在内存中存储的信息包括
    • isa(这里我们暂且将它定为isa,在后面的章节会具体的分析社么是isa以及isa的作用)
    • 其他的成员变量

2.class

#import <Foundation/Foundation.h>
#import <objc/runtime.h>
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSObject *object1 = [[NSObject alloc] init];
        NSObject *object2 = [[NSObject alloc] init];
        
        Class objectClass1 = [object1 class];
        Class objectClass2 = [object2 class];
        Class objectClass3 = object_getClass(object1);
        Class objectClass4 = object_getClass(object2);
        Class objectClass5 = [NSObject class];
        
        NSLog(@"%p %p",object1,object2);//0x101803170  0x101804f60
    
        NSLog(@"%p %p %p %p %p",
              objectClass1,//0x100b14140
              objectClass2,//0x100b14140
              objectClass3,//0x100b14140
              objectClass4,//0x100b14140
              objectClass5);//0x100b14140
        NSLog(@"End");
    }
    return 0;
}

发现:

  • objectClass1~objectClass5的内存地址都是一样的(也就是说是同一个东西),他们都是NSObject的class对象(类对象)
  • 每个类在内存中有且只有一个类对象
    接下来我们来代码分析下:
    点击Class进入系统的库(这里不是自己集成的objc4-750的哟),发现是:
    *typedef struct objc_class Class;
    再次点击:objc_class进入
    发现:
struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
    struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;

从这个里面我们可以看到:
一个类信息里面包含的东西如下:

image

点击Class进入自己集成的objc4-750的源文件里面

1.点击Class----------->
typedef struct objc_class *Class;

2.点击 objc_class进入----------->
struct objc_class : objc_object {
    //Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

    class_rw_t *data() { 
        return bits.data();
    }
    //此处省略代码...
    
3.点击class_rw_t----------->
struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint32_t version;
    const class_ro_t *ro;
    method_array_t methods;
    property_array_t properties;
    protocol_array_t protocols;
    Class firstSubclass;
    Class nextSiblingClass;
    char *demangledName;
    //此处省略代码...
    
4.点击class_ro_t----------->
struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
#ifdef __LP64__
    uint32_t reserved;
#endif
    const uint8_t * ivarLayout;
    const char * name;
    method_list_t * baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;
    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;
    method_list_t *baseMethods() const {
        return baseMethodList;
    }
};

从源码里面:我们也可以找到对应的信息


3.meta-class(元类)

  • 在main.m函数里面
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
int main(int argc, const char * argv[]) {
    @autoreleasepool {
       NSObject *object1 = [[NSObject alloc] init];
        Class objectClass1 = [object1 class];
        Class objectClass2 = object_getClass(object1);
        Class objectClass3 = [NSObject class];
        
        // meta-class对象,元类对象
        // 将类对象当做参数传入,获得元类对象
        Class objectMetaClass1 = object_getClass(objectClass1);
        Class objectMetaClass2 = object_getClass(objectClass2);
        Class objectMetaClass3 = object_getClass(objectClass3);
        
        NSLog(@"%p %p %p",
              objectClass1,//0x100b14140
              objectClass2,//0x100b14140
              objectClass3 //0x100b14140
              );
        
        NSLog(@"%p %p %p",
              objectMetaClass1,//0x100b140f0
              objectMetaClass2,//0x100b140f0
              objectMetaClass3 //0x100b140f0
              );
              
        //class方法返回的一直是class对象(类对象)
        Class objectMetaClass4 = [[[NSObject class] class] class];
        NSLog(@"%p",objectMetaClass4);//0x100b14140
        
        NSLog(@"End");
        
    }
    return 0;
}
  • 发现objectMetaClass1~objectMetaClass3 他们是同一个
  • objectMetaClass1是NSObject的meta-class的对象(元类对象)
  • 每一个类在内存中有且只有一个meta-class对象
  • meta-class对象和class对象的内存结构是一样的,只是他们不同的用途不一样(见后章节:会有细讲)
  • 在内存中主要存储的信息:
    • isa指针
    • superclass指针
    • 类的方法信息 (classMethods)
    • ...(等)

补充:

我们在objc的NSObject.mm文件里面看到(或者通过command+鼠标左键,进入打断点可知)
//类的class方法
+ (Class)class {
    return self;
}

//实例对象的class方法
- (Class)class {
    return object_getClass(self);
}

在object-class.mm文件里面(或者通过command+鼠标左键,进入打断点可知)
//方法 object_getClass
Class object_getClass(id obj)
{
    if (obj) return obj->getIsa();
    else return Nil;
}

分析:

   1.
   Class objectMetaClass4 = [[[NSObject class] class] class];
   NSLog(@"%p",objectMetaClass4);//0x100b14140
   分析:
   NSObject是一个类对象,所以它掉的是+Class方法,调用class方法,一直返回的是自己(类对象)
   
   2.
   Class objectMetaClass4 = [[[object1 class] class] class];
   NSLog(@"%p",objectMetaClass4);//0x100b14140
   分析:
   object1是一个实例对象,调用-Class方法,它返回的是一个类对象(object_getClass(self)),然后类对象继续调用+Class,+Class,最终和上面的结果一致,返回的永远是类对象

由上我们可知:(+/-)class方法返回的一直是class对象(类对象)


4.查看Class是否为meta-class

    BOOL result1 = class_isMetaClass([object1 class]);
    BOOL result2 = class_isMetaClass([NSObject class]);
    BOOL result3 = class_isMetaClass(object_getClass(([NSObject class])));
    NSLog(@"%d %d %d",
            result1,//0
            result2,//0
            result3 //1
            );
    分析:result1、result2 方法返回的永远是类对象,所以他们永远也不是元类对象
    result3:传入的是类对象,返回的是元类对象

5.objc_getClass方法
这里我们先记住结论:(后面会在内存章节会具体的分析底层原理)

Class object_getClass(id obj)
1> 传入的obj可能是instance对象、class对象、meta-class对象
2> 返回值
a) 如果是instance对象,返回class对象
b) 如果是class对象,返回meta-class对象
c) 如果是meta-class对象,返回NSObject(基类)的meta-class对象

6:定义一个自定义对象来看

 @interface Person:NSObject
 @endz
 @implementation Person
 @end

 Person   *p = [[Person alloc]init];//0x102141190
 Class pClass = object_getClass(p);//0x1000011f8
 Class metaPClass = object_getClass(pClass);//0x1000011d0
 BOOL res = class_isMetaClass(metaPClass);//1
 NSLog(@"%p %p %p %d",p,pClass,metaPClass,res);

友情链接:

相关文章

网友评论

      本文标题:OC对象的分类

      本文链接:https://www.haomeiwen.com/subject/liwelqtx.html