在手机应用程序开发中,为了降低与服务端的交互次数,加快用户的响应速度,一般都会在iOS设备中加一个缓存的机制,前面一篇文章介绍了iOS设备的内存缓存。这篇文章将设计一个本地缓存的机制。

功能需求

这个缓存机制满足以下这些功能。

1、能够将数据缓存到本地磁盘。

2、能够推断一个资源是否已经被缓存。假设已经被缓存。在请求同样的资源。先到本地磁盘搜索。

3、能够推断文件缓存什么时候过期。这里为了简单起见这里,我们在请求url资源的时候。给每次请求的文件设定一个过期的时间。

4、能够实现:假设文件已经被缓存,并且没有过期。这将本地的数据返回,否则又一次请求url。

5、能够实现:假设文件下载不成功或者下载没有完毕,下次打开程序的时候,移除这些没有成功或者没有下载完毕的文件。

6、能够实现:同一时候请求或者下载多个资源。

设计实现:

1、设计一个CacheItem类,用来请求一个web连接,它的一个实例表示一个缓存项。

这个CacheItem类,须要一个url创建一个NSURLConnection,去请求web资源。

使用CacheItem类主要用来请求web资源。

[plain] view plaincopy
  1. /* ---------缓存项-------------- */
  2. @interface CacheItem : NSObject {
  3. @public
  4. id<CacheItemDelegate> delegate;
  5. //web地址
  6. NSString              *remoteURL;
  7. @private
  8. //是否正在下载
  9. BOOL                  isDownloading;
  10. //NSMutableData对象
  11. NSMutableData         *connectionData;
  12. //NSURLConnection对象
  13. NSURLConnection       *connection;
  14. }
  15. /* -------------------------- */
  16. @property (nonatomic, retain) id<CacheItemDelegate> delegate;
  17. @property (nonatomic, retain) NSString  *remoteURL;
  18. @property (nonatomic, assign) BOOL      isDownloading;
  19. @property (nonatomic, retain) NSMutableData *connectionData;
  20. @property (nonatomic, retain) NSURLConnection *connection;
  21. /* ----------開始下载方法----------- */
  22. - (BOOL) startDownloadingURL:(NSString *)paramRemoteURL;
  23. @end

2、在NSURLConnection開始请求之前,调用CachedDownloadManager类,来搜索和管理本地的缓存文件。将缓存文件的情况保存到一个字典类中。

这个字典设计例如以下:

[plain] view plaincopy
  1. {
  2. "http://www.cnn.com" =     {
  3. DownloadEndDate = "2011-08-02 07:51:57 +0100";
  4. DownloadStartDate = "2011-08-02 07:51:55 +0100";
  5. ExpiresInSeconds = 20;
  6. ExpiryDate = "2011-08-02 07:52:17 +0100";
  7. LocalURL = "/var/mobile/Applications/ApplicationID/Documents/
  8. httpwww.cnn.com.cache";
  9. };
  10. "http://www.baidu.com" =     {
  11. DownloadEndDate = "2011-08-02 07:51:49 +0100";
  12. DownloadStartDate = "2011-08-02 07:51:44 +0100";
  13. ExpiresInSeconds = 20;
  14. ExpiryDate = "2011-08-02 07:52:09 +0100";
  15. LocalURL = "/var/mobile/Applications/ApplicationID/Documents/
  16. httpwww.oreilly.com.cache";
  17. };
  18. }
 上面这个字典里面嵌套了字典。里面那层字典表示一个缓存项的缓存信息:下载结束时间、下载開始时间、缓存有效时间、缓存过期时间、缓存到本地的路径。

以下看下CachedDownloadManager类。

用它来实现和封装我们的缓存策略。

[plain] view plaincopy
  1. /* -----------CachedDownloadManager-------------- */
  2. @interface CachedDownloadManager : NSObject
  3. <CacheItemDelegate> {
  4. @public
  5. id<CachedDownloadManagerDelegate>  delegate;
  6. @private
  7. //记录缓存数据的字典
  8. NSMutableDictionary                *cacheDictionary;
  9. //缓存的路径
  10. NSString                           *cacheDictionaryPath;
  11. }
  12. @property (nonatomic, assign)
  13. id<CachedDownloadManagerDelegate> delegate;
  14. @property (nonatomic, copy)
  15. NSMutableDictionary *cacheDictionary;
  16. @property (nonatomic, retain)
  17. NSString *cacheDictionaryPath;
  18. /* 保持缓存字典 */
  19. - (BOOL) saveCacheDictionary;
  20. /* 公有方法:下载 */
  21. - (BOOL)         download:(NSString *)paramURLAsString
  22. urlMustExpireInSeconds:(NSTimeInterval)paramURLMustExpireInSeconds
  23. updateExpiryDateIfInCache:(BOOL)paramUpdateExpiryDateIfInCache;
  24. /* -------------------------- */
  25. @end

