#iOS Runtime 从初识到实践

简介: xcode.png** 本文适用于没有揭开runtime神秘面纱的读者参考, 大神看到这里的话还希望向小弟甩点墨水, 增长学识! **Runtime简介:它是个运行时的库,基本是C和汇编写的。
img_a49d08176a75f3ea0d0ab2a8d10fad18.png
xcode.png

** 本文适用于没有揭开runtime神秘面纱的读者参考, 大神看到这里的话还希望向小弟甩点墨水, 增长学识! **

Runtime简介:


它是个运行时的库,基本是C和汇编写的。可以把一些工作从编译推迟到运行时处理, 也就是说是运行时系统来执行编译后的代码. 因为其动态性, 所以我们可以在程序运行的时候为类添加,修改,匹配方法等.

初识Runtime:


我们所创建的对象或者说类在runtime中是结构体的形式存在的, 进入runtime的头文件即可获悉

#if !OBJC_TYPES_DEFINED

/// An opaque type that represents a method in a class definition.
typedef struct objc_method *Method; // 描述一个方法

/// An opaque type that represents an instance variable.
typedef struct objc_ivar *Ivar; // 实例变量

/// An opaque type that represents a category.
typedef struct objc_category *Category; // 分类

/// An opaque type that represents an Objective-C declared property.
typedef struct objc_property *objc_property_t; // 类中属性

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;

相信看到这如果我问你category为什么不能添加属性?(不能在.h中写成员变量,却可以写属性,但调用的时候会crash) 你应该可以知道了吧! 但可以通过其它方式间接实现,详情一会会写到**

Runtime作用


1. 发送消息

方法的调用即是让对象发送消息, 运行时会转换成objc_msgSend, 使用消息机制须#import <objc/message.h>
消息机制原理:对象根据方法编号SEL去映射表查找对应的方法实现

Cat *cat = [Cat new];
// 直接调用
[self eatFish];

// 运行时会转成
objc_msgSend(cat, @selector(eatFish));

2. 动态添加方法

当我们调用[cat performSelector:@selector(play)];的时候程序会carsh, 因为没有实现play方法, 怎么办呢? 可以通过下面的方式解决



@implementation Cat

void c_play(id self,SEL sel) // 如果不写括号里的self和_cmd会隐式添加 self为方法调用者 _cmd为方法编号
{
  NSLog(@"自己玩");
}
 
//消息接收者没有找到对应的方法时候,会先调用此方法,我们拦截没实现的方法
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
 
  if (sel == @selector(play)) {
     //这里我们添加play方法
    // 参数1:哪个类添加方法 2:方法编号 3:方法实现的函数地址 4:函数类型
    //函数类型解释:v代表没有返回值void, @:代表对象, :代表SEL
    class_addMethod(self, @selector(play), c_play, "v@:");
 
  }
 
  return [super resolveInstanceMethod:sel];
}
@end

这样调用[cat performSelector:@selector(play)]程序跑起来的时候就不会crash了, 因为我们已经动态添加了;若添加类方法为resolveClassMethod, 同理的.

3. 方法的交换

**若有这样一个需求, 觉得系统的功能不太受用, 在无法改变系统方法的前提下, 为这个方法添加一些功能, 比如即使数组中插入nil时也不让其crash, 用imageNamed:这个方法时候知道图片到底加载成功了没 **

你有可能想到的是继承或者重写该方法(但分类中无法调用super,重写会覆盖之前功能), 下面我们用runtime试试看

@implementation UIImage (Image)
// 分类在内存中的时候很早就被调用的一个方法,一般都在它中实现方法的交换
+ (void)load
{
 // 获取类方法imageNamed:
  Method m1 = class_getClassMethod([UIImage class], @selector(imageNamed:));
 // // 获取类方法rt_imageNamed:
  Method m2 = class_getClassMethod([UIImage class], @selector(rt_imageNamed:));
 
  // 交换彼此方法的实现
  method_exchangeImplementations(m1, m2);
}
 
// 需要交换的方法实现
+ (UIImage *)rt_imageNamed:(NSString *)imageName
{
  // 可能有读者会问,这不是死循环了吗, no, 我们在load里已经交换了方法实现了哦, 所以看似调用自己而已
  UIImage *image = [UIImage rt_imageNamed:imageName];
  //功能
  if (image == nil) {
    // do something
  }
 
  return image;
}

@end

4. 添加属性

我们之前说过如何变相的给category添加属性, 答案是objc_getAssociatedObject, 也是大家常用的一个函数, 即关联.
值得注意的是关联对象不是为类\对象添加属性或者成员变量(因为在设置关联后也无法通过ivarList或者propertyList取得, 至于后面这两个函数我们稍后实践)
使用场景: 例如给NSArray添加一个name属性,我们这里不用继承实现


@interface NSArray (TestAssociated)

@property (nonatomic, copy)NSString *name;
@end
#import "NSArray+TestAssociated.h"

@implementation NSArray (TestAssociated)

static char associatedKey;

