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

优化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)。


接手Android后首次接受媒体采访 Sundar Pichai谈Android未来 C语言实现二分法查找 让开发者高效编程的10个新框架 Google Analytics中显示访客停留时间为“0”的秘密 大数据之惑 麻省理工三位物理学家自建数据库服务Cloudant 融资1200万美元 网页浏览器进入再造新时代 AMD揭露HSA运算架构新技术hUMA细节 美团悄然上线云主机服务:与亚马逊殊途同归,还是另有谋划? 微软发布deep-tech团队招募令,期望获得下一代开发者的支持 2013谷歌I/O开发者大会:发布多个软件产品和服务 C语言实现二分法求解方程 开发测试云与虚拟化解决方案 学苹果?微软淘汰Points支付系统改用Gift cards 甲骨文改变Java版本号命名方式 [CTO俱乐部第97期]中科院、腾讯、深圳明源研发团队管理经验分享 Android Studio来了,它能取代Eclipse吗? Google Play Game不够开放,被OpenKit炮轰 Google Play Game Service详解 不仅提供API更提供后端支持 IBM Power走向Linux,是必然的! 美国国家安全局解密特工培训手册:如何利用谷歌等大众搜索工具搜集保密数据 微软IE10如何保护上网安全(大图) 谷歌无畏苹果的理由:强力的算法和基础设施架构 开发者热议异构统一内存访问技术(hUMA) 千万级并发实现的秘密:内核不是解决方案,而是问题所在! Apache RewriteRule重写规则语法入门 IDC:2013 Q1 Android和iOS出货量占92% WP首超黑莓 传雅虎欲10亿美元收购轻博客网站Tumblr A Beautiful Mess:如何赚男人女人的钱 CSS、JavaScript开发者必备的10款最佳工具 IBM推出API管理平台 抢占API经济市场份额 delphi查询问题(急需) 急求:从硬盘安装windowsxp时电脑出显不能安装,找不到eula!如何解决? 系统分析员VS博士 C#连接Oracle? 关于<BASE> 那里有WebDB下载啊. 在ListCtrl里加类似Combo Box的东西,能实现吗? windows2000下vb的一个小问题~~ 如何把当前路径加入查找路径中?立即给分! 对选用不同的纸张大小,在预览中能看出效果吗? 请问怎么在PB6.0中实现Foxpro(*.dbf)的数据导入,到ASA库中 j2me里怎么才能实现图象的滤镜效果?waiting... CDbSet m_dbSet(NULL)在哪一步才能得到一个CDatabase对象的错针? 新手提问:“镜象”是什么意思 求教,如何配置使性能达到最佳,详情见文 怎样在广域网中提高传屏响应速度?帮帮忙!急啊. 在文件钩子当中如何判断是打开文件操作,还是创建文件操作 怎样在applet程序中显示一幅jpeg图片?该图片存在于c:\image\下。 去年12月是谁发的帖子“做项目经理难么” 关于图层拖拽的问题 我有一块硬盘,迈托4.3G,开格式化分时说错误设备或0磁道损坏,后来fdisk分区,启动无效 这是个什么错误啊~ 第一次做控件不明白 给点指点啊 急求:从硬盘安装windowsxp时电脑出显不能安装,找不到eula!如何解决? 不用的控件怎样从工程中删除? 怎样将一个表中的全部数据追加到别一个表的后面????在线 我做软件这些年 求scjd的模拟题 为什么我连续给一个socket send三次,socket一次就接收完了? 关于HTML代码不区分大小写? 大家看一下这个CListCtrl是啥回事?? Turbo c++3.0怎么使用? 《水园MSN俱乐部宗旨》 ODBC连接数据库的一个问题求教 如何让网页一打开就是全屏模式? 关于打印的问题 怎么彻底卸载linux啊!放分100 如何移动无模式窗体? 一个网络版软件的共享使用问题 在线等待!!! 一个网络版软件的共享使用问题 98怎么与xp不能连网玩游戏?高分送!! 有个问题请大虾帮帮忙! 我该怎么办呀??? ★怎样让窗口的resize在某一指定的范围内? 笔记本问题~ TURBO C 中显示鼠标和图形方式的问题(看我的源代码) ACCESS中的SQL语句不支持limit语法吗?~~急问啊~~~ 为什么会这样? 我在网上下了个C1TrueDBGrid 不知怎么用?有没有什么限制?请各位帮忙? 只有这里可以发发牢骚了,斑竹别删 100分(不够可以再加!)求《PC游戏编程--人机博弈》光盘源代码!!在线等 单独的一个数字到底有没有系数啊?为什么? 做紫外光谱时吸收峰一般在什么范围 piece和bar的区别 单独一个数字比如7有没有系数?如果有,是什么?单独一个字母比如a的系数是1吗? 食物油易溶于哪种物质 bar用于长方形物体那piece呢? 1—丁炔最简式是什么? 氨气是否极易溶于油 苏丹国家的气候情况 这个数的系数 硝酸铵能否极易溶于油? 请问苏丹的气候条件真的很恶劣吗?国人能适应吗? 丙炔和H2组成的混合气体5L,在催化剂的作用下充分反映后得到的气体体积及为V升,均在标况下测定.问若H2在混合气体中的体积比值为X,计算X在不同值时反应后气体的总体积V(用X的代数式表示 摩尔庄园求死党~求家族~我米米号658743 苏丹是哪个半球,什么人种,什么语言,什么宗教,什么气候类型? 15岁的女学生经常流白色液体对身体好吗 摩尔庄园 替我玩、、、 十万分之一天平,该选哪个好呢? 总是产生幻觉不知道什么原因? 摩尔庄园的问题我是09年的老玩家,在一直没玩过,今天登陆后发现什么都变了,现在清玩家们指示一下我该做些什么,真的很怀念,想重新玩,还有需要开超级拉姆吗?今天回家看见09年的脚印想哭 精确到0.1mg的天平到底应该使用什么样的天平?不知道用万分之一的还是十万分之一的?原理是什么?如果是十万分之一,最低和最高称量多少克呢? 确定一个药品的吸收系数为什么要有这么多的要求 酒精和氯有什么反应 用一道题及解答来介绍什么是模糊数学 转换大师怎么变药剂大师 茶液和酒精混合在一起有什么反应? 一些较浅的伤口破了之后,不流血,流一些透明的液体,基本上不粘,怎么了?有两个月了吧,混身上下都是 紫外线在生活中主要应用在那些方方面面? 是否有氢氧化硫 氢氧化氮 一说? 伤口处的透明液体是什么我的小拇指根手背处蹭破了皮,在蹭伤中应该算是深的了,血没流很多(没到可以滴的程度),但却有白色透明的液体在伤口上(不要忽悠我是血浆,水,我生物成绩很好 生活中哪些地方用到了红外线,紫外线 洛伦兹曲线与基尼系数是如何体现收入分配的平等程度的 梅特勒-托利多AG135分析天平价格多少? 羧酸在碱的存在下,水解反应产物是什么?酸的存在呢?碱的存在下呢? 收入分配的平等可以用哪些标准衡量( ) A.劳动分配率 B.洛伦斯曲线和基尼系数 C.工资的差异率 D.以上都 现在要用高效液相色谱法测定水样中残留的抗生素 要有水样前处理 实验要有水样前处理 实验 和要做出什么数据和图表 油酯属于-------类化合物它可以看成是-------和------发生酯化反应的产物.油酯在酸或碱存在的条件下发生水解反应.其中碱性条件下水解反应被称为-------反应 求教洛伦兹曲线与基尼系数的区别和联系,详细一点的. 什么是参考企业比较法? 为什么冬天和夏天的温差那么大,又为什么冬天的太阳不是很热,而夏天的太阳热的受不了.种种的这些是由什么造成的,要有科学而缜密的解释. 微观经济学中,基尼系数如何衡量收入分配的状况 梅特勒电子天平十万分之一什么是一分钟后读数吗在高原海拔4000左右,温度一般十几度湿度冬天的话也20以下.用着感觉很不稳定,一开始我们是把要称的样品放在上面等小圆圈消失后等一分钟 九九文章网怎么打不开 为什么饱和烷烃的偶极矩为0 请问肯尼亚的气候怎么样?南苏丹呢? 我怎么打开我保存到草稿箱里的文章啊?我从《世界之窗》进入“新浪”,写了一篇文章但没写完,就保存到草稿箱里了.过后又来写,完事后就怎么也不能把后来的文章保存到草稿箱里去了.用鼠 唾液淀粉酶在经过高温后还有效吗?比如说将唾液放在90°的高温下加热,然后让它冷了以后还能有用吗,还能再消化淀粉吗? 《美研究显示:现有技术无法克隆人》 阅读题 在新浪博客如何把文章保存在草稿箱里? 地沟油加工成化工原料和柴油?是真是假啊 ··我朋友让我帮他收购类 ··谁愿意合作`? 生殖性克隆不安全,应该禁止与美研究显示:现有技术无法克隆人之间是什么关系 生殖 植物细胞壁是否含有淀粉 下列事物不是利用紫外线辐射的是夏天外出戴太阳镜 将衣服、被褥等放到太阳光下暴晒紫外线诱蝇灯诱杀害虫 验钞机识别伪钞 水中氧元素的化合价+2用化学式表示 不是说女人高潮流出来的是透明液体吗?怎么我女朋友流出来的前面有点透明,后面是乳白色的呢? 300万吨的地沟油可以制作多少柴油 美的电压力锅水槽里没水,真气水在锅口流不了? 炔类燃烧通式?单单是炔类! 有关紫外光谱吸收峰强度的问题我做了紫外光谱的光谱扫描 发现有个样品的吸收峰Abs达到了1.4 想问一下这个光谱是否符合要求? 一般做光谱扫描的时候是不是要控制Abs小于1? 如果大于1会有 电离方程式是O++OH-=H2O的化学方程式→_→怎么可能没有 你认真点儿……不会的话就别来误导我…… 纽约油价1日下跌北京一周三发重污染预警 13省市雾霾北京将迎冷空气救场驱雾霾 北风吹出蓝洛杉矶机场枪击案1人死亡多人受伤 航巴基斯坦塔利班首领在美军无人机空袭中美国洛杉矶国际机场发生枪击事件洛杉矶国际机场发生枪击事件 嫌犯被执墨西哥对垃圾食品征税8%英拟对外国购房者增税补赤字美贩毒隧道铺轻轨尼加拉瓜发布 登革热红色预警泰下院争议中 通过特赦法案洛杉矶国际机场发生枪击事件 嫌犯被执广西岑溪炮竹厂爆炸已致11死17伤斯诺登密会德议员 称愿为NSA窃听事广西炮竹厂爆炸死亡人数升至11人 责俄总理:美国窃听外国领导人的行为“无广西炮竹厂爆炸死者家属:听到爆炸声意莫斯科市民发起“落叶保卫战”F-35首射空空导弹182米高印开建世界最高塑像罗伯森因右膝扭伤将缺席至少三周关注血制品企业和创新型药企 五股成长三峡水利年报拟高送转10转20A股震荡整理出现终极变盘 迟迟不攻透曙光股份2015年净利润同比预增10早评:资金离场再提速 基金重仓股寻机多部门酝酿建立产能过剩退出通道 八只知乎女神导演大骗局 论互联网权威下大抢占汽车电商风口 一猫打造三大商业场敲响C2C的丧钟?国家终于表态了扒一扒 P2P网贷让人“眼红”的高收为啥你创业一年融不到5百万,平安好医星巴克的Teavena茶吧5家关了4三公司正面交火 互联网保险业群雄逐鹿Pin4双重识别码认证技术今夏走向全资产端比拼 各家有各家的高招自融和关联担保是否合规?专业律师帮您P2P不仅是金融问题 亟需加以规范各类金融机构的杠杆率都有啥规定?互联网时代的金融创新跨平台债权转让二级市场的思路与出路
备案号:鲁ICP备13029499号-2 说三道四 www.s3d4.cn 说三道四技术文摘