一份走心的runloop源码分析

前言

对iOS开发者而言,runloop是一个老生常谈的话题,但凡是iOS开发者,在工作中必然直接或间接的接触过runloop。而对于面试者而言,runloop又几乎是必考点。在几年前,笔者写过一篇文章NSRunLoop,对runloop原理以及应用场景做了基本介绍。但是当时也是道听途说,简单的翻看了源码的do…while循环,并没有深入源码。所以,本文将从源码的角度剖析runloop的组成,强化自己对runloop的认识,验证我们脑海中一直以来似懂非懂的原理,真心希望这篇文章能够帮助到大家。

注意:为了减少篇幅、避免困惑,本篇文章贴出的源码稍有精简,比如去除了lock和windows的代码。

为什么是runLoop

runloop顾名思义就是”跑圈“,所谓跑圈就给人一种循环的感觉。runloop运行的核心代码就是一个有状态的do…while循环。每循环一次就相当于跑了一圈,线程就会对当前这一圈里面产生的事件进行处理。那么为什么线程要有runloop呢?其实我们的APP可以理解为是靠event驱动的(包括iOS和Android应用)。我们触摸屏幕、网络回调等都是一个个的event,也就是事件。这些事件产生之后会分发给我们的APP,APP接收到事件之后分发给对应的线程。通常情况下,如果线程没有runloop,那么一个线程一次只能执行一个任务,执行完成后线程就会退出。要想APP的线程一直能够处理事件或者等待事件(比如异步事件),就要保活线程,也就是不能让线程早早的退出,此时runloop就派上用场了。我们已经说了,runloop本质上就是一个有状态的do…while循环,所以只要不是超时或者故意退出状态,那么runLoop就会一直执行do…while,所以可以保证线程不退出。其实也不是必须要给线程指定一个runloop,如果需要我们线程能够持续的处理事件,那么就需要给线程绑定一个runloop。也就是说,runloop能够保证线程一直可以一直处理事件。所以runloop的作用可以理解为:

  • 使程序一直运行并处理各种事件。这些事件包括但不限于用户操作、定时器任务、内核消息
  • 有顺序的处理各种Event。因为runLoop有状态,可以决定线程在什么时候处理什么事件
  • 节省CPU资源。通常情况下,事件并不是永无休止的产生,所以也就没必要让线程永无休止的运行。runloop可以在无事件处理时进入休眠状态,避免无休止的do…while跑空圈。

不得不重复那句老生常谈的话:一个线程对应一个RunLoop,程序运行是主线程的RunLoop默认启动,子线程的RunLoop按需启动(调用run方法)。runloop是线程的事件管理者,或者说是线程的事件管家,他会按照顺序管理线程要处理的事件,决定哪些事件在什么时候提交给主线程处理。

阅读更多

详解ReactNative渲染原理

前言

《详解React Native初始化和通信机制》中我们详细的介绍了React Native的初始化和通信机制。如果对通信机制不了的的读者可以先去阅读通信机制。

React Native 本质上是以 React 为框架,笔者的理解是React Native通过JS(React)实现业务逻辑;通过Native实现视图。所以最终开发出来的页面视图是是纯Native组件。本文会通过源码分析的方式剖析React Native中视图的创建、更新、渲染原理。

JSX

JSX是一个 JavaScript 的语法扩展,可以简单理解为 JavaScript + XML 的语法糖。React虽然不强制要求使用JSX,但官方建议使用,因为JSX可以很好地描述 UI 应该呈现出它应有交互的本质形式。JSX 可能会使人联想到模版语言,但它具有 JavaScript 的全部功能。如下就是一个JSX语句:

1
const element = <h1>Hello, world!</h1>;

由于JSX是一种语法糖,所以在bundle打包过程中,以上的JSX语法会被Babel转换成普通JS语句,如下:

1
const element = React.createElement("h1", null, "Hello, world!");

可以通过babel compiler体验在线JSX转换。

阅读更多

详解RN中nativemodule暴露原理

我们知道,RN可以调用Native侧的方法。并且RN框架也给我们提供了这一能力,只要我们按照某些约定在native侧实现一个方法,那么就可以在JS侧顺利调用。如下实现了一个简单的native模块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>

@interface NativeLogModule : NSObject<RCTBridgeModule>

@end

#import "NativeLogBridge.h"

