一、Cycript
语法简介
- 根据 上一篇文章 进入
Cycript
调试模式之后,就需要通过脚本语言来进行操作调试了,比如:获取页面控制器
、获取对象属性
、手动调用对象方法
、页面上添加自己的UI
…等等。 - Cycript 也是有语法例子的,可以进去参考,命令格式:
命令 | 结果 |
UIApp | 获取当前 App 的 Appdelegate 对象 |
UIApp.keyWindow | 获取当前的keyWindow对象 |
UIWindow.keyWindow | 获取当前的keyWindow对象 |
var | 定义一个对象 |
#内存地址 | 获得当前内存地址的对象 |
#内存地址 | 获得当前内存地址的对象 |
*对象 | 获得对象的所有成员变量 |
choose(UILabel) | 查找当前界面的所有UILabel |
- 在进入
cycript
调试环境后,就可以直接写OC
、JS
… 等支持的语法了,例如:
OC
(其实也就是跟OC
语法非常非常像的语法格式):
cy# [UIApp description] @"<NMApplication: 0x113d4d710>"
UIApp
等于UIApplication
对象,这两种写法都一样,脚本给与的缩写。
cy# UIApp #"<NMApplication: 0x113d4d710>"
JS
:
cy# [for (x of [1,2,3]) x+1] [2,3,4]
- …
二、定义变量
- 不支持
OC
里面那种NSString *name = @"dzm"
这种写法
$ var 变量名 = 变量值 • 1
// 进入调试 iPhone:~ root# cycript -p neteasemusic // 创建变量 window cy# var window = UIApp.keyWindow #"<OTTouchObservingWindow: 0x10a65b610; baseClass = UIWindow; frame = (0 0; 320 568); gestureRecognizers = <NSArray: 0x28043da10>; layer = <UIWindowLayer: 0x280af58a0>>" // 输出变量 window 跟上面的 UIApp.keyWindow 值一致 cy# window #"<OTTouchObservingWindow: 0x10a65b610; baseClass = UIWindow; frame = (0 0; 320 568); gestureRecognizers = <NSArray: 0x28043da10>; layer = <UIWindowLayer: 0x280af58a0>>"
三、获取常用对象(举例)
UIApp
等于UIApplication
对象。
cy# [UIApp description] @"<NMApplication: 0x113d4d710>"
cy# UIApp #"<NMApplication: 0x113d4d710>"
- 获取
keyWindow
对象(OTTouchObservingWindow
其实就是UIWindow
,只是编译之后iOS
内部做了转换)。
cy# UIApp.keyWindow #"<OTTouchObservingWindow: 0x10a65b610; baseClass = UIWindow; frame = (0 0; 320 568); gestureRecognizers = <NSArray: 0x281ac01e0>; layer = <UIWindowLayer: 0x28146e0a0>>"
- 获取
rootViewController
。
cy# UIApp.keyWindow.rootViewController #"<NMRootNavigationController: 0x10a8cd400>"
四、内存地址访问对象
- 获取到
keyWindow
的内存地址0x10a65b610
:
cy# UIApp.keyWindow #"<OTTouchObservingWindow: 0x10a65b610; baseClass = UIWindow; frame = (0 0; 320 568); gestureRecognizers = <NSArray: 0x281ac01e0>; layer = <UIWindowLayer: 0x28146e0a0>>"
- 可以通过
#0x10a65b610
这种方式通过内存地址访问这个对象,你会发现跟上面输出 的对象是一样的:
cy# #0x10a65b610 #"<OTTouchObservingWindow: 0x10a65b610; baseClass = UIWindow; frame = (0 0; 320 568); gestureRecognizers = <NSArray: 0x28043da10>; layer = <UIWindowLayer: 0x280af58a0>>"
- 也可以通过内存地址的方式获取该对象下面的其他属性值:
cy# #0x10a65b610.rootViewController #"<NMRootNavigationController: 0x10a8cd400>"
五、ObjectiveC.classes
获取已加载的所有OC类
ObjectiveC.classes
:你可以理解成获取列出来所有的.h
头文件或者class
对象,就是列出当前已经加载所有类。
cy# ObjectiveC.classes { // 系统类 __NSGenericDeallocHandler:__NSGenericDeallocHandler,_NSZombie_:_NSZombie_,__NSMessageBuilder:__NSMessageBuilder,..... // 自定义类 RMLog:RMLog,NMSearchShowCell,NMSearchPlaylistCell:NMSearchPlaylistCell,NMDjRadioPurchaseProgramCell:NMDjRadioPurchaseProgramCell,...... }
- 这样一看做
iOS
都知道啥意思了,都是类名,自定义类,Cell 之类的。
六、查看对象的所有成员变量
- 在后面经常会需要知道
某个对象
里面有哪些成员变量
或者属性
。 - 只需要在对象前面加个
*
号即可,例如:*UIApp
:
cy# *UIApp {isa:NSKVONotifying_NMApplication,_hasOverrideClient:0,_hasOverrideHost:0,_hasInputAssistantItem:0,_delegate:#"<NMAppDelegate: 0x2838b2010>",_event:#"<UIEvent: 0x2804cdce0>",_motionEvent:#"<UIMotionEvent: 0x2831b4d00> timestamp: 0 subtype: 0",_remoteControlEvent:#"<UIRemoteControlEvent: 0x28149b1c0>",_remoteControlEventObservers:1,_topLevelNibObjects:null,_networkResourcesCurrentlyLoadingCount:0,_hideNetworkActivityIndicatorTimer:null,_editAlertController:null,_statusBar:#"<UIStatusBar: 0x10a863800; frame = (0 0; 320 20); opaque = NO; autoresize = W+BM; layer = <CALayer: 0x280a8ab20>>",_statusBarRequestedStyle:0,_statusBarWindow:#"<UIStatusBarWindow: 0x10a6561d0; frame = (0 0; 320 568); opaque = NO; gestureRecognizers = <NSArray: 0x280438930>; ......}
- 上面的所有属性都是以
,
逗号分开,每一个逗号就是一个成员变量。
挑个出来看看,比如代理属性
,前面是属性名称
,后面是值
。
_delegate:#"<NMAppDelegate: 0x2838b2010>"
七、递归打印 View
的所有子控件
- 在
iOS
开发中,可以通过控制台 LLDB
打断点后po
的方式递归输出指定视图的所有子控件。
- 语法
po [self.view.window recursiveDescription]
(lldb) po [self.view.window recursiveDescription] <UIWindow: 0x7f9f8b618460; frame = (0 0; 375 667); gestureRecognizers = <NSArray: 0x60000077a1c0>; layer = <UIWindowLayer: 0x600000926de0>> | <UITransitionView: 0x7f9f8b719630; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x60000093b300>> | | <UIDropShadowView: 0x7f9f8b71a060; frame = (0 0; 375 667); clipsToBounds = YES; autoresize = W+H; layer = <CALayer: 0x60000093b280>> | | | <UIView: 0x7f9f8b719ef0; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x60000093b2c0>> <UIWindow: 0x7f9f8b618460; frame = (0 0; 375 667); gestureRecognizers = <NSArray: 0x60000077a1c0>; layer = <UIWindowLayer: 0x600000926de0>> | <UITransitionView: 0x7f9f8b719630; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x60000093b300>> | | <UIDropShadowView: 0x7f9f8b71a060; frame = (0 0; 375 667); clipsToBounds = YES; autoresize = W+H; layer = <CALayer: 0x60000093b280>> | | | <UIView: 0x7f9f8b719ef0; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x60000093b2c0>>
- 在
Cycript
里面递归输出子控件,其实也是使用recursiveDescription
。
cy# [UIApp.keyWindow recursiveDescription] ......
cy# [UIApp.keyWindow recursiveDescription].toString() ......
- 由于输出太多我就不贴了,跟上面
iOS
中一样,一层一层的列出来。一种是递归列出所有子控件,一种是将列出的子控件转成字符串输出,两者没什么区别,就是排版的区别,都可以。
七、choose
删选出指定类型的对象
- 意思是:指定一个类,只要是继承或者等于这个类的都会被列出来,只会列出已加载到缓存中。
- 例如
UIViewController
,列出当前已加载到缓存是继承或者直接使用UIViewController
的,网易云首页举例
:
cy# choose(UIViewController)
- 输出:
[#"<NMVoiceHomeViewController: 0x11a400200>",#"<NMSettingTabViewController: 0x11a404020>",#"<NMSingTabViewController: 0x11a407540>",#"<NMContainerViewController: 0x11a409ce0>",#"<NMContainerViewController: 0x11a414d60>",#"<NMMainViewController: 0x10a6d8780>",#"<NMContainerViewController: 0x10a5f5220>",#"<NMMineTabViewController: 0x10a5f6910>",#"<NMContainerViewController: 0x11a519390>",#"<NMContainerViewController: 0x11a52f3f0>",#"<NMTabBarController: 0x10b946000>",#"<NMNavigationController: 0x10b9a7000>",#"<NMNavigationController: 0x10b9cc400>",#"<NMNavigationController: 0x10b9d6c00>",#"<NMNavigationController: 0x10b9e0200>",#"<NMNavigationController: 0x10b9ea600>",#"<NMNavigationController: 0x10a83f400>",#"<NMRootNavigationController: 0x10a8cd400>",#"<NMNavigationController: 0x10a8e7200>",#"<NMNavigationController: 0x10a912a00>",#"<NMNavigationController: 0x10a919c00>",#"<NMNavigationController: 0x10a975800>"]
上面这些就是当前软件已经加载到缓存的控制器,且都是继承或者等于UIViewController
的
如果一个都没有则为空数组,如果输出崩溃,则重新进入调试模式即可:
cy# choose(UITableViewCell) []
- 现在只是语法大致了解一下,后面会通过
Cycript
写一些脚本用来快捷调试,比如:拿到当前正在显示的控制器,这个就可以充利用上面的属性获取方式以及iOS
开发中的获取控制器方式配合来获取了。 - 后面会写到封装这样一个脚本,毕竟这个会很方便的帮我们找到当前控制器,也就知道在哪个文件可以做调整了。