每一代 iOS 都会为全球的开发者们带来新鲜的“小玩意儿”和对现有技术进行提升。显然,最新的 iOS 9 也不例外,开发者们拥有了全新的框架和 APIs 以方便调用、这可以显著地提升应用程序的水平。Core Spotlight 框架就是其中之一,它包含了许多优秀 APIs,开发者可以很方便地应用在工程中。

Core Spotlight(CS)框架属于一个更大的 API 集合 Search APIs,它让开发者们可以地将应用变得更容易被发现,以及访问起来更加便利。这在以前的 iOS 版本里是不可想象的。Search APIs 让用户和应用之间的联系更加紧密。用户可以更迅速地访问应用,同时应用也能更主动及时地响应用户。除 Core Spotlight 以外,iOS 9 其他新的搜索功能还包括(仅供参考):

  1. NSUserActivity 类的新方法和属性(负责保存应用的状态以便稍后恢复)。
  2. web markup 让网页的内容在设备上可被搜索。
  3. universal links 允许从网页内容里的链接直接打开应用。

我们不会在这篇文章里讨论以上三项,但会详细地介绍 Core Spotlight 框架。但开始之前,我们先来搞清楚这个框架的用途。

Core Spotlight 框架让应用里的数据在 Spotlight 可搜索,然后把与应用相关的搜索结果与系统返回的其他结果一同展示出来。这令人印象深刻并具有革命性,因为这是用户首次可以搜索到除 Apple 官方应用外、任意应用中的数据,然后与之进行交互。用户可以与自定义应用的相关搜索结果进行交互的意思是:不但在搜索结果项被选中时会自动启动应用,而且开发者们也能引导用户跳转到特定视图控制器,用来展示 Spotlight 中被选择的数据。

从开发者的角度看来,集成 Core Spotlight 框架和使用它的 API 并不复杂。正如本教程随后会介绍的那样,只需要几行代码就能搞定。整个过程的重点在于开发者需要“请求” iOS 去索引他们应用里的数据,并且这些数据必须预先以特定的方式来表示。

鉴于这是一篇关于 Core Spotlight 框架的教程,我不打算在简介部分过于详细。如果你有兴趣学习如何实现一些我个人觉得非常棒的功能,那么请继续阅读。我相信,当你读完之后,就能很轻松地让你的应用支持 Spotlight 搜索。

关于示例应用

为了深入剖析本节主题的细节部分,我们还是一如既往的借助实例应用来研究。在本教程,我们的应用会展示一系列的数据,这些数据能在设备(或者模拟器)的 Spotlight 中搜索到。尽管这只是一个大的蓝图,但再补充一下应用程序的细节也是很必要的。

我们的示例应用展示了一些电影及相关信息,例如简介、导演、演员、评价,等等。所有的电影数据会展示在 tableView 里,当点击某一行时,被选中电影的详情会展示在一个新的视图控制器里。没有更复杂的功能了,这种功能和数据就足以让我们了解 Core Spotlight API 是如何工作的。再补充一点,我们数据的来源是 IMDB,我是从这里获取示例数据的。

你可以先看看下面的动图,大致对这个示例应用有个初步印象

这个教程里我们有两个目标: 最首要的是在 Spotlight 中能搜索到应用的所有电影数据。这样,当用户搜索关键词时,应用中涉及到该电影相关的数据会展示出来。设置这些关键词也是稍后的工作之一,因为定义它们(关键词)也是我们的职责。

点击搜索的电影结果,会启动应用,接着我们来完成第二个目标。如果什么都不做,就会加载默认的视图控制器并呈现给用户,在我们的例子里就是那个包含了电影列表的 tableview。但是我们想要兼顾用户体验的话,这并不是一种好的设计;更好的方案是我们的应用应该在 Spotlight 中展示选中电影的详细信息,而这正是我们最终要实现的。总而言之,我们不仅要在 Spotlight 中可以搜索电影数据,还要把相关搜索结果所对应的电影详情展现出来。通过下面示例的学习,你基本就懂了。

为了立即可以开始工作,你可以先下载初始工程。在这个工程里,主要包含以下几部分:

  • UI 部分以及所有必要的 IBOutlet 属性已经设置完成。
  • 实现了基本的 tableView
  • 所有的电影数据、以及每部电影的封面(一共5张)都存放在 a.plist 文件里。

假如你对 plist 文件里面每部电影包含的信息类型感兴趣,下面的截图能清晰地说明一切:

