美文网首页
ios基础整理

ios基础整理

作者: 江水东流 | 来源:发表于2017-12-20 10:58 被阅读15次

    1 基础篇 类与对象

    Class

    Objective-C类是由Class类型来表示的,它实际上是一个指向objc_class结构体的指针。它的定义如下:

    typedef struct objc_class *Class;

    查看objc/runtime.h中objc_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;  // 类的版本信息,默认为0
    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;

    isa:需要注意的是在Objective-C中,所有的类自身也是一个对象,这个对象的Class里面也有一个isa指针,它指向metaClass(元类),
    super_class:指向该类的父类,如果该类已经是最顶层的根类(如NSObject或NSProxy),则super_class为NULL。
    cache:用于缓存最近使用的方法。一个接收者对象接收到一个消息时,它会根据isa指针去查找能够响应这个消息的对象。在实际使用中,这个对象只有一部分方法是常用的,很多方法其实很少用或者根本用不上。这种情况下,如果每次消息来时,我们都是methodLists中遍历一遍,性能势必很差。这时,cache就派上用场了。在我们每次调用过一个方法后,这个方法就会被缓存到cache列表中,下次调用的时候runtime就会优先去cache中查找,如果cache没有,才去methodLists中查找方法。这样,对于那些经常用到的方法的调用,但提高了调用的效率。

    NSArray *array = [[NSArray alloc] init];

    其流程是:

    1. [NSArray alloc]先被执行。因为NSArray没有+alloc方法,于是去父类NSObject去查找。
    2. 检测NSObject是否响应+alloc方法,发现响应,于是检测NSArray类,并根据其所需的内存空间大小开始分配内存空间,然后把isa指针指向NSArray类。同时,+alloc也被加进cache列表里面。
    3. 接着,执行-init方法,如果NSArray响应该方法,则直接将其加入cache;如果不响应,则去父类查找。
    4. 在后期的操作中,如果再以[[NSArray alloc] init]这种方式来创建数组,则会直接从cache中取出相应的方法,直接调用。

    objc_object与id

    objc_object是表示一个类的实例的结构体,它的定义如下(objc/objc.h):
    objc
    struct objc_object {
    Class isa OBJC_ISA_AVAILABILITY;
    };
    typedef struct objc_object *id;

    可以看到,这个结构体只有一个字体,即指向其类的isa指针。这样,当我们向一个Objective-C对象发送消息时,运行时库会根据实例对象的isa指针找到这个实例对象所属的类。Runtime库会在类的方法列表及父类的方法列表中去寻找与消息对应的selector指向的方法。找到后即运行这个方法。

    当创建一个特定类的实例对象时,分配的内存包含一个objc_object数据结构,然后是类的实例变量的数据。NSObject类的alloc和allocWithZone:方法使用函数class_createInstance来创建objc_object数据结构。

    另外还有我们常见的id,它是一个objc_object结构类型的指针。它的存在可以让我们实现类似于C++中泛型的一些操作。该类型的对象可以转换为任何一种对象,有点类似于C语言中void *指针类型的作用。

    alloc和init可以看做是是这样的,alloc可以看做是做了一个初稿,init按照这个初稿完成了一个可以使用的产品(对象)。
    alloc分配内存 内部存放定义的属性
    init 初始化实例变量 将初始化完成以后的空间首地址返回去

    // 创建一个新类和元类
    Class objc_allocateClassPair ( Class superclass, const char *name, size_t extraBytes );

    // 销毁一个类及其相关联的类
    void objc_disposeClassPair ( Class cls );

    // 在应用中注册由objc_allocateClassPair创建的类
    void objc_registerClassPair ( Class cls );
    为了创建一个新类,我们需要调用objc_allocateClassPair。然后使用诸如class_addMethod,class_addIvar等函数来为新创建的类添加方法、实例变量和属性等。完成这些后,我们需要调用objc_registerClassPair函数来注册类,之后这个新类就可以在程序中使用了。

    实例方法和实例变量应该添加到类自身上,而类方法应该添加到类的元类上。

    // 创建类实例
    id class_createInstance ( Class cls, size_t extraBytes );

    // 在指定位置创建类实例
    id objc_constructInstance ( Class cls, void *bytes );

    // 销毁类实例
    void * objc_destructInstance ( id obj );

    运行时
    运行时在项目中实用的方法不多,我一一列举.

    
    1 对象关联,给UIView分类写一个方法,动态给UIView增加一个点击手势
    - (void)setTapActionWithBlock:(void (^)(void))block
    {
        UITapGestureRecognizer *gesture = objc_getAssociatedObject(self, kDTActionHandlerTapGestureKey);
        
        if (!gesture)
        {
            gesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(__handleActionForTapGesture:)];
            [self addGestureRecognizer:gesture];
            objc_setAssociatedObject(self, kDTActionHandlerTapGestureKey, gesture, OBJC_ASSOCIATION_RETAIN);
        }
        objc_setAssociatedObject(self, kDTActionHandlerTapBlockKey, block, OBJC_ASSOCIATION_COPY);
    }
    
    //这段代码检测了手势识别的关联对象。如果没有,则创建并建立关联关系。同时,将传入的块对象连接到指定的key上。注意`block`对象的关联内存管理策略。
    //手势识别对象需要一个`target`和`action`,所以接下来我们定义处理方法:
    //objc
    - (void)__handleActionForTapGesture:(UITapGestureRecognizer *)gesture
    {
        if (gesture.state == UIGestureRecognizerStateRecognized)
        {
            void(^action)(void) = objc_getAssociatedObject(self, kDTActionHandlerTapBlockKey);
            if (action)
            {
                action();
            }
        }
    }
    
    
    2 方法交换
    /在类被加载到运行时的时候,就会执行,用运行时替换系统的方法.load在编译前执行,别在里面写太多方法,会影响启动速度
    + (void)load {
        
        Method originalMethod = class_getInstanceMethod([self class], @selector(pushViewController:animated:));
        Method swizzledMethod = class_getInstanceMethod([self class], @selector(jd_pushViewController:animated:));
        
        method_exchangeImplementations(originalMethod, swizzledMethod);
    }
    
    3 我用有时候想用系统类时候,想知道系统类有什么东东,可以用下面三个方法,把类的属性方法都打印出来.
    //获取一个类所有属性
                id LenderClass = objc_getClass("UISwipeActionPullView");
                unsigned int outCount, i;
                objc_property_t *properties = class_copyPropertyList(LenderClass, &outCount);
                for (i = 0; i < outCount; i++) {
                    objc_property_t property = properties[i];
                    fprintf(stdout, "%s %s\n", property_getName(property), property_getAttributes(property));
                }
                
    
    //获取一个类所有ivar
     unsigned  int count = 0;
                    Ivar *members = class_copyIvarList(LenderClass, &count);
                      
                    for (int i = 0; i < count; i++)
                    {
                            Ivar var = members[i];
                            const char *memberAddress = ivar_getName(var);
                            const char *memberType = ivar_getTypeEncoding(var);
                            NSLog(@"address = %s ; type = %s",memberAddress,memberType);
                     }
    
    
    
    //获取一个类所有对象方法
                    unsigned int count = 0;
                    Method *memberFuncs = class_copyMethodList(LenderClass, &count);
                for (int i = 0; i < count; i++)
                {
                    SEL address = method_getName(memberFuncs[i]);
                    NSString *methodName = [NSString stringWithCString:sel_getName(address) encoding:NSUTF8StringEncoding];
                    NSLog(@"member method : %@",methodName);
                }
    

    相关文章

      网友评论

          本文标题:ios基础整理

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