说三道四技术文摘-感悟人生的经典句子
说三道四 > 文档快照

优化UITableViewCell高度计算的那些事

HTML文档下载 WORD文档下载 PDF文档下载
在将UITableView+FDTemplateLayoutCell自动算高工具更新至1.2版本之后,百度知道iOS团队对UITableViewCell利用AutoLayout自动高度计算和UITableView滑动优化进行了总结,以及RunLoop实践技巧。

前言

这篇文章是我和我们团队最近对UITableViewCell利用AutoLayout自动高度计算和UITableView滑动优化的一个总结。从这篇文章里,你可以读到:

  • UITableView高度计算和估算的机制
  • 不同iOS系统在高度计算上的差异
  • iOS8 self-sizing cell
  • UITableView+FDTemplateLayoutCell如何用一句话解决高度问题
  • UITableView+FDTemplateLayoutCell中对RunLoop的使用技巧

UITableViewCell高度计算

rowHeight

UITableView是我们再熟悉不过的视图了,它的delegate和data source回调不知写了多少次,也不免遇到UITableViewCell高度计算的事。UITableView询问cell高度有两种方式。一种是针对所有Cell具有固定高度的情况,通过:

self.tableView.rowHeight = 88;

上面的代码指定了一个所有cell都是88高度的UITableView,对于定高需求的表格,强烈建议使用这种(而非下面的)方式保证不必要的高度计算和调用。rowHeight属性的默认值是44,所以一个空的UITableView显示成那个样子。

