互联网科技

iOS的界面间通信

作者:金沙国际官网    发布时间:2020-05-04 23:27     浏览次数 :92

[返回]

查看UIScrollView的头文件可以发现这样一段注释:

UINavigationController:导航控制器.是iOS中提供的多视图控制器,用来管理多个视图控制器,而且视图控制器之间存在依赖关系.本篇文章通过一个小例子来讲述界面间通信的方法.先看效果图:

LLDB还内置了一个Python解析器,使用Python脚本可以方便LLDB的调试,比如自动化执行一些命令,或者自动化处理数据之类的,具体说明可以参考官方的文档:LLDB Python Reference。

// When the user taps the status bar, the scroll view beneath the touch which is closest to the status bar will be scrolled to top, but only if its `scrollsToTop` property is YES, its delegate does not return NO from `shouldScrollViewScrollToTop`, and it is not already at the top.// On iPhone, we execute this gesture only if there's one on-screen scroll view with `scrollsToTop` == YES. If more than one is found, none will be scrolled.@property(nonatomic) BOOL scrollsToTop; // default is YES.

图片 1效果图

以下就以一个具体的例子来演示一个Python脚本的编写过程:

这样我们就可以很清楚的了解到,scrollsToTop 的默认值是 YES ,然而当有多个 UIScrollView 的时候,用户点击 StatusBar ,系统就不知道让哪一个 UIScrollView 来执行 scrollsToTop 这个动作了,所以就导致失效了。这样一来,解决方法就很简单了,设置你想要执行 scrollsToTopUIScrollView@property(nonatomic) BOOL scrollsToTop; 属性值为 YES ,其他的 UIScrollView 都为 NO 即可。注意:凡是 UIScrollView以及 UIScrollView 的子类都要设置。如: UITableViewUIWebViewUICollectionView 等。

看到效果图,我们能看出,它利用了导航控制器来控制视图的切换,以及传值等问题.而在这一篇文章中,我主要为大家介绍下如何用自定义代理模式来从"设置"视图中,把两个输入框中的数值取出来.并在计算器视图中得到计算结果,并显示.

一、获取方法的偏移地址

运行系统自带的计算器Calculator.app
图片 2

可以看到计算器的菜单里有一个“关于计算器”的选项:
图片 3

如果我们要在点击“关于计算器”事件断下来的话,就要给这个方法设置一个断点。

先找到计算器的可执行文件的路径,路径为:/Applications/Calculator.app/Contents/MacOS/Calculator
打开Hopper Disassembler反汇编调试器,将可执行文件拖进去,等待Hopper分析完成。

在左侧的搜索框输入”showabout”,会搜索到一个结果:
图片 4

点击这个结果,会跳转到该方法的汇编代码处:

                     -[CalculatorController showAbout:]:
00000001000093dd         push       rbp                                         ; Objective C Implementation defined at 0x1000188d0 (instance)
00000001000093de         mov        rbp, rsp
00000001000093e1         mov        rdi, qword [ds:objc_cls_ref_NSDictionary]   ; objc_cls_ref_NSDictionary, argument "instance" for method imp___got__objc_msgSend
00000001000093e8         mov        rsi, qword [ds:0x10001b6f0]                 ; @selector(dictionaryWithObject:forKey:), argument "selector" for method imp___got__objc_msgSend
00000001000093ef         lea        rdx, qword [ds:cfstring_2000]               ; @"2000"
00000001000093f6         lea        rcx, qword [ds:cfstring_CopyrightStartYear] ; @"CopyrightStartYear"
00000001000093fd         call       qword [ds:imp___got__objc_msgSend]
0000000100009403         mov        rdi, rax
0000000100009406         pop        rbp
0000000100009407         jmp        imp___stubs__NSShowSystemInfoPanel
                        ; endp

由汇编代码可知[CalculatorController showAbout:]方法在文件中的偏移地址为0x00000001000093dd

图片 5扫码关注公众号

首先,我们需要创建两个视图控制器,一个是计算机视图控制器,一个是设置视图控制器.然后再AppDelegate.m文件中定义一个导航控制器,来管理这两个视图控制器.代码如下:

二、使用LLDB调试器设置断点

接下来运行终端,执行以下命令,让LLDB调试器依附计算器的进程:

Jobs: ~$ ps aux | grep Calculator
Jobs            79888   0.0  0.1  2781792  11620   ??  S     6:21下午   0:01.07 /Applications/Calculator.app/Contents/MacOS/Calculator
Jobs            80204   0.0  0.0  2432772    568 s002  R+    6:26下午   0:00.00 grep Calculator
Jobs: ~$ lldb -p 79888
(lldb) process attach --pid 79888
Process 79888 stopped
* thread #1: tid = 0x6030af, 0x00007fff912d34de libsystem_kernel.dylib`mach_msg_trap + 10, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
    frame #0: 0x00007fff912d34de libsystem_kernel.dylib`mach_msg_trap + 10
libsystem_kernel.dylib`mach_msg_trap:
->  0x7fff912d34de <+10>: retq
    0x7fff912d34df <+11>: nop

libsystem_kernel.dylib`mach_msg_overwrite_trap:
    0x7fff912d34e0 <+0>:  movq   %rcx, %r10
    0x7fff912d34e3 <+3>:  movl   $0x1000020, %eax

Executable module set to "/Applications/Calculator.app/Contents/MacOS/Calculator".
Architecture set to: x86_64h-apple-macosx.
(lldb) continue
Process 79888 resuming
(lldb)

接着执行以下命令获取应用的ASLR偏移地址:

(lldb) image list -o
[  0] 0x000000000cafa000
[  1] 0x00007fff93d51000
[  2] 0x000000010cb1e000
......
(lldb)

第一行数据的16进制地址0x000000000cafa000就是应用的ASLR偏移地址,之前我们已经找到了[CalculatorController showAbout:]方法的偏移地址是0x00000001000093dd,那么该方法在内存中的地址就是0x000000000cafa000 + 0x00000001000093dd

所以我们可以执行下面的命令来给这个方法设置断点,注意加号两边不要有空格:

(lldb) br set -a "0x000000000cafa000+0x00000001000093dd"
Breakpoint 1: where = Calculator`___lldb_unnamed_function161$$Calculator, address = 0x000000010cb033dd
(lldb)

这时点击菜单的“关于计算器”选项,程序就会断下来:

Process 79888 stopped
* thread #1: tid = 0x6709de, 0x000000010cb033dd Calculator`___lldb_unnamed_function161$$Calculator, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
    frame #0: 0x000000010cb033dd Calculator`___lldb_unnamed_function161$$Calculator
Calculator`___lldb_unnamed_function161$$Calculator:
->  0x10cb033dd <+0>:  pushq  %rbp
    0x10cb033de <+1>:  movq   %rsp, %rbp
    0x10cb033e1 <+4>:  movq   0x12b98(%rip), %rdi       ; (void *)0x00007fff78684488: NSDictionary
    0x10cb033e8 <+11>: movq   0x12301(%rip), %rsi       ; "dictionaryWithObject:forKey:"
(lldb)

原文连接

图片 6创建导航控制器,并且指定计算器视图为所管理的根视图

三、编写Python脚本

那么问题来了,我们每次调试应用都要先手动获取ASLR偏移地址,如果要给多个地址打断点的话,就要写很多遍”ASLR偏移地址+方法偏移地址”,操作比较繁琐。如果这些重复性的操作可以自动完成的话,在平时的使用过程中应该能节省不少时间。

用Python就可以很方便地实现这个功能,接下来我们就来写一个简单的脚本,以简化设置断点的操作。脚本的主要功能是自动获取应用的ASLR地址,用户设置断点时只需输入方法在文件中的的偏移地址,脚本会自动把ASLR偏移地址和方法偏移地址相加,再设置断点。

新建一个Python文件,保存至~/sbr.py,代码如下:

#!/usr/bin/python
#coding:utf-8

import lldb
import commands
import optparse
import shlex
import re


# 获取ASLR偏移地址
def get_ASLR():
    # 获取'image list -o'命令的返回结果
    interpreter = lldb.debugger.GetCommandInterpreter()
    returnObject = lldb.SBCommandReturnObject()
    interpreter.HandleCommand('image list -o', returnObject)
    output = returnObject.GetOutput();
    # 正则匹配出第一个0x开头的16进制地址
    match = re.match(r'.+(0x[0-9a-fA-F]+)', output)
    if match:
        return match.group(1)
    else:
        return None

# Super breakpoint
def sbr(debugger, command, result, internal_dict):

    #用户是否输入了地址参数
    if not command:
        print >>result, 'Please input the address!'
        return

    ASLR = get_ASLR()
    if ASLR:
        #如果找到了ASLR偏移,就设置断点
        debugger.HandleCommand('br set -a "%s+%s"' % (ASLR, command))
    else:
        print >>result, 'ASLR not found!'

# And the initialization code to add your commands 
def __lldb_init_module(debugger, internal_dict):
    # 'command script add sbr' : 给lldb增加一个'sbr'命令
    # '-f sbr.sbr' : 该命令调用了sbr文件的sbr函数
    debugger.HandleCommand('command script add sbr -f sbr.sbr')
    print 'The "sbr" python command has been installed and is ready for use.'

然后在LLDB中执行下面的语句就可以把脚本导入到LLDB:

(lldb) command script import ~/sbr.py
The "sbr" python command has been installed and is ready for use.
(lldb)

输出结果表示名为sbr的Python命令已经装载好并可以使用了。
sbrSuper breakpoint的意思,只需接收一个方法偏移地址作为参数。
接下来验证一下效果,先清除断点列表,再用sbr命令来设置断点。

(lldb) br delete
About to delete all breakpoints, do you want to do that?: [Y/n] y
All breakpoints removed. (1 breakpoint)
(lldb) sbr 0x00000001000093dd
Breakpoint 2: where = Calculator`___lldb_unnamed_function161$$Calculator, address = 0x000000010cb033dd
(lldb)

这时点击菜单的“关于计算器”选项,方法也成功断下来了。

<推广> 综合计算器 是一款多功能计算器,可以计算房贷、个税、年终奖、利息、BMI指数以及大小金额的计算器,再也不用下载多个计算器找来找去了。还支持实时更新贷款利率哦~。

关于UINavigationController的知识,这里不再详细说明.先布局计算器视图(在计算器视图控制器中):

四、启动LLDB时自动加载Python脚本

对于经常要使用到的脚本,可以在LLDB的初始化文件里添加命令加载脚本,这样LLDB启动后就能使用自定义的命令了。
修改~/.lldbinit文件,在文件里加入一行:

command script import ~/sbr.py

重新进入LLDB,可以看到脚本已经自动加载了:

Jobs: ~$ lldb
The "sbr" python command has been installed and is ready for use.
(lldb) command source -s 1 '/Users/Jobs/./.lldbinit'
The "sbr" python command has been installed and is ready for use.
(lldb)

图片 7计算器视图布局

接下来,布局设置视图.(关于一些控件的基本知识,这里不再详细赘述)

图片 8设置视图布局