在深入了解 Core Spotlight API 的细节之前,我们会执行两个明确的任务:

  1. 加载电影数据并展示在 tableView 里。
  2. 在详情视图控制器里展示被选中电影的数据。

尽管在初始项目里实现以上任务能让你更快地开始本文主题的学习,但我并没有这么做,原因很简单:我坚信,通过对 demo 应用的极其数据内容的探索会让你更直观地明白特定数据是如何在 Spotlight 里被搜索的。不过不用担心,准备工作都不多且都能快速完成。

加载和展示示例数据

好的,让我们开始吧!假设现在你已经下载了初始工程并查看了包含电影数据的 plist 文件。在 MoviesData.plist 文件里,你会看到总共五项在 IMDB 网站里随机选取的示例电影数据。我们的第一个目标是把 .plist 文件里的数据加载到一个数组里,然后展示在 tableView 里。

直接进入代码部分,打开最主要的 ViewController.swift 文件,并在类的顶部声明一个属性:

var moviesInfo: NSMutableArray!

所有的电影都会加载到这个数组里,每一部电影都会以键值对字典的形式与 .plist 文件相对应。

我们先来写一个简单的自定义方法来加载数据。正如下面所展示的,首先确保了 .plist 文件的存在,然后就可以用该文件的内容来初始化数组。

func loadMoviesInfo() {if let path = NSBundle.mainBundle().pathForResource("MoviesData", ofType: "plist") {moviesInfo = NSMutableArray(contentsOfFile: path)}
}

接着,我们要在 viewDidLoad() 里调用这个方法。要确保在 configureTableView() 方法之前调用它,即要按照以下代码片段的展示:

override func viewDidLoad() {super.viewDidLoad()// 从文件里加载电影数据。loadMoviesInfo()configureTableView()navigationItem.title = "Movies"
}

我们本可以直接在 viewDidLoad() 方法里面加载文件内容,而无需创建自定义方法。但是我喜欢整齐的代码,即使对于这么简单的一个小功能,创建一个自定义方法还是好很多。

我们既然知道了应用会在每次启动时加载电影数据,就可以继续修改当前 tableView 的实现让它展示我们的电影。这里并没有太多需要做的:我们会根据电影数量定义 tableView 行数,然后把适合的数据展示在 Cell 里。

先从行数开始,很明显行数需要与电影数目相等。然而,我们首先要确保有电影可以展示,不然当数组没有加载到文件内容时应用会崩溃。

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {if moviesInfo != nil {return moviesInfo.count}return 0
}

最后,让我们显示电影数据。出于演示的目的,在起始项目里你能找到一个 UITableViewCell 的子类 MovieSummaryCell,还有与其对应的 .xib 文件代表了单个电影 Cell:

这样的 Cell 展示了每部电影的图片、标题、简介、以及评分。所有的 UI 控制器都有相对应的 IBOutlet 属性,你可以在 MovieSummaryCell.swift 文件里找到它们:

@IBOutlet weak var imgMovieImage: UIImageView!@IBOutlet weak var lblTitle: UILabel!@IBOutlet weak var lblDescription: UILabel!@IBOutlet weak var lblRating: UILabel!

以上的命名方式表明了每个属性的功能,搞清楚后,我们利用它们来展示电影的详情。回到 ViewController.swift 文件,按照下面的代码片段更新 tableView 方法:

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {let cell = tableView.dequeueReusableCellWithIdentifier("idCellMovieSummary", forIndexPath: indexPath) as! MovieSummaryCelllet currentMovieInfo = moviesInfo[indexPath.row] as! [String: String]cell.lblTitle.text = currentMovieInfo["Title"]!cell.lblDescription.text = currentMovieInfo["Description"]!cell.lblRating.text = currentMovieInfo["Rating"]!cell.imgMovieImage.image = UIImage(named: currentMovieInfo["Image"]!)return cell
}

currentMovieInfo 字典并不是必须的,但它可以让代码变得更加简单。

现在能如你所愿地第一次尝试运行这个应用了。可以看到一些电影详情在 tableView 中罗列出来。到现在为止都是大家很熟悉的步骤,下面让我们直接开始第二个准备步骤:展示所选电影的详情信息。

展示数据详情信息

我们在 ViewController 类的 tableView 里选中电影的详情,将通过 MovieDetailsViewController 类来展示,对应的场景在 Interface Builder 里已经写好,所以现在有两个任务: 从 ViewController 里传递对应的电影字典到这个类里,然后把字典里的值传递到适当的 UI 控制器里,而这些 IBOutlet 属性都已经被声明并且正确地连线了。

