iOS学习iOSiOS开发那些事

运行时(iOS)

2015-11-12  本文已影响8816人  iOS_成才录

一、什么是运行时(Runtime)?

二、运行时的作用?

三、案例:运行时获取成员变量名称

#import <Foundation/Foundation.h>
#import "XMGPerson.h"
#import <objc/runtime.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // 成员变量的数量
        unsigned int outCount = 0;

        // 获得所有的成员变量
        // ivars是一个指向成员变量的指针
        // ivars默认指向第0个成员变量(最前面)
        Ivar *ivars = class_copyIvarList([XMGPerson class], &outCount);

        // 遍历所有的成员变量
        for (int i = 0; i<outCount; i++) {
            // 取出i位置对应的成员变量
//            Ivar ivar = *(ivars + i);
            Ivar ivar = ivars[i];
            // 获得成员变量的名字
            NSLog(@"%s", ivar_getName(ivar));
        }

        // 如果函数名中包含了copy\new\retain\create等字眼,那么这个函数返回的数据就需要手动释放
        free(ivars);

//        Ivar ivar = *ivars;
//        Ivar ivar2 = *(ivars + 1);
//        NSLog(@"%s %s", ivar_getName(ivar), ivar_getName(ivar2));


        // 一个Ivar就代表一个成员变量

        // int *p; 指向int类型的变量
        // Ivar *ivars; 指向Ivar类型的变量
    }
    return 0;
}
Snip20151027_1.png
 // 成员变量的数量
    unsigned int outCount = 0;

    // 获得所有的成员变量
    Ivar *ivars = class_copyIvarList([UITextField class], &outCount);

    // 遍历所有的成员变量
    for (int i = 0; i<outCount; i++) {
        // 取出i位置对应的成员变量
        Ivar ivar = ivars[i];
        // 获得成员变量的名字
        NSLog(@"%s", ivar_getName(ivar));
    }

    // 如果函数名中包含了copy\new\retain\create等字眼,那么这个函数返回的数据就需要手动释放
    free(ivars);

四、iOS底层

1、The Runtime 简单介绍

Messages

[array insertObject:foo atIndex:5];
objc_msgSend(array, @selector(insertObject:atIndex:), foo, 5);

Objects, Classes, MetaClasses

typedef struct objc_object {
    Class isa;
} *id;

Methods, Selectors and IMPs

- (id)doSomethingWithInt:(int)aInt{}

id doSomethingWithInt(id self, SEL _cmd, int aInt){}

运行时到底能做什么呢?

class

class_getClassMethod, class_getClassVariable, class_getInstanceMethod, class_getInstanceVariable, class_getMethodImplementation和class_getProperty

- 一些通用的自省方法
```objc
class_conformsToProtocol, class_respondsToSelector, class_getSuperclass
class_createInstance来创建一个object

ivar

method

method_getName, method_getImplementation, method_getReturnType等等
method_setImplementation和method_exchangeImplementations

objc

property

protocol

sel

2、运行时能干什么?(举例)

2.1 Classes And Selectors From Strings

Class stringclass = NSClassFromString(@"NSString")
NSString *myString = [stringclass stringWithString:@"Hello World"];
- (void)parseObject:(id)object {
    for (id data in object) {
        if ([[data type] isEqualToString:@"String"]) {
            [self parseString:[data value]];
        } else if ([[data type] isEqualToString:@"Number"]) {
            [self parseNumber:[data value]];
        } else if ([[data type] isEqualToString:@"Array"]) {
            [self parseArray:[data value]];
        }
    }
}
- (void)parseObjectDynamic:(id)object {
    for (id data in object) {
        [self performSelector:NSSelectorFromString([NSString stringWithFormat:@"parse%@:", [data type]]) withObject:[data value]];
    }
}
- (void)parseString:(NSString *)aString {}
- (void)parseNumber:(NSString *)aNumber {}
- (void)parseArray:(NSString *)aArray {}

2.2 Method Swizzling

#import  <objc/runtime.h>

@interface NSMutableArray (LoggingAddObject)
- (void)logAddObject:(id)aObject;
@end

@implementation NSMutableArray (LoggingAddObject)

+ (void)load {
    Method addobject = class_getInstanceMethod(self, @selector(addObject:));
    Method logAddobject = class_getInstanceMethod(self, @selector(logAddObject:));
    method_exchangeImplementations(addObject, logAddObject);

}

- (void)logAddObject:(id)aobject {
    [self logAddObject:aObject];
    NSLog(@"Added object %@ to array %@", aObject, self);
}

@end

动态继承、交换

object_setClass(myObject, [MySubclass class]);```

+ 这可以用在Key Value Observing。当你开始observing an object时,Cocoa会创建这个object的class的subclass,然后将这个object的isa指向新创建的subclass。


#### 动态方法处理
+ 目前为止,我们讨论了方法交换,以及已有方法的处理。那么当你发送了一个object无法处理的消息时会发生什么呢?很明显,"it breaks"。大多数情况下确实如此,但Cocoa和runtime也提供了一些应对方法。

+ 首先是动态方法处理。通常来说,处理一个方法,运行时寻找匹配的selector然后执行之。有时,你只想在运行时才创建某个方法,比如有些信息只有在运行时才能得到。要实现这个效果,你需要重写+resolveInstanceMethod: 和/或 +resolveClassMethod:。如果确实增加了一个方法,记得返回YES。

```objc
+ (BOOL)resolveInstanceMethod:(SEL)aSelector {
    if (aSelector == @selector(myDynamicMethod)) {
        class_addMethod(self, aSelector, (IMP)myDynamicIMP, "v@:");
        return YES;
    }
    return [super resolveInstanceMethod:aSelector];
}

消息转发

使用Block作为Method IMP

IMP myIMP = imp_implementationWithBlock(^(id _self, NSString *string) {
    NSLog(@"Hello %@", string);
});
class_addMethod([MYclass class], @selector(sayHello:), myIMP, "v@:@");
上一篇下一篇

猜你喜欢

热点阅读