CodingTour
ARTS #74 | 终于可以轻松管理 macOS 构建机了

Algorithm

本周选择的算法题是:Insert Interval

规则如下:

Given a set of non-overlapping intervals, insert a new interval into the intervals (merge if necessary).

You may assume that the intervals were initially sorted according to their start times.

Example 1:

Input: intervals = [[1,3],[6,9]], newInterval = [2,5]
Output: [[1,5],[6,9]]

Example 2:

Input: intervals = [[1,2],[3,5],[6,7],[8,10],[12,16]], newInterval = [4,8]
Output: [[1,2],[3,10],[12,16]]
Explanation: Because the new interval [4,8] overlaps with [3,5],[6,7],[8,10].

Example 3:

Input: intervals = [], newInterval = [5,7]
Output: [[5,7]]

Example 4:

Input: intervals = [[1,5]], newInterval = [2,3]
Output: [[1,5]]

Example 5:

Input: intervals = [[1,5]], newInterval = [2,7]
Output: [[1,7]]

Constraints:

  • 0 <= intervals.length <= 104
  • intervals[i].length == 2
  • 0 <= intervals[i][0] <= intervals[i][1] <= 105
  • intervals is sorted by intervals[i][0] in ascending order.
  • newInterval.length == 2
  • 0 <= newInterval[0] <= newInterval[1] <= 105

Solution

class Solution:
    def insert(self, intervals: List[List[int]], newInterval: List[int]) -> List[List[int]]:
        ans, index, n = [], 0, len(intervals)
        while index < n and intervals[index][1] < newInterval[0]:
            ans.append(intervals[index])
            index += 1
        
        ans.append(newInterval)

        while index < n:
            if intervals[index][0] <= ans[-1][1]:
                ans[-1][0] = min(ans[-1][0], intervals[index][0])
                ans[-1][1] = max(ans[-1][1], intervals[index][1])
            else:
                ans.append(intervals[index])
            index += 1
        return ans

Review

The Inside Story on New-Style Classes

Python 语言的设计者 Guido van Rossum 早些年在 2.x 上的一些思考。

我感兴趣的主要有其中三部分:

  • descriptor - 一个基于 POP 语言的 OOP 设计实现,Objective-C 里也有一套类似的机制
  • slots - 与传统 __dict__ 相比,slots 在性能和内存使用率上有更多的优势,其基于数组,支持内存随机访问。而且由于 slots 可以和 __dict__ 在同一个类中共存,所以可以认为是一种优化
  • MRO - Python 的多重继承实现

Tip

在 lldb 中可以直接通过锁自身查看是哪个线程获取了该锁。

NSLock 为例,假设它的内存地址为 0x00006000002c79c0

(lldb) x/16 0x00006000002c79c0
0x6000002c79c0: 0x86cf8b50 0x00007fff 0x00000000 0x00000000
0x6000002c79d0: 0x4d55545a 0x00000000 0x00000000 0x000020a0
0x6000002c79e0: 0x00000000 0x4d55545a 0x00068c91 0x00000000
0x6000002c79f0: 0x00000002 0x00000000 0xffffffff 0xffffffff

查看它偏移量为 0x28 的位置:

(lldb) p (char*)0x6000002c79c0 + 0x28
(char *) $8 = 0x00006000002c79e8 "\xffffff91\xffffff8c\x06"

0x28 = 0x10 + 0x18

将它的值转为十进制整型:

(lldb) p *((int *)0x00006000002c79e8)
(int) $9 = 429201

查看线程列表:

(lldb) thread list

* thread #1: tid = 0x68c91, 0x000000010670717a VideoStudio`-[PVResourceDemoVC viewDidLoad](self=0x00007fb5f4734f00, _cmd="viewDidLoad") at PVResourceDemoVC.m:49:28, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
  thread #2: tid = 0x68d44, 0x00007fff5dca14ce libsystem_kernel.dylib`__workq_kernreturn + 10
  thread #3: tid = 0x68d49, 0x00007fff2018f728 libobjc.A.dylib`objc_release + 8
  thread #5: tid = 0x68d88, 0x00007fff5dc9fdfa libsystem_kernel.dylib`mach_msg_trap + 10, name = 'com.apple.uikit.eventfetch-thread'
  thread #7: tid = 0x68d8a, 0x00007fff5dca14ce libsystem_kernel.dylib`__workq_kernreturn + 10

thread #1 的 tid 为 0x68c91,正好是十进制 429201

(lldb) p 0x68c91
(int) $10 = 429201

表示 lock 当前是由 thread #1 占有。

Share

CI

早前为了解决 Android 构建机器环境部署困难的问题,我们引入了 Docker,因为构建环境都是基于 Linux 的,所以将环境整体打包成 Docker 镜像可以比较完美的解决我们的问题。

但是 iOS 的构建必须基于 macOS 完成,而 macOS 不能在 Linux 环境内通过 Docker 镜像运行(就算能运行,各种驱动不兼容也会造成性能低下、维护困难等问题)。

不过好在 macOS 提供了自己的虚拟化框架 Hypervisor.framework,市面上也有基于它的商业化解决方案:

  • Docker Desktop
  • VMWare Fusion

考虑到 VMWare Fusion 底层也是基于 Hypervisor.framework,我们就以此开发了基于 macOS 的虚拟化构建方案:

  • 制作一个 macOS 的虚拟机环境
  • 配置环境和必要的缓存
  • 自动登录
  • launchd 启动后自动执行脚本

最终它也可以像 Docker 那样,在终端通过 vmrun 完成对“容器“的操作,如克隆、启动和结束等:

# 启动
vmrun start path/to/vmwarevm nogui

# 结束
vmrun stop /path/to/vmx 

# 克隆
vmrun clone /path/to/source/vmx /path/to/target/vmx full -cloneName=NewName