另一种方式就是实现UITableViewDelegate中的:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {    // return xxx}

需要注意的是,实现了这个方法后,rowHeight的设置将无效。所以,这个方法适用于具有多种cell高度的UITableView。

estimatedRowHeight

这个属性iOS7就出现了,文档是这么描述它的作用的:

If the table contains variable height rows, it might be expensive to calculate all their heights when the table loads. Using estimation allows you to defer some of the cost of geometry calculation from load time to scrolling time.

恩,听上去蛮靠谱的。我们知道,UITableView是个UIScrollView,就像平时使用UIScrollView一样,加载时指定contentSize后它才能根据自己的bounds、contentInset、contentOffset等属性共同决定是否可以滑动以及滚动条的长度。而UITableView在一开始并不知道自己会被填充多少内容,于是询问data source个数和创建cell,同时询问delegate这些cell应该显示的高度,这就造成它在加载的时候浪费了多余的计算在屏幕外边的cell上。和上面的rowHeight很类似,设置这个估算高度有两种方法:

self.tableView.estimatedRowHeight = 88;// or- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {    // return xxx}

有所不同的是,即使面对种类不同的cell,我们依然可以使用简单的estimatedRowHeight属性赋值,只要整体估算值接近就可以,比如大概有一半cell高度是44, 一半cell高度是88, 那就可以估算一个66,基本符合预期。

说完了估算高度的基本使用,可以开始吐槽了:

  1. 设置估算高度后,contentSize.height根据“cell估算值 x cell个数”计算,这就导致滚动条的大小处于不稳定的状态,contentSize会随着滚动从估算高度慢慢替换成真实高度,肉眼可见滚动条突然变化甚至“跳跃”。
  2. 若是有设计不好的下拉刷新或上拉加载控件,或是KVO了contentSize或contentOffset属性,有可能使表格滑动时跳动。
  3. 估算高度设计初衷是好的,让加载速度更快,那凭啥要去侵害滑动的流畅性呢,用户可能对进入页面时多零点几秒加载时间感觉不大,但是滑动时实时计算高度带来的卡顿是明显能体验到的,个人觉得还不如一开始都算好了呢(iOS 8更过分,即使都算好了也会边划边计算)。

iOS8 self-sizing cell

具有动态高度内容的cell一直是个头疼的问题,比如聊天气泡的cell,frame布局时代通常是用数据内容反算高度:

CGFloat height = textHeightWithFont() + imageHeight + topMargin + bottomMargin + ...;

供UITableViewDelegate调用时很可能是个cell的类方法:

@interface BubbleCell : UITableViewCell+ (CGFloat)heightWithEntity:(id)entity;@end

各种魔法margin加上耦合了屏幕宽度。

AutoLayout时代好了不少,提供了-systemLayoutSizeFittingSize:的API,在contentView中设置约束后,就能计算出准确的值;缺点是计算速度肯定没有手算快,而且这是个实例方法,需要维护专门为计算高度而生的template layout cell,它还要求使用者对约束设置的比较熟练,要保证contentView内部上下左右所有方向都有约束支撑,设置不合理的话计算的高度就成了0。

这里还不得不提到一个UILabel的蛋疼问题,当UILabel行数大于0时,需要指定preferredMaxLayoutWidth后它才知道自己什么时候该折行。这是个“鸡生蛋蛋生鸡”的问题,因为UILabel需要知道superview的宽度才能折行,而superview的宽度还依仗着子view宽度的累加才能确定。这个问题好像到iOS 8才能够自动解决(不过我们找到了解决方案)。

回到正题,iOS 8 WWDC中推出了self-sizing cell的概念,旨在让cell自己负责自己的高度计算,使用frame layout和auto layout都可以享受到:


这个特性首先要求是iOS 8,要是最低支持的系统版本小于8的话,还得针对老版本单写套老式的算高(囧),不过用的API到不是新面孔:

self.tableView.estimatedRowHeight = 213;self.tableView.rowHeight = UITableViewAutomaticDimension;

这里又不得不吐槽了,自动计算rowHeight跟estimatedRowHeight到底是有什么仇,如果不加上估算高度的设置,自动算高就失效了。

PS:iOS 8系统中rowHeight的默认值已经设置成了UITableViewAutomaticDimension,所以第二行代码可以省略。

问题:

  • 这个自动算高在push到下一个页面或者转屏时会出现高度特别诡异的情况,不过现在的版本修复了。
  • 求一个能让最低支持iOS 8的公司。

iOS 8抽风的算高机制

相同的代码在iOS 7和iOS 8上滑动顺畅程度完全不同,iOS 8莫名奇妙的卡。很大一部分原因是iOS 8上的算高机制大不相同,这是我做的小测试:


研究后发现这么多次额外计算有下面的原因:

  1. 不开启高度估算时,UITableView上来就要对所有cell调用算高来确定contentSize。
  2. dequeueReusableCellWithIdentifier:forIndexPath: 相比不带“forIndexPath”的版本会多调用一次高度计算。
  3. iOS 7计算高度后有“缓存”机制,不会重复计算;而iOS 8不论何时都会重新计算cell高度。

iOS 8把高度计算搞成这个样子,从WWDC也倒是能找到点解释,cell被认为随时都可能改变高度(如从设置中调整动态字体大小),所以每次滑动出来后都要重新计算高度。

说了这么多,究竟有没有既能省去算高烦恼,又能保证顺畅的滑动,还能支持iOS 6+的一站式解决方案呢?

UITableView+FDTemplateLayoutCell

使用UITableView+FDTemplateLayoutCell无疑是解决算高问题的最佳实践之一,既有iOS 8 self-sizing功能简单的API,又可以达到iOS7流畅的滑动效果,还保持了最低支持iOS6。

使用起来大概是这样:

#import <UITableView+FDTemplateLayoutCell.h>- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {    return [tableView fd_heightForCellWithIdentifier:@"identifer" cacheByIndexPath:indexPath configuration:^(id cell) {        // 配置 cell 的数据源,和 "cellForRow" 干的事一致,比如:        cell.entity = self.feedEntities[indexPath.row];    }];}

写完上面的代码后,你就已经使用到了:

  • 和每个UITableViewCell ReuseID一一对应的template layout cell
这个cell只为了参加高度计算,不会真的显示到屏幕上;它通过UITableView的-dequeueCellForReuseIdentifier: 方法lazy创建并保存,所以要求这个ReuseID必须已经被注册到了UITableView中,也就是说,要么是Storyboard中的原型cell,要么就是使用了UITableView的-registerClass:forCellReuseIdentifier:或-registerNib:forCellReuseIdentifier:其中之一的注册方法。
  • 根据autolayout约束自动计算高度
使用了系统在iOS 6就提供的API:-systemLayoutSizeFittingSize:
  • 根据index path的一套高度缓存机制
计算出的高度会自动进行缓存,所以滑动时每个cell真正的高度计算只会发生一次,后面的高度询问都会命中缓存,减少了非常可观的多余计算。
  • 自动的缓存失效机制
无须担心你数据源的变化引起的缓存失效,当调用如-reloadData,-deleteRowsAtIndexPaths:withRowAnimation:等任何一个触发 UITableView 刷新机制的方法时,已有的高度缓存将以最小的代价执行失效。如删除一个indexPath为[0:5]的cell时,[0:0] ~ [0:4]的高度缓存不受影响,而[0:5]后面所有的缓存值都向前移动一个位置。自动缓存失效机制对UITableView的9个公有API都进行了分别的处理,以保证没有一次多余的高度计算。
  • 预缓存机制
预缓存机制将在UITableView没有滑动的空闲时刻执行,计算和缓存那些还没有显示到屏幕中的cell,整个缓存过程完全没有感知,这使得完整列表的高度计算既没有发生在加载时,又没有发生在滑动时,同时保证了加载速度和滑动流畅性,下文会着重讲下这块的实现原理。

我们在设计这个工具的API时斟酌了非常长的时间,既要保证功能的强大,也要保证接口的精简,一行调用背后隐藏着很多功能。

这一套缓存机制能对滑动起多大影响呢?除了肉眼能明显的感知到外,我还做了个小测试。一个有54个内容和高度不同cell的table view,从头滑动到尾,再从尾滑动到头,iOS 8系统下,iPhone 6,使用Time Profiler监测算高函数所花费的时间:

  • 未使用缓存API、未使用估算,共花费877ms


  • 使用缓存API、开启估算,共花费77ms


测试数据的精度先不管,从量级上就差了一个数量级,说实话自己也没想到差距有这么大。

同时,工具也顺手解决了-preferredMaxLayoutWidth的问题,在计算高度前向contentView加了一条和table view宽度相同的宽度约束,强行让contentView内部的控件知道了自己父view的宽度,再反算自己被外界约束的宽度,破除“鸡生蛋蛋生鸡”的问题,这里比较tricky,就不展开说了。下面说说利用RunLoop预缓存的实现。

利用RunLoop空闲时间执行预缓存任务

FDTemplateLayoutCell的高度预缓存是一个优化功能,它要求页面处于空闲状态时才执行计算,当用户正在滑动列表时显然不应该执行计算任务影响滑动体验。

一般来说,这个功能要耦合UITableView的滑动状态才行,但这种实现十分不优雅且可能破坏外部的delegate结构,但好在我们还有RunLoop这个工具,了解它的运行机制后,可以用很简单的代码实现上面的功能。

空闲RunLoopMode

当用户正在滑动UIScrollView时,RunLoop将切换到UITrackingRunLoopMode接受滑动手势和处理滑动事件(包括减速和弹簧效果),此时,其他Mode(除NSRunLoopCommonModes这个组合Mode)下的事件将全部暂停执行,来保证滑动事件的优先处理,这也是iOS滑动顺畅的重要原因。

当UI没在滑动时,默认的Mode是NSDefaultRunLoopMode(同CF中的kCFRunLoopDefaultMode),同时也是CF中定义的“空闲状态Mode”。当用户啥也不点,此时也没有什么网络 IO时,就是在这个Mode下。

用RunLoopObserver找准时机

注册RunLoopObserver可以观测当前RunLoop的运行状态,并在状态机切换时收到通知:

  1. RunLoop开始
  2. RunLoop即将处理Timer
  3. RunLoop即将处理Source
  4. RunLoop即将进入休眠状态
  5. RunLoop即将从休眠状态被事件唤醒
  6. RunLoop退出

因为“预缓存高度”的任务需要在最无感知的时刻进行,所以应该同时满足:

  1. RunLoop处于“空闲”状态Mode;
  2. 当这一次RunLoop迭代处理完成了所有事件,马上要休眠时。

使用CF的带block版本的注册函数可以让代码更简洁:

CFRunLoopRef runLoop = CFRunLoopGetCurrent();CFStringRef runLoopMode = kCFRunLoopDefaultMode;CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopBeforeWaiting, true, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity _) {    // TODO here});CFRunLoopAddObserver(runLoop, observer, runLoopMode);

在其中的TODO位置,就可以开始任务的收集和分发了,当然,不能忘记适时的移除这个observer。

分解成多个RunLoop Source任务

假设列表有20个cell,加载后展示了前5个,那么开启估算后table view只计算了这5个的高度,此时剩下15个就是“预缓存”的任务,而我们并不希望这15个计算任务在同一个RunLoop迭代中同步执行,这样会卡顿UI,所以应该把它们分别分解到15个RunLoop迭代中执行,这时就需要手动向RunLoop中添加Source任务(由应用发起和处理的是Source 0任务)

Foundation层没对RunLoopSource提供直接构建的API,但是提供了一个间接的、既熟悉又陌生的API:

- (void)performSelector:(SEL)aSelector               onThread:(NSThread *)thr              withObject:(id)arg           waitUntilDone:(BOOL)wait                   modes:(NSArray *)array;

这个方法将创建一个Source 0任务,分发到指定线程的RunLoop中,在给定的Mode下执行,若指定的RunLoop处于休眠状态,则唤醒它处理事件,简单来说就是“睡你xx,起来嗨!”

于是,我们用一个可变数组装载当前所有需要“预缓存”的index path,每个RunLoopObserver回调时都把第一个任务拿出来分发:

NSMutableArray *mutableIndexPathsToBePrecached = self.fd_allIndexPathsToBePrecached.mutableCopy;CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopBeforeWaiting, true, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity _) {    if (mutableIndexPathsToBePrecached.count == 0) {        CFRunLoopRemoveObserver(runLoop, observer, runLoopMode);        return;    }    NSIndexPath *indexPath = mutableIndexPathsToBePrecached.firstObject;    [mutableIndexPathsToBePrecached removeObject:indexPath];    [self performSelector:@selector(fd_precacheIndexPathIfNeeded:)                 onThread:[NSThread mainThread]               withObject:indexPath            waitUntilDone:NO                    modes:@[NSDefaultRunLoopMode]];});

