ios基础整理

2017-12-20  本文已影响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);
            }
上一篇下一篇

猜你喜欢

热点阅读