iOS Runtime基础

2017-06-13  本文已影响54人  勇往直前888

RuntimeObject-C的一种特性,本人并不感冒。不过这块内容却很流行,也是Object-C动态特性的来源,被认为是比Swift好的地方。
平时用不用是一回事,知道这些基础知识还是有好处的,至少跟人聊的时候能打上话。
动态特性带来了方便,但是同时也带来了很大的安全隐患,在使用的时候尽量谨慎一点。
另外,这是底层的函数,像ARC这种偷懒用的好特性就没有了,要注意内存泄漏问题。(基本上无法避免)

Runtime全方位装逼指南
RuntimeLearn
这篇文章写得比较好,基础概念写得比较清晰,值得优先读

iOS动态性(二)可复用而且高度解耦的用户统计埋点实现
统计埋点确实是一个比较典型的应用,这篇文章写得比较清楚

对象、类、元类

Object-C类图.jpg

消息发送

void objc_msgSend(void /* id self, SEL op, ... */ );
typedef struct objc_selector *SEL;
typedef struct objc_object *id;

// 下面几个都是将字符串转换为函数指针SEL;根据使用场景选择方便的
// 这个c字符串
SEL 变量名 = sel_registerName(const char *str); // 在c的模块中推荐用
// 下面两个是NSString
SEL 变量名 = NSSelectorFromString(NSString *aSelectorName); // 推荐用这个
SEL 变量名 = @selector(NSString *aSelectorName); // 这个用得比较多,不过难理解,不是很推荐
- (void)showSizeWithWidth:(float)aWidth andHeight:(float)aHeight{
    NSLog(@"size is %.2f * %.2f",aWidth, aHeight);
}

TestClass *testObject = [[TestClass alloc] init];
[testObject showSizeWithWidth:110.5f andHeight:200.0f]

也可以用下面的调用方式:

((void (*) (id, SEL, float, float)) objc_msgSend) (testObject, sel_registerName("showSizeWithWidth:andHeight:"), 110.5f, 200.0f);

类的本质

typedef struct objc_class *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;
    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;

这里没有单独的属性列表,给理解带来了困难。如果都是基本类型,可以认为成员变量列表就是属性列表,比如下面的“自动归档”部分处理的那样。
另外一种说法是有单独的属性列表,只是这里没有显示出来。比如“字典转模型”,就使用了属性列表。
class_copyPropertyList和class_copyIvarList的区别

远程调用

FOUNDATION_EXPORT NSString *NSStringFromSelector(SEL aSelector);
FOUNDATION_EXPORT SEL NSSelectorFromString(NSString *aSelectorName);

FOUNDATION_EXPORT NSString *NSStringFromClass(Class aClass);
FOUNDATION_EXPORT Class _Nullable NSClassFromString(NSString *aClassName);
- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;

iOS应用架构谈 组件化方案
CTMediator

蘑菇街 App 的组件化之路
MGJRouter

对象关联

void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);
id objc_getAssociatedObject(id object, const void *key);
void objc_removeAssociatedObjects(id object); // 移除所有关联属性,不要轻易使用

// 属性的修饰符,根据情况设置
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
    OBJC_ASSOCIATION_ASSIGN = 0,           /**< Specifies a weak reference to the associated object. */
    OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /**< Specifies a strong reference to the associated object. 
                                            *   The association is not made atomically. */
    OBJC_ASSOCIATION_COPY_NONATOMIC = 3,   /**< Specifies that the associated object is copied. 
                                            *   The association is not made atomically. */
    OBJC_ASSOCIATION_RETAIN = 01401,       /**< Specifies a strong reference to the associated object.
                                            *   The association is made atomically. */
    OBJC_ASSOCIATION_COPY = 01403          /**< Specifies that the associated object is copied.
                                            *   The association is made atomically. */
};

这方面的资料很多,使用也相对简单,比如下面就有一篇:
iOS-OC-Runtime使用小谈(objc_setAssociatedObject)

自动归档

@protocol NSCoding

- (void)encodeWithCoder:(NSCoder *)aCoder;
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder; // NS_DESIGNATED_INITIALIZER

@end
// 把成员变量当做属性,获取列表
Ivar *class_copyIvarList(Class cls, unsigned int *outCount);
// 获得成员变量的名字,带_前缀;这是c字符串
const char *ivar_getName(Ivar v) ;
// 通过KVC获得成员变量的值
- (nullable id)valueForKey:(NSString *)key;
// 通过KVC设置成员变量的值
- (void)setValue:(nullable id)value forKey:(NSString *)key;

字典与模型互转

// 属性列表,不是成员变量列表
objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount);
// 获取属性的名字
const char *property_getName(objc_property_t property) ;

字典转模型,自动归档等推荐的第三方库为YYModel,实际用过,确实很方便
YYModel

方法动态解析

给一个对象发消息,就是执行objc_msgSend(id, SEL, ...)函数。使用很小心,id、SEL都正确的情况下,当然没问题。但是,如果出错了呢?
是的,崩溃,崩溃信息一般如下:
unrecognized selector sent to instance ...

+ (BOOL)resolveInstanceMethod:(SEL)sel;
- (id)forwardingTargetForSelector:(SEL)aSelector;
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;
- (void)forwardInvocation:(NSInvocation *)anInvocation;
- (void)doesNotRecognizeSelector:(SEL)aSelector;

继承自NSObject的不常用又很有用的函数(2)

方法交换

iOS动态性(二)可复用而且高度解耦的用户统计埋点实现

Objective-C的hook方案(一): Method Swizzling

Method class_getInstanceMethod(Class cls, SEL name);
void method_exchangeImplementations(Method m1, Method m2);

IMP method_getImplementation(Method m);
IMP method_setImplementation(Method m, IMP imp); 
BOOL class_addMethod(Class cls, SEL name, IMP imp, 
                                 const char *types); 

使用method-swizzling让程序更健壮

上一篇 下一篇

猜你喜欢

热点阅读