Swift之深入解析如何使用Xcode和LLDB v2修改UI元素
Posted Forever_wj
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Swift之深入解析如何使用Xcode和LLDB v2修改UI元素相关的知识,希望对你有一定的参考价值。
一、前言
- 在上一篇博客中,已经详细地介绍如何使用 LLDB 表达式修改 UI 元素,具体请参考:Swift之深入解析如何将代码添加为自定义LLDB命令。
- 在这篇博客中,将继续讨论相同的问题需求,并将重点讨论如何最大限度地利用 LLDB 表达式,如果工具太过复杂,那么它就无法工作并获得用户的采用,出于这个原因,本文将分享一些使 LLDB 表达式更容易使用的替代方法。
二、命令别名
- 首先,必须输入(或复制)这个长度的命令(例如 po [[[UIApplication sharedApplication] keyWindow] recursiveDescription])可能会阻碍开发者使用这些 LLDB 命令。幸运的是,这个问题有解决方案,它被称为命令别名,惟一需要做的就是编辑位于 ~/.lldbinit 目录中的文件,Lldbinit(或者通过键入 touch ~/.lldbinit 创建它),并添加如下命令:
command alias views expression -l objc -O -- [[[UIApplication sharedApplication] keyWindow] recursiveDescription]
command alias flush expression -l objc -- (void)[CATransaction flush]
command regex change_color 's/(.+) (.+)/e (void)[(id)%1 setBackgroundColor:[UIColor %2]]/'
- 前两个命令在Swift之深入解析如何将代码添加为自定义LLDB命令已经介绍使用过,第一个是打印视图层次结构,获取按钮的内存地址,第二个是刷新 UI。
- 最后一个命令演示了如何使用 regex 创建带参数的命令,在这个特定的情况下,需要一个内存地址和颜色的名称(blueColor),结果按钮的背景颜色将会改变。例如,运行 change_color 0x7f9f7e40cd70 blueColor 并刷新将导致将按钮的背景颜色更改为蓝色。
- 使用别名将提高使用此工具的可能性,但在某些情况下需要更高级的别名。比如说,如何通过按钮标签的文本找到按钮的内存地址。在这种情况下,Python 可以提供可扩展性。
三、LLDB 与 Python
- 现在开始编写 Python 命令的一个简单方法是使用 Xcode 控制台,输入命令脚本添加帮助并遵循说明:
- 或者,可以创建一个脚本文件,它可以添加到回收和版本控制系统中,然后在 Xcode 控制台上运行命令 command script import <script_file_path>,或者更好的方法是将它添加到 ~/.lldbinit 文件中。
- 要写一个 Python 函数用作新的 LLDB 命令,需要实现一个带有四个参数的函数:
def command_function(debugger, command, result, internal_dict):
# Your code goes here
- 根据 LLDB python 参考,这些变量的类型和描述如下:
-
- debugger (类型:lldb.SBDebugger):当前调试器对象;
-
- command (类型:python string):一个包含命令所有参数的 Python 字符串,如果需要分割参数,请尝试使用 shlex 模块的 shlex.split(命令)来正确地提取参数;
-
- result (类型: lldb.SBCommandReturnObject):一个返回对象,它封装了命令的成功/失败信息,以及作为命令结果需要打印的输出文本,普通的 Python“print” 命令也可以工作,但默认情况下文本不会进入结果(它作为临时日志记录工具很有用);
-
- internal_dict (类型:python dict object): 当前嵌入脚本会话的字典,其中包含所有变量和函数。
- 如果使用命令脚本导入方法,可以像下面这样定义 __lldb_init_module 函数:
def __lldb_init_module(debugger, internal_dict):
# Command Initialization code goes here
debugger.HandleCommand('command script add -f filter.filter_button_by_label filter_button_by_label')
- 其中调试器和 internal_dict 如上所述,当加载模块时,这个函数将被调用,允许在当前调试器中添加任何想要的命令。debugger.HandleCommand 的参数描述如下:
-
- command script add:使用 LLDB 命令添加脚本;
-
- -f argument:指定命令将执行的 Python 函数的名称,它遵循的格式:module name.function name;如同上面的例子中:
-
-
- filter:是模块的名称(在 Python 中只是不带 .py 扩展名的文件名);
-
-
-
- filter_button_by_label:命令功能,描述同 command_function(debugger, command, result, internal_dict)) 一致;
-
-
- argument(例如 filter_button_by_label):是 Xcode 控制台中用来调用此函数的命令。
- 如下所示,展示了一个如何从按钮标签的文本中获取按钮内存地址的示例:
import lldb
import commands
import optparse
import shlex
import re
def create_options():
usage = "usage: filter_button_by_label [options]"
description='''
This command is used to find a UIButton with a label matching the option provided as option
'''
parser = optparse.OptionParser(description=description, prog='filter_button_by_label', usage=usage)
parser.add_option('-n', '--needle', type='string', dest='needle', help='Text to search on UIButton labels.')
return parser
def filter_button_by_label(debugger, command, result, internal_dict):
target = debugger.GetSelectedTarget()
process = target.GetProcess()
mainThread = process.GetThreadAtIndex(0)
currentFrame = mainThread.GetSelectedFrame()
# Parse arguments and options
command_args = shlex.split(command)
parser = create_options()
try:
(options, args) = parser.parse_args(command_args)
# if needle is not provided
if not options.needle:
parser.print_help()
return
except:
return
view_hierarchy_command = '(id)[[[UIApplication sharedApplication] keyWindow] recursiveDescription]'
view_hierarchy = currentFrame.EvaluateExpression(view_hierarchy_command).GetObjectDescription()
for match in re.finditer('.*<UIButton: (0x[0-9a-fA-F]*);.*', view_hierarchy, re.IGNORECASE):
view = match.groups()[-1]
created_command = '(NSString *)[ (id)' + view + ' currentTitle]'
title = currentFrame.EvaluateExpression(created_command).GetObjectDescription()
if title == options.needle:
print >>result, view
else:
print >>result, "Not Found"
def __lldb_init_module(debugger, internal_dict):
debugger.HandleCommand('command script add -f ' + __name__ + '.filter_button_by_label filter_button_by_label')
- 它可以像 filter_button_by_label -n “Press me” 这样调用,它会返回那个按钮的内存地址。因此改变按钮颜色的整个流程就变成:
- 与第一个实现相比,代码更短,更容易记忆,因此更容易使用。
四、Chisel
以上是关于Swift之深入解析如何使用Xcode和LLDB v2修改UI元素的主要内容,如果未能解决你的问题,请参考以下文章
Swift之深入解析Xcode13对Swift对象生命周期的优化
iOS之深入解析Xcode 13(iOS 15)正式版发布的新特性