@implementation NativeLogModule
RCT_EXPORT_MODULE()
RCT_EXPORT_METHOD(nativeLog:(id)obj) {
NSLog(@"开始输出日志:");
NSLog(@"%@",obj);
NSLog(@"日志输出完毕!");
}
@end

阅读更多

详解ReactNative初始化和通信机制

致敬

开始准备写这一篇文章的时候,中国的新型冠状病毒肺炎疫情还在继续,累积确诊81058人,现存确诊10827人。这篇文章写完的时候,累计确诊81501人,现存确认5846人。疫情已经持续了3个月,但也终将过去。毫无疑问,在这漫长的3个月的时间里,很多与疫情抗争的工作人员都是非常辛苦的,再次感谢&致敬。

前言

这是一篇原理性文章,也是一篇源码分析文章。这篇文章是笔者学习RN源码过程中的一篇记录文章,主要记录了程序从启动之初到开始执行JS源码的整个流程。从AppDelegate的application:didFinishLaunchingWithOptions:说起,全流程涉及到关键类的初始化工作和JavaScript的执行以及JS&Native之间的通信。围绕bridge的初始化、JS源码的加载、JS源码的执行、Native调用JS、JS调用Native展开分析。内容虽然很长,但其实很浅,大部分都是源码,并没有加入自己太多的思考,耐心看完就可以理解。

本文篇幅很长的原因是笔者贴出了大量的RN源码。文章中的源码已经做了精简,如果想看完整的代码还是建议参考RN源码。笔者主要删除了源码中与逻辑无强关联的代码。比如debug环境的宏、锁、调试相关的代码、健壮性相关的代码、错误处理相关的代码、代码执行耗时相关的代码。删除这些代码不会影响对源码的阅读和理解,请大家放心。

阅读这篇文章你最好具备以下条件:你应该是一个iOS开发者,本文是站在一个iOS工程的角度分析RN的源码,当然如果你能看懂Objective-C代码也是可以的。你应该对RN有所了解,最好是使用RN开发过一些需求。你应该对JS有所了解,本文会涉及少量JS代码。最后,你最好具备一些C++的知识,RN源码中存在大量的C++代码,不需要会写,了解C++语法能看懂C++代码即可。当然,如果你认为万物皆对象,以上条件都可以忽略,那么让我们开始吧》》》

本文基于React Native 0.61.5进行分析

阅读更多

WSRouter—一款轻量级路由跳转框架

简介

大约6个月前,笔者写了一个基于url-block的路由跳转框架,命名为WSRouter。这是一个轻量级框架,仅包含4个.m文件共400多行代码。虽然这个框架很轻,但并不代表其功能不完备,相反,这个框架的功能完全可以应付我们日常的开发需要。无论是对于项目使用还是学习参考,WSRouter都是一个不错的选择。
目前路由跳转方案有很多,大致分为4中:基于URL(url-controller、url-block)的方案、基于target-action的方案、protocol-class方案。本文所说的WSRouter是基于url-block的跳转方案。本文不对这些方案的优缺点进行对比,仅对WSRouter的基本功能和实现做一些简介,欢迎大家使用或star。

阅读更多

iOS页面秒开优化-优化篇

前言

页面跳转优化包括资源预加载、自定义push转场、loading动画过渡、骨架屏占位、网络请求前置、网络请求后置、异步处理任务、精简生命周期方法、懒加载以及UI布局优化等方向。

优化方向

资源预加载

  • 这里的资源指的是图片资源。对于一些存在轮播图或者banner区的目的页,可以优先加载一张缩略图作为占位;也可以在进入页面之前预加载一张轮播图以备使用。

阅读更多

iOS包大小优化-优化篇

本地图片检查

  • 移除无用图片
  • 移除相似图片
  • 部分大图由后端下发跳链(交互型icon不建议后端下发)
  • 图片压缩(仅限压缩jpg图片)

    Xcode在构建的过程中,有一个步骤叫做compile asset catalog。在这个步骤中,Xcode会自行对png图片作压缩,并且会压缩成能够快速读取渲染的格式。如果我们对png图片进行了压缩,那么Xcode在compile asset catalog阶段还是会对图片进行处理,而这个处理过程有可能导致我们压缩过的png图片反而变大。

代码文件检查

  • 移除无用文件和模块
  • 下线历史代码
  • 强关联性文件合并
  • 下线低PV页面
  • 合并相关的工具类、工具方法
  • 无用第三方库清理以及精简三方库
  • 部分非主流程页面改为动态页面,比如RN、webview