说到字典,让我们在 MovieDetailsViewController 类的顶部做出以下声明:

var movieInfo: [String: String]!

先暂时回到 ViewController.swift,看看当一行电影数据被点击的时候,我们需要做些什么。这时需要了解被选中行的索引,以便从 movieInfo 数组中选择恰当的字典,并在 Segue(名为 idSegueShowMovieDetails)执行的时候传递给下一个视图控制器。从 tableView 的代理方法里获取索引很简单,但我们仍需要一个自定义属性来保存它。因此在 ViewController 类的顶部我们需要声明:

var selectedMovieIndex: Int!

然后,我们需要按照以下方法处理 tableView 的行选择:

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {selectedMovieIndex = indexPath.row
performSegueWithIdentifier("idSegueShowMovieDetails", sender: self)

在这儿,我们做两件非常简单的事情:首先,把选中的行索引保存在自定义的属性里,然后执行展示电影详情的 Segue。然而这还不够,因为还没有从 moviesInfo 数组里选择合适的电影字典,而且也还没把任何数据传递给 MovieDetailsViewController 类。那么我们需要做些什么呢? 那就是重写 prepareForSegue:sender: 方法并完成上述功能。

oerride func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {if let identifier = segue.identifier {if identifier == "idSegueShowMovieDetails" {let movieDetailsViewController = segue.destinationViewController as! MovieDetailsViewControllermovieDetailsViewController.movieInfo = moviesInfo[selectedMovieIndex] as! [String : String]}}
}

足够简单了吧。我们只是通过这个 Segue 的 destinationViewController 属性获得 MovieDetailsViewController 实例,然后把对应的电影字典赋值给在本部分最开始声明的movieInfo 属性。

现在,重新打开 MovieDetailsViewController.swift 文件,其中只有一个自定义方法。通过该该方法,将 movieInfo 字典中的值分配给相应的 UI 控制器,至此我们的工作就结束了。以下是一个简单的实现,我就不详述了:

func populateMovieInfo() {lblTitle.text = movieInfo["Title"]!lblCategory.text = movieInfo["Category"]!lblDescription.text = movieInfo["Description"]!lblDirector.text = movieInfo["Director"]!lblStars.text = movieInfo["Stars"]!lblRating.text = movieInfo["Rating"]!imgMovieImage.image = UIImage(named: movieInfo["Image"]!)
}

最后,让我们在 viewWillAppear: 方法里调用以上方法:

override func viewWillAppear(animated: Bool) {...if movieInfo != nil {populateMovieInfo()}
}

这部分就结束了。你可以再运行一下,然后在 tableView 里选择某电影的时候查看一下电影的详情。

为 Spotlight 索引数据

使用 iOS 9 的 Core Spotlight 框架让任何应用的数据都能通过 Spotlight 搜索到。要做到这一点,关键在于请求 Core Spotlight API 索引我们的数据,以便用户可以搜索到。但无论是我们的应用还是 CS API 都无法判断数据的类型。因为准备数据并把数据以特定格式提供给 API 是我们的职责。

再解释一下,我们希望能在 Spotlight 中搜索到的所有数据都必须表现为 CSSearchableItem 对象,然后组织成数组形式提供给 CS API 索引。单个 CSSearchableItem 对象包括一组属性,它可以让 iOS 完全掌握被搜索项的细节,类似于哪部分数据应该在搜索时展示(例如,电影的名字、它的图片和描述信息),还有哪些关键词会触发包含相关数据的应用在 Spotlight 里出现。单个可被搜索的项目的所有属性都会展示在一个 CSSearchableItemAttributeSet 对象里,它提供了许多属性让我们用于赋值。作为参考,我提供了官方文档链接便于你查看所有可用的属性。

Spotlight 索引数据是最后一步。正常情况下涉及以下步骤(包括索引):

  1. 为每个数据片段设置属性,例如一部电影(CSSearchableItemAttributeSet 对象)。
  2. 利用上一步获得的属性为每个数据片段初始化一个可搜索项目(CSSearchableItem 对象)。
  3. 把所有可搜索项添加到一个数组里。
  4. 利用以上数组为 Spotlight 索引数据。

我们来按步骤执行,为了实现目标,需要在 ViewController.swift 文件里创建一个名为 setupSearchableContent() 的自定义方法。在实现本部分内容后,你会发现想要搜索全部的数据并不是一件难事。但是,我们不打算一步登天,也不准备一次性把所有的实现都告诉你们;而是把代码分段,以便你们理解。别担心,这并不复杂。

在我们实现新方法之前,需要先导入两个框架:

import CoreSpotlight
import MobileCoreServices

让我们开始定义新方法,首先声明一个数组,待会用来收集可被搜索的项目:

func setupSearchableContent() {var searchableItems = [CSSearchableItem]()}

现在我们能在循环里读取每一部电影:

func setupSearchableContent() {var searchableItems = [CSSearchableItem]()for i in 0...(moviesInfo.count - 1) {let movie = moviesInfo[i] as! [String: String]}
}

我们会为每部电影创建一个 CSSearchableItemAttributeSet 对象,然后设置相应的属性,这样在 Spotlight 搜索时就会展示相关的结果。在示例中,我们会指定电影标题、简介和图片这部分数据展示给用户。

func setupSearchableContent() {var searchableItems = [CSSearchableItem]()for i in 0...(moviesInfo.count - 1) {let movie = moviesInfo[i] as! [String: String]let searchableItemAttributeSet = CSSearchableItemAttributeSet(itemContentType: kUTTypeText as String)// 设置标题.searchableItemAttributeSet.title = movie["Title"]!// 设置电影封面.let imagePathParts = movie["Image"]!.componentsSeparatedByString(".")searchableItemAttributeSet.thumbnailURL = NSBundle.mainBundle().URLForResource(imagePathParts[0], withExtension: imagePathParts[1])// 设置简介.searchableItemAttributeSet.contentDescription = movie["Description"]!}
}

留意在以上代码片段里我们是如何为电影图片这个属性进行赋值的。有两种方式:一是指定图片的 URL,二是把图片作为 NSData 对象。对我们来说,最简单的方式就是为提供所有电影图片文件的 URL,众所周知这些图片就存在于应用 bundle 里。然而,这种方式需要把每个图片文件名分成实际名字和扩展名,因此我们利用 String 类的 componentsSeparatedByString: 方法来实现分割操作,剩下的就很简单了。

现在是时候设置一波关键词了,这样就能通过 Spotlight 搜索到 App 中的相关数据了。在指定关键词之前要考虑清楚,因为你的决定会影响 App 在 Spotlight 里的搜索结果、这对用户也很重要。在本例中,我们会把电影所属的类别及其评星数设置为关键词。

func setupSearchableContent() {var searchableItems = [CSSearchableItem]()for i in 0...(moviesInfo.count - 1) {...var keywords = [String]()let movieCategories = movie["Category"]!.componentsSeparatedByString(", ")for movieCategory in movieCategories {keywords.append(movieCategory)}let stars = movie["Stars"]!.componentsSeparatedByString(", ")for star in stars {keywords.append(star)}searchableItemAttributeSet.keywords = keywords}
}

要知道电影的分类在 MoviesData.plist 文件中用一段字符串表示,每部电影之间用逗号分隔。因此很有必要把这段字符串所代表的电影类比分隔出来,然后存在 movieCategories 数组里方便访问。接着使用内循环把每个值添加到 keywords 数组。对于评星数,我们也执行完全相同的步骤,再次把一个包含所有电影评星数的字符串分隔为许多独立的部分,最后添加到关键词数组。

需要注意的是最后一行;我们为每部电影的属性集合设置了关键词。如果漏了这一行,那么 Spotlight 就不会展示任何关于这个应用的搜索结果。
现在我们已经为 Spotlight 设置了属性和关键词,是时候初始化一个可搜索项目并添加到 searchableItems 数组了:

func setupSearchableContent() {var searchableItems = [CSSearchableItem]()for i in 0...(moviesInfo.count - 1) {...let searchableItem = CSSearchableItem(uniqueIdentifier: "com.appcoda.SpotIt.\(i)", domainIdentifier: "movies", attributeSet: searchableItemAttributeSet)searchableItems.append(searchableItem)}
}

以上的初始化方法接收三个参数:

  • uniqueIdentifier: 这个参数唯一地标识了当前在 Spotlight 的可搜索项目。你可以用你喜欢的方式编写这个标识符,但是要注意一个细节:在这个示例里我们添加了当前电影的索引作为标识符,因为稍后会展示与索引值相匹配的电影详情。总体来说,在标识符中包含一个指向某些数据的索引是个好主意,这些数据将会用于详情展示。你稍后会更好地了解电影索引的作用。
  • domainIdentifier: 使用这个参数把可搜索项目组成集合。
  • attributeSet 这是我们刚刚用于赋值的属性集合对象。

最后,新的可搜索项目被加到 searchableItems 数组里。

我们最后需要执行的步骤是使用 Core Spotlight API 索引这些项目。通过 for 循环来实现:

func setupSearchableContent() {...CSSearchableIndex.defaultSearchableIndex().indexSearchableItems(searchableItems) { (error) -> Void inif error != nil {print(error?.localizedDescription)}}
}

上面的方法已经功能齐全了,就等调用了。我们会在 viewDidLoad() 方法里调用它:

override func viewDidLoad() {...setupSearchableContent()
}

我们现在已经准备好首次使用 Spotlight 搜索电影了。运行应用,退出,然后在 Spotlight 使用之前定义好的任意关键词。你会看见搜索结果展现在眼前。点击任意搜索结果,会自动启动相关应用。

实现定点着陆

虽然通过 Spotlight 可以搜索到我们应用中的电影数据这一点令人印象深刻,但还是能更上一层楼。目前,点击搜索结果,会跳转到应用首页 ViewController 界面。但是我们的目标是让它直接跳转到电影详情的视图控制器,并展示所选择电影的相关信息。

虽然听起来比较复杂,但其实相当容易。针对我们这个示例应用则更简单了。因为我们已经完成了绝大部分的基础工作,可以很容易地实现展示选中电影的详情页面。

这里的主要工作是重写一个 UIKit 方法 restoreUserActivityState:,来处理在 Spotlight 被选中的搜索结果。当我们最终想要实现的是,从可搜索的项目标识符中取出该电影在 moviesInfo 数组里的索引值(如果你记得的话,我们在之前的部分动态地创建了这个标识符)。

该方法接受一个 NSUserActivity 对象作为参数。这个对象有一个名为 userInfo 的字典属性,其中包括了在 Spotlight 中被选中的可搜索项目的标识符。我们通过标识符在 moviesInfo 数组里获取该电影的索引值,然后展示详情视图控制器。就这些。

来看看具体实现:

override func restoreUserActivityState(activity: NSUserActivity) {if activity.activityType == CSSearchableItemActionType {if let userInfo = activity.userInfo {let selectedMovie = userInfo[CSSearchableItemActivityIdentifier] as! StringselectedMovieIndex = Int(selectedMovie.componentsSeparatedByString(".").last!)performSegueWithIdentifier("idSegueShowMovieDetails", sender: self)}}
}

如你所见,首先检查 activity typeCSSearchableItemActionType 是必要的。坦白地讲,这么做并不重要,但假设应用需要处理多个 NSUserActivity 对象,那么你就别忘了做这件事(例如,在 iOS 8 首次出现的 Handoff 特性利用了 NSUserActivity 类)。这个标识符是一个储存在 userInfo 字典里的字符串值。得到这个字符串之后,我们会把它根据点符号(dot symbol)分成不同部分,然后获取最后一部分,这是被选中的电影在电影集合里的索引。剩下的就很简单了:给 selectedMovieIndex 属性赋值然后执行 Segue。剩下的任务就交给我们之前的实现了。

现在打开 AppDelegate.swift 文件。我们需要添加一个新的代理方法。每一次与应用相关的搜索结果在 Spotlight 里被选中的时候,这个方法都会被调用,我们只需调用上面实现的方法,传递 user activity 对象即可。来看看具体实现:

func application(application: UIApplication, continueUserActivity userActivity: NSUserActivity, restorationHandler: ([AnyObject]?) -> Void) -> Bool {let viewController = (window?.rootViewController as! UINavigationController).viewControllers[0] as! ViewControllerviewController.restoreUserActivityState(userActivity)return true
}

在以上代码片段里,在恢复用户活动状态前,我们首先通过 window 属性获取到 ViewController 视图控制器。你还可以利用 NSNotificationCenter 和发送自定义通知来实现,这样你需要在 ViewController 类里处理通知。显然第一种方案更为直观。

这就是全部内容了!我们的示例应用已经完成,那么再运行一次看看在 Spotlight 里搜索电影时会发生什么吧。

总结

iOS 9 最新的搜索 API 对于开发者而言前景广阔,因为这些 API 能大幅提高应用的曝光度、也更容易被用户访问。在本教程里,我们涉及了索引应用数据的所有步骤,最终在 Spotlight 搜索时能发现这些数据。也说明了应用该如何处理选中的搜索结果,并展现特定的数据给用户。在实现这些特性一定能大幅提升用户体验,因此你应该认真地考虑在现有的和将来的项目中添加这些特性。

如何使用 iOS 9 的 Core Spotlight 框架相关推荐

  1. 如何使用iOS9中的Core Spotlight框架

    如何使用iOS9中的Core Spotlight框架 作者: Gabriel Theodoropoulos,时间:2015/12/22 翻译:BigNerdCoding, 如有错误欢迎指出.原文链接 ...

  2. iOS绘图UIBezierPath 和 Core Graphics框架

    前言 iOS系统本身提供了两套绘图的框架,即UIBezierPath 和 Core Graphics.而前者所属UIKit,其实是对Core Graphics框架关于path的进一步封装,所以使用起来 ...

  3. iOS开发之集成iOS9中的Core Spotlight Framework搜索App的内容

    转载自:  http://www.cnblogs.com/ludashi/p/4867511.html iOS开发之集成iOS9中的Core Spotlight Framework搜索App的内容 S ...

  4. 【iOS开发】从Cocoa框架说开去--Fundation框架系列

    [Cocoa简介] Cocoa是苹果公司为Mac OS X所创建的原生面向对象API 是Mac OS X上五大API之一.(其他四个:Carbon POSIX X11 Java)它是苹果的面向对象开发 ...

  5. Core Foundation框架

    转载自:http://blog.csdn.net/weiwangchao_/article/details/7744972 Core Foundation框架 (CoreFoundation.fram ...

  6. 184使用 Core Image 框架处理照片

    实现图片的老旧.色彩.旋转效果 (1)使用 StoryBoard 故事版布局界面: (2)使用 Core Image 框架的 CIFilter 过滤器:分别对应的过滤器名称是:CISepiaTone( ...

  7. 通过极简模拟框架让你了解ASP.NET Core MVC框架的设计与实现[中篇]:请求响应

    <200行代码,7个对象--让你了解ASP.NET Core框架的本质>让很多读者对ASP.NET Core管道有了真实的了解.在过去很长一段时间中,有很多人私信给我:能否按照相同的方式分 ...

  8. 通过极简模拟框架让你了解ASP.NET Core MVC框架的设计与实现[上篇]

    <200行代码,7个对象--让你了解ASP.NET Core框架的本质>让很多读者对ASP.NET Core管道有了真实的了解.在过去很长一段时间中,有很多人私信给我:能否按照相同的方式分 ...

  9. .NET Core UI框架Avalonia

    .NET Core UI框架Avalonia,Avalonia是一个基于WPF XAML的跨平台UI框架,并支持多种操作系统:Windows(.NET Framework,.NET Core),Lin ...

最新文章

  1. 剑指offer:面试题33. 二叉搜索树的后序遍历序列
  2. Redis数据介绍与指令大全
  3. vim设置行号等一系列配置
  4. opencv 多线程_深度学习和OpenCV的对象检测(MobileNet SSD视频流实时识别)
  5. python学习之函数
  6. 一维条形码***技术(Badbarcode)
  7. U盘专杀工具,U盘防御软件,U盘病毒防火墙--UDiskSyS
  8. C语言 嵌入式 面试小知识点(一)
  9. 转:Google的系统工程师(SA)如何工作
  10. poj3557 Map Generator
  11. 【路径规划】局部路径规划算法——人工势场法(含python实现)
  12. 无线网络技术导论知识点
  13. 智慧工厂智能制造供应链解决方案
  14. 89c52单片机c语言时钟,液晶显示AT89C52单片机DS1302实时时钟程序
  15. 机器学习(3)——有监督学习
  16. 挖矿病毒入侵服务器(没有解决,重置服务器了)
  17. iFunk翼S苏宁京东热卖进行中
  18. 【经验总结】CS与MSF之间的会话传递
  19. 世界最快的超级计算机 神威太湖之光,揭秘全国产世界最快超级计算机“神威·太湖之光”(组图)...
  20. 用这8个办公软件,效率高一倍

热门文章

  1. COMSOL软件入门及软件基本操作
  2. 攻击的检测与防护方法
  3. Lightroom Classic 教程:如何在 Lightroom 中改变场景颜色?
  4. TensorFlow创建常量(tf.constant)详解
  5. 蓝凌平台型OA应用标杆 招商局集团OA3.0正式上线
  6. 计算机主板芯片知识点,【写给小白的硬件知识点】什么是主板,主板的作用
  7. 随机数c 语言代码大全,C 语言随机数函数
  8. 使用Brackets
  9. equire.ensure
  10. json 中的ensure_ascii=False