草庐IT

ios - DispatchWallTime 在 iOS 上有什么作用?

coder 2023-09-26 原文

我认为 DispatchTime 和 DispatchWallTime 之间的区别与应用程序是否已暂停或设备屏幕是否已锁定或其他原因有关:DispatchTime 应该暂停,而 DispatchWallTime 应该继续运行,因为现实世界中的时钟继续运行。

所以我写了一个小测试应用程序:

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        return true
    }
    func applicationDidEnterBackground(_ application: UIApplication) {
        print("backgrounding the app, starting timers for 60 seconds", Date())
        DispatchQueue.main.asyncAfter(deadline: .now() + 60) {
            print("deadline 60 seconds ended", Date())
        }
        DispatchQueue.main.asyncAfter(wallDeadline: .now() + 60) {
            print("wallDeadline 60 seconds ended", Date())
        }
    }
    func applicationWillEnterForeground(_ application: UIApplication) {
        print("app coming to front", Date())
    }
}

我在我的设备上运行了该应用程序。我将应用程序置于后台,等待一段时间,然后将应用程序带到前台。有时“等了一会儿”包括关闭屏幕。我得到了这样的结果:

backgrounding the app, starting timers for 60 seconds 2018-08-15 17:41:18 +0000
app coming to front 2018-08-15 17:41:58 +0000
wallDeadline 60 seconds ended 2018-08-15 17:42:24 +0000
deadline 60 seconds ended 2018-08-15 17:42:24 +0000

backgrounding the app, starting timers for 60 seconds 2018-08-15 17:42:49 +0000
app coming to front 2018-08-15 17:43:21 +0000
wallDeadline 60 seconds ended 2018-08-15 17:43:55 +0000
deadline 60 seconds ended 2018-08-15 17:43:55 +0000

deadline 计时器触发之前的延迟没有我预期的那么长:它比 60 秒的截止日期多了 6 秒,尽管我让应用“休眠”的时间比这长得多。但更令人惊讶的是,两个计时器同时触发。

wallDeadline 在 iOS 上的作用与 deadline 的作用有哪些不同

最佳答案

The Dreams Wind的回答没有错,但我想更准确地理解这些API。这是我的分析。

调度时间

这是上面的评论DispatchTime.init :

/// Creates a `DispatchTime` relative to the system clock that
/// ticks since boot.
///
/// - Parameters:
///   - uptimeNanoseconds: The number of nanoseconds since boot, excluding
///                        time the system spent asleep
/// - Returns: A new `DispatchTime`
/// - Discussion: This clock is the same as the value returned by
///               `mach_absolute_time` when converted into nanoseconds.
///               On some platforms, the nanosecond value is rounded up to a
///               multiple of the Mach timebase, using the conversion factors
///               returned by `mach_timebase_info()`. The nanosecond equivalent
///               of the rounded result can be obtained by reading the
///               `uptimeNanoseconds` property.
///               Note that `DispatchTime(uptimeNanoseconds: 0)` is
///               equivalent to `DispatchTime.now()`, that is, its value
///               represents the number of nanoseconds since boot (excluding
///               system sleep time), not zero nanoseconds since boot.

所以 DispatchTime 是基于 mach_absolute_time 的。但是什么是 mach_absolute_time?它在 mach_absolute_time.s 中定义.每种 CPU 类型都有单独的定义,但关键是它在 x86-like CPU 上使用 rdtsc 并在 ARM 上读取 CNTPCT_EL0 寄存器。在这两种情况下,它得到的值单调增加,并且仅在处理器未处于足够深度的 sleep 状态时增加

请注意,即使设备看起来处于 sleep 状态,CPU 也不一定 sleep 足够深。

DispatchWallTime

DispatchWallTime 定义中没有类似有用的注释,但我们可以查看其now 方法的定义:

public static func now() -> DispatchWallTime {
    return DispatchWallTime(rawValue: CDispatch.dispatch_walltime(nil, 0))
}

然后我们可以咨询the definition of dispatch_walltime :

dispatch_time_t
dispatch_walltime(const struct timespec *inval, int64_t delta)
{
  int64_t nsec;
  if (inval) {
      nsec = (int64_t)_dispatch_timespec_to_nano(*inval);
  } else {
      nsec = (int64_t)_dispatch_get_nanoseconds();
  }
  nsec += delta;
  if (nsec <= 1) {
      // -1 is special == DISPATCH_TIME_FOREVER == forever
      return delta >= 0 ? DISPATCH_TIME_FOREVER : (dispatch_time_t)-2ll;
  }
  return (dispatch_time_t)-nsec;
}

inval为nil时,调用_dispatch_get_nanoseconds,所以let's check that out :

static inline uint64_t
_dispatch_get_nanoseconds(void)
{
  dispatch_static_assert(sizeof(NSEC_PER_SEC) == 8);
  dispatch_static_assert(sizeof(USEC_PER_SEC) == 8);

#if TARGET_OS_MAC
  return clock_gettime_nsec_np(CLOCK_REALTIME);
#elif HAVE_DECL_CLOCK_REALTIME
  struct timespec ts;
  dispatch_assume_zero(clock_gettime(CLOCK_REALTIME, &ts));
  return _dispatch_timespec_to_nano(ts);
#elif defined(_WIN32)
  static const uint64_t kNTToUNIXBiasAdjustment = 11644473600 * NSEC_PER_SEC;
  // FILETIME is 100-nanosecond intervals since January 1, 1601 (UTC).
  FILETIME ft;
  ULARGE_INTEGER li;
  GetSystemTimePreciseAsFileTime(&ft);
  li.LowPart = ft.dwLowDateTime;
  li.HighPart = ft.dwHighDateTime;
  return li.QuadPart * 100ull - kNTToUNIXBiasAdjustment;
#else
  struct timeval tv;
  dispatch_assert_zero(gettimeofday(&tv, NULL));
  return _dispatch_timeval_to_nano(tv);
#endif
}