- (void)setName:(NSString *)name{

//参数1: 给谁添加关联 2: 关联的key(通过key获取关联的对象) 3: 被关联的对象 4: 关联策略(点进入看一下就知道了)
    objc_setAssociatedObject(self, &associatedKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

- (NSString *)name{

// 同上
    return objc_getAssociatedObject(self, &associatedKey);
}
@end

这样我们就不仅能用tag值了, 使用就这样

NSArray *arr = @[];
    arr.name = @"111";
    
    NSLog(@"%@", arr.name);//打印的值为111

5. 打印对象的属性等

unsigned int i = 0;
    // 打印当前对象的属性
    objc_property_t *pros = class_copyPropertyList([self class], &i);
    
    for (int j = 0; j < i; j++) {
        
        NSString *name = @(property_getName(pros[j]));
        NSLog(@":%@", name);
           
    }

// 同理我们可以打印更多的东西从头文件可知
objc_ivar_list //成员变量列表
objc_method_list //方法列表
...

如果你看到了这里, 你现在一定恍然大悟, 原来那些大神们写的json转model就是这么实现的啊!

使用总结: 对对象进行操作的方法一般以object_开头

对类进行操作的方法一般以class_开头

对类或对象的方法进行操作的方法一般以method_开头

对成员变量进行操作的方法一般以ivar_开头

对属性进行操作的方法一般以property_开头开头

对协议进行操作的方法一般以protocol_开头


总结:关于runtime的使用还有更多更多, 我只介绍了冰山一角,或者说助你看清了轮廓. 实际做项目中我们很少时候用到它, 但实现强大的功能时,往往却离不开它, 例如: json转model功能, 调用私有函数并且绕过苹果审核等!

相关文章
|
缓存 编解码 测试技术
基于iOS平台的高效图片处理技术实践
【4月更文挑战第7天】 在移动应用开发领域,图片处理是一个常见且要求高性能的功能模块。特别是在iOS平台上,由于其封闭的生态系统和用户对流畅体验的高期待,开发者需采用高效的图片处理技术以满足应用的性能需求。本文将探讨一种针对iOS平台优化的图片处理流程,涉及图像加载、缓存策略、异步处理以及图形渲染等关键技术点,旨在为iOS应用提供一个低内存消耗、高效率的图片处理解决方案。
|
API iOS开发
iOS面试关于runtime
iOS面试关于runtime
203 0
|
10月前
|
安全 Swift iOS开发
Swift 与 UIKit 在 iOS 应用界面开发中的关键技术和实践方法
本文深入探讨了 Swift 与 UIKit 在 iOS 应用界面开发中的关键技术和实践方法。Swift 以其简洁、高效和类型安全的特点,结合 UIKit 丰富的组件和功能,为开发者提供了强大的工具。文章从 Swift 的语法优势、类型安全、编程模型以及与 UIKit 的集成,到 UIKit 的主要组件和功能,再到构建界面的实践技巧和实际案例分析,全面介绍了如何利用这些技术创建高质量的用户界面。
225 2
|
12月前
|
安全 Android开发 数据安全/隐私保护
探索安卓与iOS的安全性差异:技术深度分析与实践建议
本文旨在深入探讨并比较Android和iOS两大移动操作系统在安全性方面的不同之处。通过详细的技术分析,揭示两者在架构设计、权限管理、应用生态及更新机制等方面的安全特性。同时,针对这些差异提出针对性的实践建议,旨在为开发者和用户提供增强移动设备安全性的参考。
515 3
|
移动开发 开发工具 Android开发
探索安卓与iOS开发的差异:平台特性与编程实践
【7月更文挑战第8天】在移动开发的广阔天地中,安卓和iOS这两大操作系统各自占据着半壁江山。它们在用户界面设计、系统架构及开发工具上展现出截然不同的特色。本文将深入探讨这两个平台在技术实现和开发生态上的关键差异,并分享一些实用的开发技巧,旨在为跨平台开发者提供有价值的见解和建议。
159 2
|
开发工具 数据安全/隐私保护 iOS开发
探索iOS应用开发的核心理念与实践
【8月更文挑战第23天】在数字时代的浪潮中,iOS应用开发不仅仅是技术的堆砌,更是一场关于创新、用户体验和持续改进的旅程。本文将深入探讨iOS应用开发的核心理念,从设计哲学到开发工具的选择,再到市场趋势的适应,旨在为开发者提供一条清晰的路径,帮助他们在不断变化的技术世界中保持竞争力和创新力。
|
安全 IDE Android开发
探索Android与iOS开发的差异:平台特性与编程实践
【6月更文挑战第17天】在移动应用开发的广阔天地中,Android和iOS两大平台各自占据半壁江山。它们在用户群体、系统架构以及开发环境上的差异,为开发者带来了不同的挑战和机遇。本文深入探讨了这两个平台在技术实现、界面设计、性能优化等方面的主要区别,并提供了实用的开发建议,旨在帮助开发者更好地理解各自平台的特性,从而创造出更加优秀的移动应用。
|
iOS开发 开发者 UED
iOS 中的并发编程:GCD 与 Operation 的对比与实践
【4月更文挑战第23天】 在iOS开发中,为了提高应用的性能和响应能力,理解并合理运用并发编程是至关重要的。本文将深入探讨两种主要的并发模式:Grand Central Dispatch (GCD) 和 NSOperation。我们将比较它们的优势和局限性,并通过代码示例演示如何在实际场景中应用这两种技术来优化应用性能。文章旨在为开发者提供一个清晰的指南,以便在选择适合自己项目的并发工具时做出明智的决策。
|
安全 开发工具 Swift
ios-class-guard - iOS代码混淆与加固实践
ios-class-guard - iOS代码混淆与加固实践
190 0
|
安全 开发工具 Swift
 ios-class-guard - iOS代码混淆与加固实践
然后在作者的回答中也有提到,PreEmptive Protection for iOS - Rename (or PPiOS-Rename),使用相同的原理,但是它将使用 iPhoneSimulator 支持 iPhoneOS SDK,但是经测试,该工具只能用于纯 OC 项目,并不支持 OC 与 Swift 混编的项目