这样,每个任务都被分配到下个“空闲”RunLoop迭代中执行,其间但凡有滑动事件开始,Mode切换成UITrackingRunLoopMode,所有的“预缓存”任务的分发和执行都会自动暂定,最大程度保证滑动流畅。

本文原载于Sunny's Blog,作者:孙源(@我就叫Sunny怎么了),90后非主流iOS程序猿,现负责百度知道iOS团队。


CSDN移动将持续为您优选移动开发的精华内容,共同探讨移动开发的技术热点话题,涵盖移动应用、开发工具、移动游戏及引擎、智能硬件、物联网等方方面面,如果您有想分享的技术、观点,可通过电子邮件(tangxy#csdn.net,请把#改成@)投稿。

第一时间掌握最新移动开发相关信息和技术,请关注mobilehub公众微信号(ID: mobilehub)。


处理过程时的鼠标显示 -VB资料 窗口处理技巧大全 -VB资料 窗口事件的发生顺序-VB资料 VB创建不规则窗口 VB创建透明窗体 VB创建位图菜单 VB创建无 Icon 的窗口 打开 Win95 的创建快捷方式窗口-VB资料 打印机技巧 -VB资料 带有历史记录功能的菜单-VB资料 得知目前Mouse所指的Menu Item是哪一个-VB资料 调整 Combo 下拉部分的宽度 -VB资料 对象是使用 TAB 键还是鼠标激活的 -VB资料 防止用户编辑文本框控件中的内容-VB资料 改变 ListIndex而不发生 Click 事件 -VB资料 改变 TreeView 的背景 -VB资料 改变安装向导的背景色-VB资料 改变按钮的文本颜色-VB资料 功能强大的增强列表框 ――VB6控件LISTVIEW使用指南 关闭MDI窗体中所有的子窗体-VB资料 规范设计Windows应用软件菜单-VB资料 VB建立可滚动的视区 -VB资料 VB建立可下拉选择的属性 -VB资料 VB建立无模式窗口 -VB资料 将VB5中的ToolBar变成平面的 将程序从任务列表中隐藏 -VB资料 将我自已的命令加入窗体的控制菜单栏-VB资料 VB利用 UnloadMode 来控制窗体的卸载? VB利用Form_QueryUnload询问使用者是否关闭窗口 利用VB的图片框实现屏幕的滚动 利用VB动态改变Windows显示模式的两种方法 急急急急急!!!!!!!!!!,Band里的内容过长,如何让它自动的分页??? oicq的窗口title怎么为空了?如何得到句柄? session问题! 用dbgrideh控件,如何实现汇总结果的打印 有关ATL的最基础问题 怎样监控icmp包?用自编的程序 急急急急急!!!!!!!!!!,Band里的内容过长,如何让它自动的分页??? 使对话框最小化渐变到托盘区。 各位高手!!我用的是win2000工作组模式,为什么我能访问别人,而别人不能访问我呢??? 有关文件的读写问题。 大家讨论一下内存分配、释放的问题 关于在jsp中运用sqlserver的小问题!!! 怎么样可以将session的存活期限设置成“永远不死”?? 送分题,如何取得一个表的所有字段名? 那里可以下载Wrox系列书的英文版? 无法启动sqlserver的问题. DELPHI中dbgird的语句问题 关于控制动态产生Checkbox,并进行动态控问题 BCB中使用MSComm控件,无法定义动态数组变量,该怎么办?--急啊!在线等待 战神----巴蒂 两个问题! 用ADO访问DBASE数据库 百分求救:关于COM方法的reference参数不返回值的问题 请教各位高手 如何将程序中不同的查询结果写入报表? 怎样自编程序监控icmp包,应该截获什么消息,还是用别的方法? java本地化的问题(各位帮帮我!) 一个关于类型转换的菜鸟问题!! 招聘 Jpeg中FFd9可以有几个? ?????不同窗口中多个参数传递的问题,在线等待中........ 关于 EmbeddedWb 的Mshtml_tlb, SHDocVw_tlb 问题! 各位大虾 帮我翻译一下 急 悲... DBGrid为什么没有数据集记录失去焦点事件? 怎样手动和在程序中更改SEQUENCE的当前值? 怎么结贴子,什么叫FAQ? 关于“软件蓝领”的3个疑惑。 在函数中以引用方式调用结构的问题?给我能给的最高分100 怎样实现随机打印一个数据表的几个子段? 大家看看,这个功能如何实现? 怎样编一个程序读取服务器上的时间? VC工程不能正常打开? Jpeg中FFd9可以有几个? datawidgets简介!简单的问题---“送大伙一点小分” 新人到!!有问题想要问!!! 有谁知道sybase12.5中定义的所有保留字keywords? 为什么我的Tomcat突然找不到Serverlet了 关于报考程序员的问题 请问:一个应用程序发布时需要为每个发布的实例建立一个唯一标识用GUID好不好? 介绍些ActiveX/COM的好书吧! 1.用锤击钉,设木板对钉子的阻力跟钉子在木板中的深度成正比,每次击钉时锤子对钉子做的功相同,已知击第一次时,钉子进入板内1cm,求击第二次时,钉子能进入木板的深度.2.速度为v沿水平方向 怎么跑100米和200米啊?星期三就要比赛了. 地热供暖疑问《不热》我家是电厂统一供暖,5楼,83平 地热3组 把冷山墙.4楼6楼停暖 ,测量出水管温度37°现在室外温度-17°,我家室内温度15度左右,地热已经清洗.气也放了、过滤阀也清洗了.现在 c switch 问个 switch (i) 函数中case后面只能跟常量吗?可不可以这种写法case :i>1 && i=35 &&i 岩土的热物性是什么意思啊 致100米、200米、800米、4*200米运动员 关于c中switch的应用企业发放的奖金根据利润提成.利润I低于或等于100000元的,奖金可提10%;利润高于100000元但低于或等于200000元时,低于100000的按10%提成,高于100000的部分,可提成7.5%;200000 讨论非恒定加热功率以及出现断电的情况下怎么算岩土的热物性,探讨用非恒定的加热功率是否能缩短测试时间, 什么是实验室反应釜?它的作用是什么? 在一个2米高的圆形水池底部开个3厘米的孔装台微型水轮机来发电,能不能带动一台50至100W的发电机呀?我想用家里2米的水池来发电~~ 离子通道与钠钾泵的区别? 最近家里蚊子特别多怎么办哦!求大神帮助 溴化锂机组要多少溴化锂以 200W 大卡 为例 需要用多少溴化锂溶液 不锈钢反应釜的作用(放在烘箱里加热了) 海南的蚊子为什么那么毒?被叮一下就肿起一个大包,隔天都还会痒,讨厌死叻! 远大溴化锂机组报价、双良溴化锂机组报价、LG溴化锂机组报价、开利溴化锂机组报价、三菱溴化锂机组报价 position switch是什么意思 为什么澳大利亚的蚊子这么毒阿大神们帮帮忙 SWITCH的初步认识 code switch是什么意思 家里的蚊子.苍蝇从哪里来?如何才能彻底消灭它们?求大神帮助 如果你家中有乐器,观察一下它是怎样发出声音的,又是怎样改变音调和响度的?具体说一种乐器,具体点行吗?thanks! 吸收式制冷的工作原理是什么 第13频道至第68频道属于微波中的“分米波”,试着算出第68频道的波长范围就知道这个呼称的原因了. 自制乐器,要求能改变响度,音调,能发声.声音改变要比较明显今晚之前要知道 求热电致冷材料的工作原理!不过能不能再说清楚一点呢?n、p半导体我知道,可是原理我还是不清楚啊! 阅读短文,回答问题:为了满足人们对能源不断增长的需要,科学家们设想在空间建立太阳能电站来利用太阳能.这种空间太阳能电站建在地球同步轨道上,其工作过程如图所示.在太阳能收集 通常有几种乐器?每种乐器的发声体是什么?每种乐器怎样改变声调和响度 单层玻璃反应釜如何加热?有人知道吗? 光学波长频率的计算红光在水中的波长和绿光在真空中的波长相等,水对红光的折射率是4/3,则红光和绿光在水中的频率之比为?我要过程,和解释? 下列现象中属于做功使物体内能增加的是:A水蒸气将壶盖顶起 2用火炉给水加热 3钢条在水泥地上摩擦变热 4用太阳能热水器对水加热 单层玻璃反应釜的工作原理是什么啊? 微波属于?,但其波长较短,频率较高. 下列现象中,内能对外做功的是:A.冬天在户外时两手相搓一会儿就暖和B.刀在砂轮的高速摩擦下溅出火花C.在烈日之下柏油路面被晒熔化了D.火箭在“熊熊烈火”的喷射中冲天而起求详解一下D 单层玻璃反应釜的工作原理是什么啊? 书上说:“体系只做体积功而不做其他功时,恒容反应热等于体系热力学能的变化”.那我有个疑问,它说是只做"体积功",做体积功是要变体积的,但又说是"恒容反应热",但"恒容"又是不变体积的, switch 什么意思? 汽车无极变速器(CVT)它的原理是什么? Q(热量)、T(热力学温度)、V(系统体积)、W(功)、ρ(密度),其中属于状态函数的是 下列现象中,由于做功而使物体内能增加的有A,煮开水,水蒸气把壶盖顶起:B 打气筒打气时,筒壁会热起来C 用手来回弯折铁丝,弯折处会烫手D 用锯锯木头时,锯条会变热(简要说下理由,我觉得B 火电厂中什么气体可看作理想气体?什么气体可看作实际气体? 塑胶熔点与阻燃性的关系 下列现象中,利用做功改受内能的是A、晒太阳感到暖和 B、用蒸笼熘冷馒头C、感冒发烧,用冷毛巾敷额头D、插上电的电炉,炉丝烫了 卧式反应釜的加热系统如何做恩?说说啦 牧童这首诗中抓住什么进行描写 switch position是什么意思 应该是溶解度单位.还有这个Ncm3/g?不知道那个3时不是个上角标.n为什么要大写! 举例说明古今声调的关系(文字说明,从古声调至今声调,再从今声调至古声调) 神经细胞钾离子钠离子通道是什么 我400米59秒 800米2分20秒 请问我100米.200米正常应该跑多少?我400米59秒 800米2分20秒 请问我100米.200米正常应该跑多少?我14岁 175cm 60KG注 我短跑不是很厉害 家里地热的问题 供暖我家是复式的房子 家里都是地热 现在发现 一到供暖的时候 楼下的地热就自动过水 自动循环的流动 但是楼上的地热却是都是空气 把空气放出去 就能听到水在流动 钠钾离子通道是不是既有主动运输又有被动运输,那什么时候主动什么时候被动? 若不慎将油汤洒到衣服上,可以用什么办法出去,说明依据的原理-_-|||高一化学.. 离子浓度大小判断,D选项哪里出错呢?.A选项感觉也不对,A选项对吗?麻烦详细解释 蚊子闻到蚊香的气味,是晕?还是死? 汽车变速箱中的齿轮是一档齿轮大还是五档齿轮大? 什么是地热供暖 为什么蚊香熄灭后,蚊子又出现了?蚊香是驱赶蚊子还是杀灭蚊子? 鼓怎样改变音调 音调的单位是如何定义的
备案号:鲁ICP备13029499号-2 说三道四 www.s3d4.cn 说三道四技术文摘