它引用 POSIX CLOCK_REALTIME 时钟。因此它基于时间的普遍概念,如果您在“设置”(或 Mac 上的“系统偏好设置”)中更改设备的时间,它会发生变化。

神秘的六秒

你说你的计时器启动了

6 seconds over the 60 second deadline

让我们看看它是从哪里来的。

asyncAfter(deadline:execute:)asyncAfter(wallDeadline:execute:) 调用相同的 C API,dispatch_after。截止日期(或“时钟”)的类型与时间值一起被编码到 dispatch_time_t 中。 dispatch_after 函数调用 the internal GCD function _dispatch_after ,我在这里部分引用:

static inline void
_dispatch_after(dispatch_time_t when, dispatch_queue_t dq,
      void *ctxt, void *handler, bool block)
{
  dispatch_timer_source_refs_t dt;
  dispatch_source_t ds;
  uint64_t leeway, delta;

截图

  delta = _dispatch_timeout(when);
  if (delta == 0) {
      if (block) {
          return dispatch_async(dq, handler);
      }
      return dispatch_async_f(dq, ctxt, handler);
  }
  leeway = delta / 10; // <rdar://problem/13447496>

  if (leeway < NSEC_PER_MSEC) leeway = NSEC_PER_MSEC;
  if (leeway > 60 * NSEC_PER_SEC) leeway = 60 * NSEC_PER_SEC;

截图

  dispatch_clock_t clock;
  uint64_t target;
  _dispatch_time_to_clock_and_value(when, &clock, &target);
  if (clock != DISPATCH_CLOCK_WALL) {
      leeway = _dispatch_time_nano2mach(leeway);
  }
  dt->du_timer_flags |= _dispatch_timer_flags_from_clock(clock);
  dt->dt_timer.target = target;
  dt->dt_timer.interval = UINT64_MAX;
  dt->dt_timer.deadline = target + leeway;
  dispatch_activate(ds);
}

_dispatch_timeout 函数可以在 time.c 中找到.可以说它返回当前时间和传递给它的时间之间的纳秒数。它根据传递给它的时间时钟确定“当前时间”。

因此 _dispatch_after 获取执行 block 之前要等待的纳秒数。然后它将 leeway 计算为该持续时间的十分之一。当它设置计时器的截止日期时,它会为您传入的截止日期添加余地

在您的情况下,delta 大约为 60 秒(= 60 * 109 纳秒),因此 leeway 大约为 6 秒。因此,您的 block 将在您调用 asyncAfter 后大约 66 秒执行。

关于ios - DispatchWallTime 在 iOS 上有什么作用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51863940/

有关ios - DispatchWallTime 在 iOS 上有什么作用?的更多相关文章

  1. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  2. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  3. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  4. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  5. ruby - 为什么 4.1%2 使用 Ruby 返回 0.0999999999999996?但是 4.2%2==0.2 - 2

    为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返

  6. ruby - ruby 中的 TOPLEVEL_BINDING 是什么? - 2

    它不等于主线程的binding,这个toplevel作用域是什么?此作用域与主线程中的binding有何不同?>ruby-e'putsTOPLEVEL_BINDING===binding'false 最佳答案 事实是,TOPLEVEL_BINDING始终引用Binding的预定义全局实例,而Kernel#binding创建的新实例>Binding每次封装当前执行上下文。在顶层,它们都包含相同的绑定(bind),但它们不是同一个对象,您无法使用==或===测试它们的绑定(bind)相等性。putsTOPLEVEL_BINDINGput

  7. ruby - Infinity 和 NaN 的类型是什么? - 2

    我可以得到Infinity和NaNn=9.0/0#=>Infinityn.class#=>Floatm=0/0.0#=>NaNm.class#=>Float但是当我想直接访问Infinity或NaN时:Infinity#=>uninitializedconstantInfinity(NameError)NaN#=>uninitializedconstantNaN(NameError)什么是Infinity和NaN?它们是对象、关键字还是其他东西? 最佳答案 您看到打印为Infinity和NaN的只是Float类的两个特殊实例的字符串

  8. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

    如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

  9. ruby - 为什么 SecureRandom.uuid 创建一个唯一的字符串? - 2

    关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion为什么SecureRandom.uuid创建一个唯一的字符串?SecureRandom.uuid#=>"35cb4e30-54e1-49f9-b5ce-4134799eb2c0"SecureRandom.uuid方法创建的字符串从不重复?

  10. ruby - 当使用::指定模块时,为什么 Ruby 不在更高范围内查找类? - 2

    我刚刚被困在这个问题上一段时间了。以这个基地为例:moduleTopclassTestendmoduleFooendend稍后,我可以通过这样做在Foo中定义扩展Test的类:moduleTopmoduleFooclassSomeTest但是,如果我尝试通过使用::指定模块来最小化缩进:moduleTop::FooclassFailure这失败了:NameError:uninitializedconstantTop::Foo::Test这是一个错误,还是仅仅是Ruby解析变量名的方式的逻辑结果? 最佳答案 Isthisabug,or

随机推荐