{% github it-boyer chisel f331dc6 width = 30% %} 这个pull请求添加findinstances命令,该命令完成Add findinstances, and new support framework in Chisel.xcodeproj Add Makefile for installing framework的工作。 用户可以运行help findinstances获取findinstances的详细信息。简要总结一下,findinstances可以找到给定class类或protocol协议的所有实例,并使用谓词表达式过滤这些结果。 如果您有一个名为XXSocialUser的类,那么您可以通过运行findinstances XXSocialUser == 'curry'来找到一个特定的用户。

Chisel.xcodeproj为新建findinstance提供了凿子框架支持。 使用Chisel.xcodeproj支持新建chisel Framework通过本地代码实现command,findinstances等功能。也可以通过这种方式来实现更多chisel命令

findinstance命令通过扫描iOS/macOSmalloc API。对于每个allocation分配,都会使用heuristics来识别可能的Objective-C实例heuristics不调用对象上的method,而是依赖objc runtime运行时函数,基于class metadata类元数据来匹配到oc实例。这避免了在objc运行时机制下分配和有状态副作用。 在第一次传递之后,候选对象将通过第二次传递,检查它们是否与可选的NSPredicate匹配。如果没有谓词,则输出对象的信息最少。如果有谓词,并且对象传递谓词,那么对象将输出更多细节,特别是谓词中查询的细节。

1
2
3
4
5
6
7
findinstances UIView
findinstances *UIView
findinstances UIScrollViewDelegate
findinstances UIView window == nil || hidden == true || alpha == 0 || layer.bounds.#size.width == 0 ||  layer.bounds.#size.height == 0 
findinstances UIView subviews.@count == 0
findinstances NSDictionary any @allKeys beginswith 'perf_'
findinstances NSArray @count > 100

开发使用

构建Xcode项目,并获得到Chisel Framework的路径。:

1
/Users/<me>/Library/Developer/Xcode/DerivedData/Chisel-<stuff>/Build/Products/Debug-iphonesimulator/Chisel.framework/Chisel

在lldb环境下执行:

1
2
3
$ lldb
>>> expr -l objc -- (void*)dlopen("/path/to/Chisel.framework/Chisel", 2)
script o=lldb.SBExpressionOptions(); o.SetLanguage(lldb.eLanguageTypeObjC); o.SetTrapExceptions(False); o.SetTryAllThreads(False); o.SetTimeoutInMicroSeconds(10*1000000); lldb.frame.EvaluateExpression('(void)PrintInstances("<classname>", "<predicate>")', o)

<classname>:可以是class类名或protocol协议名 <predicate>:是一个可由NSPredicate解析的字符串 可以使用regex command 命令: (注意,findinstance后面必须换行)

1
2
command regex findinstances
s/(\S+) *(.*)/script o=lldb.SBExpressionOptions(); o.SetLanguage(lldb.eLanguageTypeObjC); o.SetTrapExceptions(False); o.SetTryAllThreads(False); o.SetTimeoutInMicroSeconds(10*1000000); lldb.frame.EvaluateExpression('(void)PrintInstances("%1", "%2")', o)/

或者,作为python命令,存储在path/to/findinstances.py中:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import lldb

def findinstances(debugger, command, exe_ctx, result, _):
options = lldb.SBExpressionOptions()
options.SetTrapExceptions(False)
options.SetTryAllThreads(False)
options.SetTimeoutInMicroSeconds(10*1000000)
options.SetLanguage(lldb.eLanguageTypeObjC)

frame = exe_ctx.frame

if not exe_ctx.target.module['Chisel']:
frame.EvaluateExpression('(void*)dlopen("/path/to/Chisel.framework/Chisel", 2)', options)

args = command.split(' ', 1)
typeName = args[0]
predicate = args[1] if len(args) > 1 else ''
frame.EvaluateExpression('(void)PrintInstances("{}", "{}")'.format(typeName, predicate), options)

def __lldb_init_module(debugger, _):
debugger.HandleCommand('command script add -f findinstances.findinstances findinstances')

安装

Add Makefile for installing framework This allows you to run make install with optional environment variables in order to build and install Chisel.framework.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
PREFIX ?= /usr/local/lib

export INSTALL_NAME =
ifneq ($(LD_DYLIB_INSTALL_NAME),)
    INSTALL_NAME = "LD_DYLIB_INSTALL_NAME=$(LD_DYLIB_INSTALL_NAME)"
endif

install:
    xcodebuild \
        -scheme Chisel \
        -configuration Release \
        -sdk iphonesimulator \
        install \
        $(INSTALL_NAME) \
        DSTROOT=/ \
        INSTALL_PATH="$(PREFIX)"