Runtime实践

来源:互联网 发布:淘宝店运营计划书 编辑:程序博客网 时间:2024/04/30 12:07

基础

这里写图片描述

总结:1.对类操作:class_
2. 对对象操作:objc_,ojbect_
3. 对属性操作:property_
4. 对变量操作:property_
5. 对变量操作:ivar_
6. 对方法操作:method_ (SEL+IMP)
7. 对方法操作: imp_
8. 对协议操作: protocol_

继承体系

这里写图片描述

//实例方法(减号方法): 实例对象instance->类class->方法method(->SEL->IMP)->实现函数
//静态方法(加号方法): 类对象->元类class->方法method(->SEL->IMP)->实现函数

struct objc_class {    Class isa  OBJC_ISA_AVAILABILITY; };
struct objc_object {    Class isa  OBJC_ISA_AVAILABILITY;};

## Method Swizzling,面向切面编程(另一种实现方式:继承)
- Method Swizzling 两种实现方式

  • 方法1 - method_exchangeImplementations
/** *  要先尝试添加原 selector 是为了做一层保护,因为如果这个类没有实现 originalSelector ,但其父类实现了,那 class_getInstanceMethod 会返回父类的方法。这样 method_exchangeImplementations 替换的是父类的那个方法,这当然不是你想要的 * */void swizzleMethod(Class class, SEL originalSelector, SEL swizzledSelector){    // 方法可能在当前类不存在,在父类存在,这时我们不需要swi    Method originalMethod = class_getInstanceMethod(class, originalSelector);    Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);    // 如果已存在,则会添加失败  为viewdidLoad添加swizzleViewDidLoad的实现    BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));    // 如果没有我们为其添加一个,以避免替换父类的方法    if (didAddMethod) {        class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));    }    else {        method_exchangeImplementations(originalMethod, swizzledMethod);    }}//调用swizzleMethod(self.class, @selector(viewDidLoad), @selector(swizzleViewDidLoad));-(void)swizzleViewDidLoad{//系统调用viewDidLoad(SEL)---找到这里swizzleViewDidLoad(IMP)    [self swizzleViewDidLoad];//这里swizzleViewDidLoad(SEL)---找到viewDidLoad(IMP)    NSLog(@"swizzleViewDidLoad invoked");}
  • 方法2—class_addMethod
void resetOriginMethodIMP(Class clazz, SEL originalSelector){    Method originalMethod=class_getInstanceMethod(clazz,originalSelector);    originViewDidLoad=(void *)method_getImplementation(originalMethod);    //IMP创建方法1    IMP swizzleIMP=imp_implementationWithBlock(^(id self,SEL _cmd){        NSLog(@"swizzleViewDidLoad invoked");        originViewDidLoad(self, _cmd);    });    BOOL didAddMethod = class_addMethod(clazz, @selector(viewDidLoad),swizzleIMP, method_getTypeEncoding(originalMethod));    if (!didAddMethod) {        method_setImplementation(originalMethod, swizzleIMP);    }}//调用resetOriginMethodIMP(self.class,@selector(viewDidLoad));-(void)viewDidLoad{    [super viewDidLoad];    NSLog(@"viewDidLoad invoked");}
  • 小知识
问题 +(void)load +(void)initialize 执行时机 类别中的定义 在类的方法第一次被调时执行 若自身未定义,是否沿用父类的方法? 否 是 类别中的定义 全都执行,但后于类中的方法 覆盖类中的方法,只执行一个

处理iOS中常见的崩溃

  • 数组越界,数组中值为空,字典中值为空
Method Swizzling
  • 不识别的方法
//方案一(Method Resolution)    //动态添加实现+(BOOL)resolveInstanceMethod:(SEL)sel;//方案二(消息转发Fast forwarding)-(id)forwardingTargetForSelector:(SEL)aSelector;//方案三(伪多继承 Normal forwarding)-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;

分类

  • 当系统方法变为自定义方法
- (void)fd_reloadRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation {    if (self.fd_indexPathHeightCache.automaticallyInvalidateEnabled) {        [self.fd_indexPathHeightCache buildCachesAtIndexPathsIfNeeded:indexPaths];        [indexPaths enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) {            [self.fd_indexPathHeightCache enumerateAllOrientationsUsingBlock:^(FDIndexPathHeightsBySection *heightsBySection) {                heightsBySection[indexPath.section][indexPath.row] = @-1;            }];        }];    }    @try {        FDPrimaryCall([self fd_reloadRowsAtIndexPaths:indexPaths withRowAnimation:animation];);    } @catch (NSException *exception) {        NSLog(@"%@",exception);    } @finally {    }}
  • 分类中移除通知和KVO 的两种方法

    • method swizzling

    • 在分类中定义一个帮助类,作为分类的属性

  • 添加属性的简便方法

//定义属性时用__避开大小写#define lt_interface_property( __type, __name)#define lt_implementation_property( __type, __name)
  • 分类中不能直接添加属性,而能直接添加方法的原因

  • 问题:分类中实现代理,weak属性变量

应用

  • KVO
[self.message lt_addObserver:self forKey:NSStringFromSelector(@selector(text)) withBlock:^(id observedObject, NSString *observedKey, id oldValue, id newValue) {        textField.text=newValue;    }];
    //创建这个KVO类  然后额外的空间(通常为 0)    Class kvoClass=objc_allocateClassPair(self.class, kvoClassName.UTF8String, 0);    //借用父类self.class方法的签名 方法的实现是父类的class实现    Method clazzMethod=class_getInstanceMethod(self.class, @selector(class));    IMP kvo_classIMP=class_getMethodImplementation(self.superclass, @selector(class));    const char *classType=method_getTypeEncoding(clazzMethod);    class_addMethod(kvoClass, @selector(class), kvo_classIMP, classType);    //注册    objc_registerClassPair(kvoClass);
 static void kvo_setter(id self,SEL _cmd, id newValue){    //获得父类    struct objc_super superclazz={        .receiver=self,        .super_class=class_getSuperclass(object_getClass(self))    };    //强转,避免编译器警告    void (*objc_msgSendSuperCasted)(void *,SEL,id)=(void *)objc_msgSendSuper;    //调用父类的setterf方法    objc_msgSendSuperCasted(&superclazz,_cmd,newValue);    //遍历观察者,调用block    NSMutableArray *observers=objc_getAssociatedObject(self, (__bridge void *)kLTKVOAssociatedObservers); }

isa-swizzling

 object_setClass(self, clazz);
  • 字典转模型
    NSDictionary *dict = @{                           @"name" : @"Jack",                           @"icon" : @"lufy.png",                           @"age" : @"20",                           @"height" : @1.55,                           @"money" : @"100.9",                           @"sex" : @(0),                           @"gay" : @"1"                           };    MJUserModel *userModel=[MJUserModel learn_objectWithKeyValues:dict];    unsigned int outCount=0;    objc_property_t *properties=class_copyPropertyList(self.class, &outCount);
  • 项目中实践
    • 上拉加载的提示信息
    • 网络层区分两端
    • FDTemplateLayoutCell
    • 归档/单例对象赋值/获取私有属性和私有方法
    • 获取网络状态: 2G-3G-4G ( UIApplication-statusBar-foregroundView-UIStatusBarDataNetworkItemView-dataNetworkType)

乱扯

  • Category的实现
  • GCD
  • RunLoop
  • HTML5
  • c语言
0 0