decelerationRate属性用来定义用户滑动UIScrollView,手指离开UIScrollView后,UIScrollView减速所受到的阻力。
decelerationRate是一个浮点数类型,系统提供了预先定义的两个值:
UIScrollViewDecelerationRateNormalUIScrollViewDecelerationRateFast
其中UIScrollViewDecelerationRateNormal是默认值。
1 Normal 和 Fast 的值
UIScrollViewDecelerationRateNormal和UIScrollViewDecelerationRateFast的值,打印如下:
# UIScrollViewDecelerationRateNormal
(lldb) po @import UIKit
(lldb) p (UIScrollViewDecelerationRate)UIScrollViewDecelerationRateNormal
(UIScrollViewDecelerationRate) 0.998# UIScrollViewDecelerationRateFast
(lldb) p (UIScrollViewDecelerationRate)UIScrollViewDecelerationRateFast
(UIScrollViewDecelerationRate) 0.98999999999999999
decelerationRate的数值越小,减速越快。
UIScrollViewDecelerationRateFast就是浮点数0.99。
由于计算机对浮点数表示精度问题,显示为0.98999999999999999。
2 decelerationRate setter 的实现
decelerationRate的设置函数代码比较短,直接贴出来:
UIKitCore`-[UIScrollView setDecelerationRate:]:
-> 0x1a2a4998c <+0>: adrp x8, 5091// 1. 读取阈值到 d10x1a2a49990 <+4>: ldr d1, [x8, #0x558]// 2. 将要设置的值与阈值比较0x1a2a49994 <+8>: fcmp d0, d1// 3. 如果要设置的值 < 阈值,w8 置为 1,否则,w8 置为 00x1a2a49998 <+12>: cset w8, lt0x1a2a4999c <+16>: adrp x9, 5091// 4. x9 存储 UIScrollViewDecelerationRateNormal 和 UIScrollViewDecelerationRateFast 组成的数组首地址0x1a2a499a0 <+20>: add x9, x9, #0x5a0 ; UIScrollTestParametersLengthAutomatic + 128// 5. 将 UIScrollViewDecelerationRateNormal 或者 UIScrollViewDecelerationRateFast 读取到 d00x1a2a499a4 <+24>: ldr d0, [x9, w8, uxtw #3]0x1a2a499a8 <+28>: adrp x8, 4164650x1a2a499ac <+32>: ldrsw x8, [x8, #0xf9c]0x1a2a499b0 <+36>: add x8, x0, x8// 6. 存储 decelerationRate 到 UIScrollView 对象0x1a2a499b4 <+40>: stp d0, d0, [x8]0x1a2a499b8 <+44>: ret
注释1,读取阈值到寄存器d1。
代码后面会将要设置的值与阈值比较。
打印阈值为:
(lldb) p $d1
(double) 0.99399999999999999
可以看到,阈值就是0.994。
这个阈值,正好是UIScrollViewDecelerationRateNormal和UIScrollViewDecelerationRateFast的中间值。
同样由于计算机浮点数精度问题,表示为0.99399999999999999。
注释2,注释3,将要设置的值与阈值进行比较。
如果要设置的值比阈值小,那么寄存器w8被置为1,否则,w8被置为0。
寄存器w8的值,后面会看到成为数组索引。
注释4,将UIScrollViewDecelerationRateNormal和UIScrollViewDecelerationRateFast组成的数组首地址,读入寄存器x9。
可以打印这个数组的内容:
(lldb) x/2fg $x9
0x1a3e2c5a0: 0.998
0x1a3e2c5a8: 0.98999999999999999
从打印内容可以看到,数组第一项,是UIScrollViewDecelerationRateNormal,数组第二项是UIScrollViewDecelerationRateFast。
注释5,根据寄存器w8做索引,读入相应的值到寄存器d0。
如果要设置的值比阈值大,那么就读入UIScrollViewDecelerationRateNormal,否则,读入UIScrollViewDecelerationRateFast。
注释6,将最终的值存入到UIScrollView对象中。
存储decelerationRate的实例变量相对于UIScrollView首地址偏移量存储在寄存器x8,打印为:
(lldb) p/x $x8
(unsigned long) 0x0000000000000358
通过_ivarDescription方法打印UIScrollView的所有实例变量,经过尝试,可以发现,偏移量0x358处的实例变量为_decelerationFactor:
(lldb) po class_getInstanceVariable(UIScrollView.class, "_decelerationFactor")
0x000000020a6adbd8
(lldb) po ivar_getOffset(0x000000020a6adbd8)
0x0000000000000358
_decelerationFactor是一个结构体,存储这水平和垂直减速比例值:
_decelerationFactor (struct CGSize): {0.998, 0.998}
总结一下,如果设置UIScrollView的decelerationRate属性,系统会将设置的值与阈值0.994进行比较:
如果设置的值大于阈值,那么系统会选择UIScrollViewDecelerationRateNormal;
如果设置的值小于阈值,那么系统会选择UIScrollViewDecelerationRateFast。
也就是说,无论外界设置何种值,系统只会在UIScrollViewDecelerationRateNormal和UIScrollViewDecelerationRateFast中进行选择。
3 decelerationRate getter 的实现
decelerationRate的getter方法也很短,代码如下:
UIKitCore`-[UIScrollView decelerationRate]:0x1a3ba2354 <+0>: adrp x8, 4120240x1a3ba2358 <+4>: ldrsw x8, [x8, #0xf9c]// 1. 读取 _decelerationFactor 的水平值
-> 0x1a3ba235c <+8>: ldr d0, [x0, x8]0x1a3ba2360 <+12>: ret
注释1,寄存器x8存储这_decelerationFactor结构体相对于UIScrollView首地址的偏移量:
(lldb) p/x $x8
(unsigned long) 0x0000000000000358
这里将_decelerationFactor的水平值读取出来,作为结果返回。
4 _decelerationFactor 结构体
如果我们直接设置UIScrollView的decelerationRate属性,系统始终会把设置的值调整为预定义的UIScrollViewDecelerationRateNormal或者UIScrollViewDecelerationRateFast。
那么,我们能否直接设置_decelerationFactor结构体,从而使用不同的减速系数呢?
通过调用_methodDescription查看UIScrollView的所有方法,可以找到如下2个私有方法:
- (void) setHorizontalScrollDecelerationFactor:(double)arg1; (0x138bd7d00)
- (void) setVerticalScrollDecelerationFactor:(double)arg1; (0x138bd7d70)
这两个方法,这是设置_decelerationFactor结构体的水平值和垂直值。
我们先看setHorizontalScrollDecelerationFactor:方法的实现,垂直方法的实现类似:
UIKitCore`-[UIScrollView setHorizontalScrollDecelerationFactor:]:
-> 0x1a3ba2378 <+0>: adrp x8, 4120240x1a3ba237c <+4>: ldrsw x8, [x8, #0xf9c]// 1. 将设置的值直接存入到 _decelerationFactor 结构体的水平分量0x1a3ba2380 <+8>: str d0, [x0, x8]0x1a3ba2384 <+12>: ret
注释1,将设置的值直接存入到_decelerationFactor结构体的水平分量。
因此,通过上面两个私有方法,就可以设置decelerationRate的值为[0, 1)直接的值。
注意,不能通过上面2个私有方法,将decelerationRate的值设置为1.0,否则滚动时会发生崩溃。
比如如果我们调用setVerticalScrollDecelerationFactor:方法,将垂直减速系数设置为0.999999,那么就会发现拖动UIScrollView之后的减速非常缓慢。

这里需要注意,如果设置了_decelerationFactor的垂直分量,最好一起设置水平分量,因为decelerationRate的getter方法,只返回水平分量的值。
