• 浅谈iOS调试技巧的实际应用(二) UI篇


    前篇简单介绍了断点及几个调试命令的使用的使用。

    做iOS开发, 大部分的工作跟UI相关,而分析UI最重要的就是分析UI的Hierarchy,这篇就是围绕View Hierarchy来详细介绍UI相关的调试技巧。

    • 获取Hierarchy

      巧妇难为无米之炊,首先我们得得到Hierarchy,方法是调用某个UIView对象或其子类的recursiveDescription方法。

      recursiveDescription方法是UIView的私有方法。苹果未公开该方法。常用的的场合是, 在任意断点处执行

    po [[[UIApplication sharedApplication] keyWindow] recursiveDescription]

    po [oneView recursiveDescription]

      该方法将递归打印该View的所有子View的层次关系,效果如下图:

      

    • 通过Hierarchy能解决哪些问题? 可用在哪些场景?
      •   场景1: 获取当前正在显示的是哪个页面(ViewController)?

            好吧, 同事在很专心的思索某个问题, 我不忍心打扰他,我想知道他的VC是什么, 做了什么事情?或者想知道某个问题它是如何处理的。

            这时, 你先在模拟器里定位到他的页面, 然后pause program,再在控制台打印当前keyWindow的Hierarchy,然后从输出信息里, 从按照等级从高到低(将View前的“|”的数量定义为该View的等级,越少等级越高), 然后同一等级下从下往上找。因为,在打印信息里,在同一层级下,越往下的View,zOrder越高。zOrder高的View当然会盖住zOrder低的View。

            以上图为例, 你发现<UIView: 0x8d75ba0...你要找的View。因为前面的都是系统的View。它会将你包裹起来做其他事情。

            好了,View知道了, 那VC呢?

            对象与对象之间的关系在某种程度上可以分为3种:1.互不相知, 2.单向引用, 3,双向引用。后面2种有点像链表。我们都知道通过VC的view属性就可以找到View了。但是,好像从来没有过通过view找到它的VC的经验?对吧。但是,也许, 你会猜,View应该也会记住它的VC是谁吧。是的。 事实确实如此。

            

            如图(UIView的.h文件), 你发现你找到证据了,VC是view的一个代理,对的, 因为View有时候需要让VC去做一些事情。 但是,这只是结构体中的一个变量了。它不是一个类的属性, 没有get, set方法, 你不能通过给对象发送消息来获取这个值啊。因为没有这个方法。 那怎么办呢? 对了。 这时OC Runtime的KVO机制就可以发挥作用了。

    [oneView ValueForKey:@"_viewDelegate"]

            上面这个方法就可以获取view的VC是谁了。

            除了上面KVO的方法?还有其他方法吗?答案是:有的。

            如果你了解UI的事件传递机制。你会知道消息会一直从hitTestView一直往上forward, 直到UIApplication为止。这个工作是由基类UIResponder完成的。当一个Responder对象不对事件处理时, 它会调用nextResponder方法找到Responder Chain的下一个对象, 然后丢给它处理。

            好了。如果一个View有它的VC, 按照事件传递机制, 它的nextResponder必定是VC。对了,如此问题就解决了。 调用View的nextResponder就一定能找到它的VC是谁。是不是很简单呢?

    [oneView nextResponder]
      •   场景2: 为什么我的视图没显示呢?或者在某个时候不见了呢?

            这个问题比较复杂。因为View的很多属性都会影响视图的显示。解决问题的方法如下:

             1. 自身原因

              frame:有可能origion映射到Window上时,已经超出Window的边界了。(可以通过UIView的convert方法获取)

                  有可能你的width或者height中有一个为0.

              hidden: 有可能某个时候hidden属性被置为YES。

              alpha:有可能某个时候alpha属性被置为0.

             2. superView的原因

              也许你的view根本没add到superView上,或者某个时候被移除。也许是由于superView的上述“自身原因”,也许是superView的superView... 。

             

             幸好,frame属性在Hierarchy信息中被打出, 如果hidden属性为YES,alpha为0, Hierarchy会特意打印出来。所以, 基于Hierarchy你就可以分析某个试图为什么没有正确显示的原因。

      •   场景3: 为什么我的视图(通过UIControll或者加UIGesture)点下去没反应?

            这个问题很复杂,有可能它不是hitTestView, 有可能消息被UIGesture优先捕获了。这里牵涉到消息机制, 暂时不在这里说明。

            但是, 下面可以针对hitTestView这个原因来进行分析。

            如果它不被确定为hitTestView。 那么

            1.自身原因

              frame: 有可能你的width或者height为0(如果一个视图的clipsToBounds为NO时,有可能他的frame是空,但是依然能显示出来。例如UIImageView),此时hitTest方法不能判定该View能相应事件。

                  有可能自身的frame超出superView的frame。 那么超出部分的事件将不会将hitTestView判定给该View。这个问题比较隐蔽。也会经常遇到。

              userInteractionEnabled: 为NO时,不能响应事件。

              Hidden:为YES时,不能响应事件。

            2.superView的原因

              superView的上述“自身原因”也会影响到子View响应事件。

            类似场景2. 上面的问题也可以通过查看打印的Hierarchy分析。

        基于Hierarchy的UI调试方法还有通过第三方工具, 例如Hierarchy Viewer可视化工具等。

        当然,通过Hierarchy还可以做更多的事情。 暂时先介绍到这里。请待下一次再进行更新。

  • 相关阅读:
    swift 初见-4运算符与字符串操作
    IOS中数据持久化1-CoreData
    swift 初见-3
    swift 初见-2
    系统硬件1-短信,打电话
    swift 初见-1
    socket理解流程图
    文件操作方法fscanf
    Prim模板
    树剖求LCA模板
  • 原文地址:https://www.cnblogs.com/JohnDeBlog/p/3550919.html
Copyright © 2020-2023  润新知