从上面代码能够看出,这个管理缓存的类中。有一个缓存字典:cacheDictionary,用来表示全部资源的缓存情况;cacheDictionaryPath用来表示缓存的路径。saveCacheDictionary用来将缓存字典归档到本地文件里。

download:urlMustExpireInSeconds:updateExpiryDateIfInCache是一个公共接口,通过传递url、缓存过期时间、是否更新缓存过期时间三个參数来方便的使用,实现我们的缓存策略。

3、假设这个文件已经被下载,并且没有过期。则从本地获取文件的数据。假设文件已经过期。则又一次下载。

我们通过download:urlMustExpireInSeconds:updateExpiryDateIfInCache方法来实现,主要看这种方法的代码:

[plain] view plaincopy
  1. /* ---------下载-------------- */
  2. - (BOOL)         download:(NSString *)paramURLAsString
  3. urlMustExpireInSeconds:(NSTimeInterval)paramURLMustExpireInSeconds
  4. updateExpiryDateIfInCache:(BOOL)paramUpdateExpiryDateIfInCache{
  5. BOOL result = NO;
  6. if (self.cacheDictionary == nil ||
  7. [paramURLAsString length] == 0){
  8. return(NO);
  9. }
  10. paramURLAsString = [paramURLAsString lowercaseString];
  11. //依据url。从字典中获取缓存项的相关数据
  12. NSMutableDictionary *itemDictionary =
  13. [self.cacheDictionary objectForKey:paramURLAsString];
  14. /* 使用以下这些变量帮助我们理解缓存逻辑 */
  15. //文件是否已经被缓存
  16. BOOL    fileHasBeenCached = NO;
  17. //缓存是否过期
  18. BOOL    cachedFileHasExpired = NO;
  19. //缓存文件是否存在
  20. BOOL    cachedFileExists = NO;
  21. //缓存文件是否能被载入
  22. BOOL    cachedFileDataCanBeLoaded = NO;
  23. //缓存文件数据
  24. NSData  *cachedFileData = nil;
  25. //缓存文件是否全然下载
  26. BOOL    cachedFileIsFullyDownloaded = NO;
  27. //缓存文件是否已经下载
  28. BOOL    cachedFileIsBeingDownloaded = NO;
  29. //过期时间
  30. NSDate    *expiryDate = nil;
  31. //下载结束时间
  32. NSDate    *downloadEndDate = nil;
  33. //下载開始时间
  34. NSDate    *downloadStartDate = nil;
  35. //本地缓存路径
  36. NSString  *localURL = nil;
  37. //有效时间
  38. NSNumber  *expiresInSeconds = nil;
  39. NSDate    *now = [NSDate date];
  40. if (itemDictionary != nil){
  41. fileHasBeenCached = YES;
  42. }
  43. //假设文件已经被缓存。则从缓存项相关数据中获取相关的值
  44. if (fileHasBeenCached == YES){
  45. expiryDate = [itemDictionary
  46. objectForKey:CachedKeyExpiryDate];
  47. downloadEndDate = [itemDictionary
  48. objectForKey:CachedKeyDownloadEndDate];
  49. downloadStartDate = [itemDictionary
  50. objectForKey:CachedKeyDownloadStartDate];
  51. localURL = [itemDictionary
  52. objectForKey:CachedKeyLocalURL];
  53. expiresInSeconds = [itemDictionary
  54. objectForKey:CachedKeyExpiresInSeconds];
  55. //假设下载開始和结束时间不为空,表示文件所有被下载
  56. if (downloadEndDate != nil &&
  57. downloadStartDate != nil){
  58. cachedFileIsFullyDownloaded = YES;
  59. }
  60. /* 假设expiresInSeconds不为空。downloadEndDate为空。表示文件已经正在下载 */
  61. if (expiresInSeconds != nil &&
  62. downloadEndDate == nil){
  63. cachedFileIsBeingDownloaded = YES;
  64. }
  65. /* 推断缓存是否过期 */
  66. if (expiryDate != nil &&
  67. [now timeIntervalSinceDate:expiryDate] > 0.0){
  68. cachedFileHasExpired = YES;
  69. }
  70. if (cachedFileHasExpired == NO){
  71. /* 假设缓存文件没有过期,载入缓存文件,而且更新过期时间 */
  72. NSFileManager *fileManager = [[NSFileManager alloc] init];
  73. if ([fileManager fileExistsAtPath:localURL] == YES){
  74. cachedFileExists = YES;
  75. cachedFileData = [NSData dataWithContentsOfFile:localURL];
  76. if (cachedFileData != nil){
  77. cachedFileDataCanBeLoaded = YES;
  78. } /* if (cachedFileData != nil){ */
  79. } /* if ([fileManager fileExistsAtPath:localURL] == YES){ */
  80. [fileManager release];
  81. /* 更新缓存时间 */
  82. if (paramUpdateExpiryDateIfInCache == YES){
  83. NSDate *newExpiryDate =
  84. [NSDate dateWithTimeIntervalSinceNow:
  85. paramURLMustExpireInSeconds];
  86. NSLog(@"Updating the expiry date from %@ to %@.",
  87. expiryDate,
  88. newExpiryDate);
  89. [itemDictionary setObject:newExpiryDate
  90. forKey:CachedKeyExpiryDate];
  91. NSNumber *expires =
  92. [NSNumber numberWithFloat:paramURLMustExpireInSeconds];
  93. [itemDictionary setObject:expires
  94. forKey:CachedKeyExpiresInSeconds];
  95. }
  96. } /* if (cachedFileHasExpired == NO){ */
  97. }
  98. if (cachedFileIsBeingDownloaded == YES){
  99. NSLog(@"这个文件已经正在下载...");
  100. return(YES);
  101. }
  102. if (fileHasBeenCached == YES){
  103. if (cachedFileHasExpired == NO &&
  104. cachedFileExists == YES &&
  105. cachedFileDataCanBeLoaded == YES &&
  106. [cachedFileData length] > 0 &&
  107. cachedFileIsFullyDownloaded == YES){
  108. /* 假设文件有缓存并且没有过期 */
  109. NSLog(@"文件有缓存并且没有过期.");
  110. [self.delegate
  111. cachedDownloadManagerSucceeded:self
  112. remoteURL:[NSURL URLWithString:paramURLAsString]
  113. localURL:[NSURL URLWithString:localURL]
  114. aboutToBeReleasedData:cachedFileData
  115. isCachedData:YES];
  116. return(YES);
  117. } else {
  118. /* 假设文件没有被缓存。获取缓存失败 */
  119. NSLog(@"文件没有缓存.");
  120. [self.cacheDictionary removeObjectForKey:paramURLAsString];
  121. [self saveCacheDictionary];
  122. } /* if (cachedFileHasExpired == NO && */
  123. } /* if (fileHasBeenCached == YES){ */
  124. /* 去下载文件 */
  125. NSNumber *expires =
  126. [NSNumber numberWithFloat:paramURLMustExpireInSeconds];
  127. NSMutableDictionary *newDictionary =
  128. [[[NSMutableDictionary alloc] init] autorelease];
  129. [newDictionary setObject:expires
  130. forKey:CachedKeyExpiresInSeconds];
  131. localURL = [paramURLAsString
  132. stringByAddingPercentEscapesUsingEncoding:
  133. NSUTF8StringEncoding];
  134. localURL = [localURL stringByReplacingOccurrencesOfString:@"://"
  135. withString:@""];
  136. localURL = [localURL stringByReplacingOccurrencesOfString:@"/"
  137. withString:@"{1}quot;];
  138. localURL = [localURL stringByAppendingPathExtension:@"cache"];
  139. NSString *documentsDirectory =
  140. [self documentsDirectoryWithTrailingSlash:NO];
  141. localURL = [documentsDirectory
  142. stringByAppendingPathComponent:localURL];
  143. [newDictionary setObject:localURL
  144. forKey:CachedKeyLocalURL];
  145. [newDictionary setObject:now
  146. forKey:CachedKeyDownloadStartDate];
  147. [self.cacheDictionary setObject:newDictionary
  148. forKey:paramURLAsString];
  149. [self saveCacheDictionary];
  150. CacheItem *item = [[[CacheItem alloc] init] autorelease];
  151. [item setDelegate:self];
  152. [item startDownloadingURL:paramURLAsString];
  153. return(result);
  154. }

4、以下我们设计缓存项下载成功和失败的两个托付方法:

[plain] view plaincopy
  1. @protocol CacheItemDelegate <NSObject>
  2. //下载成功运行该方法
  3. - (void) cacheItemDelegateSucceeded
  4. :(CacheItem *)paramSender
  5. withRemoteURL:(NSURL *)paramRemoteURL
  6. withAboutToBeReleasedData:(NSData *)paramAboutToBeReleasedData;
  7. //下载失败运行该方法
  8. - (void) cacheItemDelegateFailed
  9. :(CacheItem *)paramSender
  10. remoteURL:(NSURL *)paramRemoteURL
  11. withError:(NSError *)paramError;
  12. @end

当我们下载成功的时候,改动缓存字典中的下载时间,表示已经下载完毕。并且须要将请求的资源数据缓存到本地:

[plain] view plaincopy
  1. //缓存项的托付方法
  2. - (void) cacheItemDelegateSucceeded:(CacheItem *)paramSender
  3. withRemoteURL:(NSURL *)paramRemoteURL
  4. withAboutToBeReleasedData:(NSData *)paramAboutToBeReleasedData{
  5. //从缓存字典中获取该缓存项的相关数据
  6. NSMutableDictionary *dictionary =
  7. [self.cacheDictionary objectForKey:[paramRemoteURL absoluteString]];
  8. //取当前时间
  9. NSDate *now = [NSDate date];
  10. //获取有效时间
  11. NSNumber *expiresInSeconds = [dictionary
  12. objectForKey:CachedKeyExpiresInSeconds];
  13. //转换成NSTimeInterval
  14. NSTimeInterval expirySeconds = [expiresInSeconds floatValue];
  15. //改动字典中缓存项的下载结束时间
  16. [dictionary setObject:[NSDate date]
  17. forKey:CachedKeyDownloadEndDate];
  18. //改动字典中缓存项的缓存过期时间
  19. [dictionary setObject:[now dateByAddingTimeInterval:expirySeconds]
  20. forKey:CachedKeyExpiryDate];
  21. //保存缓存字典
  22. [self saveCacheDictionary];
  23. NSString *localURL = [dictionary objectForKey:CachedKeyLocalURL];
  24. /* 将下载的数据保持到磁盘 */
  25. if ([paramAboutToBeReleasedData writeToFile:localURL
  26. atomically:YES] == YES){
  27. NSLog(@"缓存文件到磁盘成功.");
  28. } else{
  29. NSLog(@"缓存文件到磁盘失败.");
  30. }
  31. //运行缓存管理的托付方法
  32. [self.delegate
  33. cachedDownloadManagerSucceeded:self
  34. remoteURL:paramRemoteURL
  35. localURL:[NSURL URLWithString:localURL]
  36. aboutToBeReleasedData:paramAboutToBeReleasedData
  37. isCachedData:NO];
  38. }
假设下载失败我们须要从缓存字典中移除改缓存项:
[plain] view plaincopy
  1. //缓存项失败失败的托付方法
  2. - (void) cacheItemDelegateFailed:(CacheItem *)paramSender
  3. remoteURL:(NSURL *)paramRemoteURL
  4. withError:(NSError *)paramError{
  5. /* 从缓存字典中移除缓存项,并发送一个托付 */
  6. if (self.delegate != nil){
  7. NSMutableDictionary *dictionary =
  8. [self.cacheDictionary
  9. objectForKey:[paramRemoteURL absoluteString]];
  10. NSString *localURL = [dictionary
  11. objectForKey:CachedKeyLocalURL];
  12. [self.delegate
  13. cachedDownloadManagerFailed:self
  14. remoteURL:paramRemoteURL
  15. localURL:[NSURL URLWithString:localURL]
  16. withError:paramError];
  17. }
  18. [self.cacheDictionary
  19. removeObjectForKey:[paramRemoteURL absoluteString]];
  20. }
5、载入缓存字典的时候,我们能够将没有下载完毕的文件移除:
[plain] view plaincopy
  1. //初始化缓存字典
  2. NSString *documentsDirectory =
  3. [self documentsDirectoryWithTrailingSlash:YES];
  4. //生产缓存字典的路径
  5. cacheDictionaryPath =
  6. [[documentsDirectory
  7. stringByAppendingString:@"CachedDownloads.dic"] retain];
  8. //创建一个NSFileManager实例
  9. NSFileManager *fileManager = [[NSFileManager alloc] init];
  10. //推断是否存在缓存字典的数据
  11. if ([fileManager
  12. fileExistsAtPath:self.cacheDictionaryPath] == YES){
  13. NSLog(self.cacheDictionaryPath);
  14. //载入缓存字典中的数据
  15. NSMutableDictionary *dictionary =
  16. [[NSMutableDictionary alloc]
  17. initWithContentsOfFile:self.cacheDictionaryPath];
  18. cacheDictionary = [dictionary mutableCopy];
  19. [dictionary release];
  20. //移除没有下载完毕的缓存数据
  21. [self removeCorruptedCachedItems];
  22. } else {
  23. //创建一个新的缓存字典
  24. NSMutableDictionary *dictionary =
  25. [[NSMutableDictionary alloc] init];
  26. cacheDictionary = [dictionary mutableCopy];
  27. [dictionary release];
  28. }
   
这样就基本上完毕了我们须要的功能。以下看看我们怎样使用我们设计的缓存功能。

样例场景:

我们用一个UIWebView来显示stackoverflow这个站点,我们在这个站点的内容缓存到本地20秒,假设在20秒内用户去请求该站点,则从本地文件里获取内容,否则过了20秒。则又一次获取数据,并缓存到本地。

在界面上拖放一个button和一个webview控件,例如以下图。

这样我们能够非常方便使用前面定义好的类。

我们在viewDidLoad 中实例化一个CachedDownloadManager,并设置它的托付为self。

当下载完毕的时候。运行CachedDownloadManager的下载成功的托付方法。

- (void)viewDidLoad {[super viewDidLoad];  [self setTitle:@"本地缓存測试"];CachedDownloadManager *newManager = [[CachedDownloadManager alloc] init];self.downloadManager = newManager;[newManager release];[self.downloadManager setDelegate:self];}

在button的点击事件中增加以下代码,请求stackoverflow :

  static NSString *url = @"http://stackoverflow.com";[self.downloadManager download:urlurlMustExpireInSeconds:20.0fupdateExpiryDateIfInCache:YES];

上面的代码表示将这个stackoverflow的缓存事件设置为20s。而且假设在20s内有同样的请求,则从本地获取stackoverflow的内容数据。updateExpiryDateIfInCache设置为yes表示:在此请求的时候。缓存时间又更新为20s。类似我们的session。假设设置成no。则第一次请求20s之后。该缓存就过期。

请求完毕之后会运行CachedDownloadManager的托付方法。我们将数据展示在uiwebview中,代码例如以下:

- (void) cachedDownloadManagerSucceeded:(CachedDownloadManager *)paramSenderremoteURL:(NSURL *)paramRemoteURLlocalURL:(NSURL *)paramLocalURLaboutToBeReleasedData:(NSData *)paramAboutToBeReleasedDataisCachedData:(BOOL)paramIsCachedData{  [webview loadData:paramAboutToBeReleasedData MIMEType:@"text/html" textEncodingName:@"UTF-8" baseURL:[NSURL URLWithString:@"http://stackoverflow.com"]];
}

这样我们就实现了20s的缓存。

效果:

第一次点击測试button:

20s内点击button,程序就从本地获取数据。比較高速的就显示出该网页了。

总结:

本文通过代码和实例设计了一个iPhone应用程序本地缓存的方案。

当然这个方法不是最好的,假设你有更好的思路。欢迎告诉我。

设计一个移动应用的本地缓存机制相关推荐

  1. 如何设计一个牛逼的本地缓存

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 来源:ksfzhaohui juejin.im/post/5dd9 ...

  2. 如何设计一个牛逼的本地缓存!

    来源:ksfzhaohui | http://dwz.win/Ws4 最近在看Mybatis的源码,刚好看到缓存这一块,Mybatis提供了一级缓存和二级缓存:一级缓存相对来说比较简单,功能比较齐全的 ...

  3. 我设计了一个牛逼的本地缓存!

    点击上方 好好学java ,选择 星标 公众号 重磅资讯.干货,第一时间送达 今日推荐:硬刚一周,3W字总结,一年的经验告诉你如何准备校招! 个人原创100W+访问量博客:点击前往,查看更多 作者:k ...

  4. 多任务学习,如何设计一个更好的参数共享机制?| AAAI 2020

    2019-12-26 05:44:43 作者 | 孙天祥 编辑 | 刘萍 原文标题:稀疏共享:当多任务学习遇见彩票假设 本文介绍了复旦大学邱锡鹏团队在AAAI 2020 上录用的一篇关于多任务学习的工 ...

  5. GUAVA本地缓存01_概述、优缺点、创建方式、回收机制、监听器、统计、异步锁定

    文章目录 ①. 本地缓存 - 背景 ②. 本地缓存 - 优缺点 ③. Guava Cache介绍 ④. Guava - 三种创建方式 ⑤. Guava - 如何回收缓存 ⑥. Guava - 移除监听 ...

  6. 本地缓存-loadingCache

    本地缓存 缓存大概是一个不能再熟悉的话题,今天的主要内容是分享一下我们公司使用比较多的本地缓存 loadingcache,同时也是自己使用过程中的一些探索的分享,其背后的架构其实就是Guava cac ...

  7. 实现 Java 本地缓存

    缓存,我相信大家对它一定不陌生,在项目中,缓存肯定是必不可少的.市面上有非常多的缓存工具,比如 Redis.Guava Cache 或者 EHcache.对于这些工具,我想大家肯定都非常熟悉,所以今天 ...

  8. java几点钟_实现 Java 本地缓存,该从这几点开始

    缓存,我相信大家对它一定不陌生,在项目中,缓存肯定是必不可少的.市面上有非常多的缓存工具,比如 Redis.Guava Cache 或者 EHcache. 对于这些工具,我想大家肯定都非常熟悉,所以今 ...

  9. Guava 本地缓存使用教程

    文章目录 准备工作 创建缓存 使用缓存 Cache 读取缓存 LoadingCache 读取缓存 修改缓存 Cache 修改缓存 LoadingCache 修改缓存 其他方法 在前面的文章 Sprin ...

最新文章

  1. Imc连环画《红楼梦》
  2. 2018.11.09 codeforces487E. Tourists(tarjan+树链剖分)
  3. Linux下svn新建用户,Linux下建立svn工程
  4. codematic2连接mysql失败_codematic2.rar
  5. 高级数据结构与算法 | 回溯算法(Back Tracking Method)
  6. android编译的错误日志,android编译遇到错误
  7. Android View框架总结(八)ViewGroup事件分发机制
  8. 串口通讯WaitCommEvent 、GetLastError、ClearCommError、...
  9. 【环境搭建】zip 分卷压缩
  10. python删除第一行_python学习之删除DataFrame某一行/列内容
  11. 读《Machine Learning in Action》的感想
  12. 李航·《统计学习方法》学习笔记
  13. 关于BufferedOutputStream的flush方法
  14. Java应用性能分析工具:async-profiler
  15. 差分进化算法_想用遗传算法?来看看这些已为你做好的开源优化框架
  16. gitbook 使用粘自csdn
  17. 罗马音平假名中文可复制_如何快速有效地学习日语五十音?
  18. python高级编程(6) - 对象引用,可变性和垃圾和回收
  19. 网件 R6400 TTL 救砖详细 教程
  20. PPt2007制作三维立体字效果教程

热门文章

  1. error while loading shared libraries: libgconf-2.so.4
  2. 获取windows所有端口
  3. 找到bug的根源,问五次为什么
  4. [leetcode]687. Longest Univalue Path
  5. lvs + keepalive的安装配置
  6. Web中常用字体介绍(转)
  7. 解决ng界面长表达式(ui-set)
  8. 在VS2010中F5调试Silverlight程序时,提示“无法启动调试,找不到Microsoft Internet Explorer”...
  9. SYSTEM32 下的几乎所有文件的简单说明
  10. 最新!全球学术排名出炉:22所中国大学位居世界100强