• 深入浅出Cocoa 之动态创建类


    在前文《深入浅出Cocoa之类与对象》一文中,我已经详细介绍了ObjC中的 Class 与 Object 的概念,今天我们来如何在运行  
      动态创建类。下面这个函数就是应用前面讲到的Class,MetaClass的概念,在运行时动态创建一个类。这个函数来自《Inside Mac OS X-The Objective-C Programming Language》。

    #import <objc/objc.h>
    #import <objc/runtime.h>

    BOOL CreateClassDefinition( const char * name, const char * superclassName)
    {
        struct objc_class * meta_class;
        struct objc_class * super_class;
        struct objc_class * new_class;
        struct objc_class * root_class;
        va_list args;
        
        // 确保父类存在
        super_class = (struct objc_class *)objc_lookUpClass (superclassName);
        if (super_class == nil)
        {
            return NO;
        }
        
        // 确保要创建的类不存在
        if (objc_lookUpClass (name) != nil)
        {
            return NO;
        }
        
        // 查找 root class,因为 meta class 的 isa 指向 root class 的 meta class
        root_class = super_class;
        while( root_class->super_class != nil )
        {
            root_class = root_class->super_class;
        }
        
        // 为 class 及其 meta class 分配内存
        new_class = calloc( 2, sizeof(struct objc_class) );
        meta_class = &new_class[1];
        
        // 设置 class
        new_class->isa = meta_class;
        new_class->info = CLS_CLASS;
        meta_class->info = CLS_META;

        
    // 拷贝类名字,这里为了提高效率,让 class 与 meta class 都指向同一个类名字符串
        new_class->name = malloc (strlen (name) + 1);
        strcpy ((char*)new_class->name, name);
        meta_class->name = new_class->name;
        
        
    // 分配并置空 method lists,我们可以在之后使用   
    class_addMethods 向类中增加方法
      
        new_class->methodLists = calloc( 1, sizeof(struct objc_method_list *) );
        meta_class->methodLists = calloc( 1, sizeof(struct objc_method_list *) );
        
        
    // 将类加入到继承体系中去:
        
    // 1,设置类的 super class
        
    // 2,设置 meta class 的 super class
        
    // 3,设置 meta class 的 isa
        new_class->super_class = super_class;
        meta_class->super_class = super_class->isa;
        meta_class->isa = (void *)root_class->isa;
        
        // 最后,将 class 注册到运行时系统中
        objc_addClass( new_class );
        
        return YES;
    }

    如果要在代码中使用运行时相关的函数,我们需要导入 libobjc.dylib,并导入相关的头文件(比如这里的 runtime.h)。

    前文中总结到“ObjC 为每个类的定义生成两个 objc_class ,一个即普通的 class,另一个即 metaclass。我们可以在运行期创建这两个 objc_class 数据结构,然后使用 objc_addClass 动态地创建新的类定义。”,这在上面的代码中就体现出来了:new_class 和 meta_class 就是新类所必须的两个 objc_class。其他的代码就不解释了,注释以及代码足以自明了。

    在实际的运用中,我们使用 ObjC 运行时函数来动态创建类:
    Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes);

    譬如:
    #import <objc/objc.h>
    #import <objc/runtime.h>

    void ReportFunction(id self, SEL _cmd)
    {
        NSLog(@" >> This object is %p.", self);
        NSLog(@" >> Class is %@, and super is %@.", [self class], [self superclass]);
        
        Class prevClass = NULL;
        int count = 1;
        for (Class currentClass = [self class]; currentClass; ++count)
        {
            prevClass = currentClass;
            
            NSLog(@" >> Following the isa pointer %d times gives %p", count, currentClass);
            
            currentClass = object_getClass(currentClass);
            if (prevClass == currentClass)
                break;
        }
        
        NSLog(@" >> NSObject's class is %p", [NSObject class]);
        NSLog(@" >> NSObject's meta class is %p", object_getClass([NSObject class]));
    }

    int main (int argc, const char * argv[])
    {

        @autoreleasepool
        {
            Class newClass = objc_allocateClassPair([NSString class], "NSStringSubclass", 0);
            class_addMethod(newClass, @selector(report), (IMP)ReportFunction, "v@:");
            objc_registerClassPair(newClass);
            
            id instanceOfNewClass = [[newClass alloc] init];
            [instanceOfNewClass performSelector:@selector(report)];
            [instanceOfNewClass release];
        }
        
        return 0;
    }

    在上面的代码中,我们创建继承自 NSString 的子类 NSStringSubclass,然后向其中添加方法 report,并在运行时系统中注册,这样我们就可以使用这个新类了。在这里使用 performSelector 来向新类的对象发送消息,可以避免编译警告信息(因为我们并没有声明该类及其可响应的消息)。

    执行结果为:
     >> This object is 0x100114710.
     >> Class is NSStringSubclass, and super is NSString.
     >> Following the isa pointer 1 times gives 0x100114410
     >> Following the isa pointer 2 times gives 0x100114560
     >> Following the isa pointer 3 times gives 0x7fff7e257b50
     >> NSObject's class is 0x7fff7e257b78
     >> NSObject's meta class is 0x7fff7e257b50

    根据前文中的类关系图,我们不难从执行结果中分析出 NSStringSubclass 的内部类结构:
    1,对象的地址为 :0x100114710
    2,class 的地址为:0x100114410
    3,meta class 的地址为:0x100114560
    4,meta class 的 class 地址为:0x7fff7e257b50 (也是 NSObject 的 meta class)
    5,NSObject 的 meta class 的 meta class 是其自身
    更多0
     
  • 相关阅读:
    Using Resource File on DotNet
    C++/CLI VS CSharp
    JIT VS NGen
    [Tip: disable vc intellisense]VS2008 VC Intelisense issue
    UVa 10891 Game of Sum(经典博弈区间DP)
    UVa 10723 Cyborg Genes(LCS变种)
    UVa 607 Scheduling Lectures(简单DP)
    UVa 10401 Injured Queen Problem(简单DP)
    UVa 10313 Pay the Price(类似数字分解DP)
    UVa 10635 Prince and Princess(LCS N*logN)
  • 原文地址:https://www.cnblogs.com/BinShao/p/3561345.html
Copyright © 2020-2023  润新知