程序调试 (二) —— Xcode Simulator的高级功能(一)

版本记录

版本号 时间
V1.0 2021.01.04 星期一

前言

程序总会有bug,如果有好的调试技巧和方法,那么就是事半功倍,这个专题专门和大家分享下和调试相关的技巧。希望可以帮助到大家。感兴趣的可以看下面几篇文章。
1. 程序调试 (一) —— App Crash的调试和解决示例(一)

开始

首先看下主要内容:

在本教程中,您将学习Xcode Simulator的高级功能,以改善您的日常开发经验。内容来自翻译

下面看下写作环境:

Swift 5, iOS 14, Xcode 12

接着就是正文了。

Xcode Simulator是开发人员使用最广泛的工具之一。 在模拟器上运行和测试应用程序已成为每个开发人员日常工作的一部分。 熟悉各种模拟器选项对于任何开发人员都是至关重要的。 您知道您也可以从命令行创建和配置模拟器吗? 在本教程中,您将学习:

  • 什么是模拟器
  • 深入了解有用的模拟器选项
  • 从命令行创建和配置模拟器
  • 使用命令行流式传输和捕获日志
  • 创建Bash脚本以在不同区域设置的模拟器上自动启动应用程序。

打开RayWonders项目。 构建并运行。

这个程序有两个tabPhoto and MapPhoto tab显示了世界奇观的照片。 点击照片可显示其详细说明。 Map tab将世界奇观的照片显示为地图上的注释。 这是raywenderlich.com教程之一,您无需修改项目。 相反,您将使用此应用程序作为基础来学习各种模拟器选项。 在开始世界巡回演唱之前,您需要了解什么是模拟器。


What Is a Simulator?

该模拟器是在Mac上模拟iOS,iPadOS,tvOS和watchOS的绝佳工具。 它通过模拟不同的设备来帮助快速进行原型设计和测试。 从技术上讲,模拟器是在macOS内核上运行的独立用户空间。

User space是分配给运行应用程序的系统内存,而内核空间是分配给运行OS内核和设备驱动程序的系统内存。启动新的模拟器会创建一个单独的用户空间。

内核(kernel)是操作系统的核心组件。它促进了硬件和软件组件之间的交互。它负责管理硬件,分配内存并在进程之间进行仲裁。

这些进程之一 —— ** Daemon** —— 在后台运行。launchd,cfprefsd,distnoted是一些系统daemons程序。

在设备和模拟器上运行的应用程序是否相同?接下来,您将学到。


Running App on a Device vs. a Simulator

在模拟器上测试应用程序非常方便。但是,在设备和模拟器上运行的应用程序之间存在一些关键差异。

1. Computing Performance

模拟器与Mac共享相同的计算资源。这包括内存,CPU和网络连接。相反,与Mac相比,物理设备具有更少的内存和计算能力。同样,网络可能是物理设备上不可预测的野兽。

2. Display

Mac和物理设备上的分辨率和色域可能会不同,从而导致图像和文本出现锯齿。 在本教程的后面,您将学习如何模拟确切的物理设备大小。

3. Hardware Limitations

模拟器不支持某些硬件组件。 这些包括:

  • 蓝牙
  • 相机
  • 运动传感器,例如加速度计和陀螺仪
  • 接近传感器

4. Framework Limitations

模拟器不支持某些框架。 这些包括:

  • ARKit
  • HomeKit
  • IOSurface
  • MessageUI

有关所有差异的更详尽列表,请通过在“模拟器”菜单中选择Help ▸ Simulator Help来参考文档。

接下来,您将学习如何组织模拟器。


Organizing Simulators Using Xcode

Xcode附带了一组默认的模拟器。 要查看这些模拟器的列表,请按照下列步骤操作:

  • 1) 打开Xcode
  • 2) 选择Window菜单选项。
  • 3) 选择Devices and Simulators菜单。
  • 4) 选择Simulators选项卡。

您会看到Xcode随附的模拟器列表。 如果您过去曾经下载过其他运行时,那么也会看到这些相关的模拟器。

现在,您将使用自定义名称创建一个新的模拟器。

按着这些次序:

  • 1) 按下左下方的+按钮。
  • 2) 将模拟器名称设置为Demo
  • 3) 选择iPhone 12 Pro作为设备类型。
  • 4) 选择iOS 14.2作为操作系统版本。
  • 5) 按Create

这将创建一个名为Demo的新模拟器,您现在可以在模拟器列表中找到它。

取消选中Demo simulatorShow as runtime destination复选框。

这隐藏了模拟器。 它不会显示在该app scheme旁边的模拟器列表中。

按住Control键并单击Demo模拟器。 出现几个选项,例如DeleteRename

继续并通过单击Delete来删除Demo simulator。 不用担心 您很快就会使用其他方法创建它。

1. Running Older Runtimes

有时,您必须在旧版运行时上运行您的应用。

创建新的模拟器时,可以通过在OS版本下拉菜单中选择Download more runtimes来下载较早的运行时。

您也可以从Components窗口下载更多运行时。 选择Xcode ▸ Preferences ▸ Components选项。

该窗口显示下载的模拟器运行时以及其他可供下载的运行时的列表。

接下来,您将学习创建模拟器的另一种方法。

2. Creating Simulators From the Simulator Menu

构建并运行。

当模拟器正在运行时,请按照下列步骤操作:

  • 1) 从模拟器菜单中选择File ▸ New Simulator
  • 2) 输入Demo作为模拟器名称。
  • 3) 选择iPhone 12 Pro作为Device Type
  • 4) 选择iOS 14.2作为版本。
  • 5) 单击Create

请按照以下步骤打开模拟器:

  • 1) 从菜单中选择File ▸ Open Simulator
  • 2) 选择iOS 14.2运行时。
  • 3) 选择Demo模拟器。

这将启动Demo模拟器。

那又快又简单! 接下来,您将了解模拟器中的各种尺寸选项。


Comparing Simulator Size Options

您可以通过在四个角之一中单击并拖动来调整模拟器的大小。 Window menu选项提供四个缩放选项。

1. Physical Size

物理大小会调整模拟器的大小,以匹配实际的设备大小。 这可以直观地显示您的应用在不同屏幕尺寸下的外观。

2. Point Accurate

点精确模式可调整窗口大小,以便在具有不同比例因子的设备上内容具有相同大小。 结果,显示为3x的设备上的图像与2x显示器的尺寸相同。

3. Pixel Accurate

在此模式下,窗口大小更改为与物理设备相同的像素数。 模拟设备上的每个像素都映射到Mac显示器上的一个像素。 如果Mac显示器的像素密度低于模拟设备的像素密度,这会使模拟器在屏幕上显得更大。 您可以使用此模式检查应用程序中图像和控件的对齐方式。

4. Fit Screen

这会将模拟器的大小调整为Mac显示器的大小。

接下来,您将学习模拟器中提供的各种选项。


Slow Animations

动画是应用程序体验不可或缺的一部分。 在Demo模拟器中构建并运行RayWonders

点击Photos选项卡。 接下来,点击一张世界奇观的照片,以呈现有关该地方的详细信息。 向下滑动以消除视图。

要模拟慢速动画,请在Simulator菜单中选择Debug ▸ Slow Animations选项。

再次点击图片。 现在,视图动画缓慢。

缓慢查看动画有助于提高清晰度。 另外,它可以帮助检查渲染和性能方面的问题。

接下来,您将学习模拟Dark Mode。 在继续之前,通过取消选择Debug ▸ Slow Animations菜单选项来禁用慢动画。

1. Dark Mode

Dark Mode可在弱光环境下提供出色的观看体验。 模拟器提供了在黑暗模式下查看您的应用程序的选项。

选择Features ▸ Toggle Appearance。 这会将外观切换为Dark Mode。 现在,点击RayWonders中的Map选项卡。

您会注意到地图已更改为深色外观。 很简单,不是吗? 这是在黑暗模式下测试应用程序的便捷方法。

要将外观改回默认设置,请取消选择Features ▸ Toggle appearance。 要了解如何支持暗模式,请查看Supporting Dark Mode: Adapting You App to Support Dark Mode tutorial

接下来,您将学习如何模拟推送通知!


Simulating Push Notifications

推送通知是一种让用户知道应用程序中新内容更新的好方法。 过去,测试推送通知是一个挑战。 您需要物理设备来测试推送通知。 从Xcode 11.4开始,您可以在模拟器上模拟推送通知。

使用以下命令创建一个名为RayWondersPushNotification.apns的文件:

{
  "Simulator Target Bundle": "com.raywenderlich.RayWonders",
  "aps": {
    "alert": {
      "title": "Hindi language support added!",
      "body": "Checkout RayWonders in Hindi!"
    }
  }
}

这是带有titlebody的简单可见的推送通知有效内容。 Simulator Target Bundle是一个特殊的键。 它指定应接收通知的应用程序的bundle ID

在尝试有payload之前,您需要首先在应用程序中授予通知权限。

按着这些次序:

  • 1) 在Demo模拟器中打开RayWonders
  • 2) 在Photos选项卡中,点击右上角的响铃图标。
  • 3) 在通知权限提示中选择Allow
  • 4) 从Simulator menu中选择Device ▸ Home,将应用程序置于后台。

接下来,将RayWondersPushNotification.apns文件拖放到Demo模拟器上。

出现一个可见的推送通知。 那很简单! 请记住,模拟器只能模拟推送通知。 要测试从Apple推送通知服务器收到的实际推送通知,需要使用物理设备。

要更深入地了解推送通知,请阅读 Push Notifications Tutorial: Getting Started

接下来,您将学习如何缩放。


Zooming In and Out

在模拟器中打开RayWonders。 切换到Map标签。 地图以世界奇观为中心并放大。 世界其他奇迹在哪里?

您需要缩小以查看全景。 在按住Option键的同时单击并拖动。

单击和拖动很棒,但是很快就会变得很累。 接下来,您将学到一种更快的去往某个地点的方法。


Simulating a Location

模拟器提供了一种简单的方法来模拟位置。 首先,您需要在RayWonders中授予位置权限。

按着这些次序:

  • 1) 在模拟器中打开RayWonders并切换到Map选项卡。
  • 2) 点击Map选项卡中的Start Location Services按钮。
  • 3) 选择Allow While Using App权限。

现在,要模拟位置:

  • 1) 从模拟器菜单中选择Features ▸ Location ▸ Custom Location
  • 2) 输入41.8902142作为纬度,输入12.4900422作为经度。
  • 3) 单击OK

这会将您直接带到地图上的罗马竞技场-您有史以来最快的旅程!

但是,输入要模拟的每个位置的坐标都需要占用大量内存,即您的内存即任务。 接下来,您将学到解决此问题的简便方法。

1. Sharing Locations From the Maps App

macOS上的Maps应用程序提供了一种与模拟器共享位置的简便方法。

按着这些次序:

  • 1) 打开Maps应用。
  • 2) 在搜索文本中输入Machu Picchu
  • 3) 单击搜索字段旁边的Share按钮。
  • 4) 从下拉菜单中选择Simulator
  • 5) 在位置提示中,选择Demo作为模拟器。
  • 6) 单击Send

您现在位于模拟器的地图视图中的Machu Picchu

恭喜你! 您刚刚创造了从罗马斗兽场到Machu Picchu旅行最快的世界纪录!

要了解有关更深入地处理应用中位置的信息,请查看 MapKit and Core Location

现在,该改变一下了。


Simulating Shake Gesture

摇动手势(Shake gestures)是提示用户在应用程序中提供反馈的绝佳线索。 但是,如何摇动模拟器? 幸运的是,严格摇动Mac不是解决方案。 模拟器提供了一种简单的方法。

Demo模拟器上打开RayWonders。 从Simulator菜单中选择Device ▸ Shake

这模拟了摇动手势。

RayWonders会检测到晃动并向用户提示alert。 您可以通过实现motionEnded(_:with :)方法来检测和处理应用中的摇动手势。 要了解更多信息,请查阅Apple documentation

接下来,您将学习如何模拟内存警告。


Simulating a Memory Warning

当iOS的内存使用量接近设备的上限时,iOS会向该应用发送警告。 应用程序需要通过清除缓存数据做出响应,该数据可以在以后重新创建。

在演示模拟器上打开RayWonders。 从菜单中选择Debug ▸ Simulate Memory Warning

这模拟了内存警告。

RayWonders显示alert。 您可以在Responding to Memory Warnings Apple文档中了解有关处理内存警告的更多信息。

到目前为止,您已经看到了一些有用的模拟器选项。 本教程未涵盖其他几项。 您应该根据自己的应用需求将其签出。 其中一些选项包括:

  • 使用Features ▸ Trigger iCloud Sync来模拟iCloud同步。
  • 使用Features ▸ Authorize Apple Pay来模拟Apple Pay授权。
  • 使用Device ▸ Siri触发Siri
  • 使用Debug ▸ Open System Log检查系统日志。

接下来,您将学习使用命令行与模拟器进行交互。


Organizing Simulators Using the Command Line

到目前为止,您已经使用Xcode创建和管理了模拟器。 此外,您还了解了各种Simulator菜单选项。 现在,您将学习从命令行管理和使用模拟器。

打开终端。 输入以下命令:

xcrun simctl --help

然后按Enter

help选项提供了使用simctl可用的所有子命令的列表。

Simctl是帮助管理模拟器并以编程方式与模拟器交互的工具。 您可以使用xcrun命令行工具访问simctl

现在,您将探索其中的几个子命令。 运行以下命令:

xcrun simctl list

list命令显示所有可用设备和运行时的列表。

它还显示了设备的当前状态,无论是Booted还是Shutdown

接下来,您将学习从命令行创建和启动模拟器。

1. Creating Simulator From the Command Line

在创建新模拟器之前,请删除通过Xcode创建的Demo模拟器。 输入以下内容:

xcrun simctl delete Demo

delete命令标识一个模拟器并将其删除。 现在,从命令行创建一个新的模拟器。

输入以下内容:

xcrun simctl create Demo "iPhone 12 Pro" "iOS14.2"

create命令接受设备类型和运行时,并创建模拟器。 这将创建具有iOS 14.2运行时的iPhone 12 Pro模拟器。

现在,终端显示新设备的唯一标识符。

输入以下内容:

xcrun simctl boot Demo

启动演示模拟器。 默认情况下,它处于关机(Shutdown)状态。

现在,使用命令行安装RayWonders。 您首先需要该app bundle。 按着这些次序:

  • 1) 打开“项目”导航器,然后选择Products文件夹中的RayWonders.app
  • 2) 右键单击并选择在Finder中显示。
  • 3) 将RayWonders复制到您的主目录。

打开终端并通过输入以下内容导航到主目录:

cd ~

接下来,运行以下命令:

xcrun simctl install Demo RayWonders.app

install命令将RayWonders安装在模拟器上。 很好!

您甚至可以使用simctl启动该应用程序。 输入以下内容:

xcrun simctl launch Demo com.raywenderlich.RayWonders

启动(launch)命令将app bundle ID作为参数。 RayWondersDemo模拟器中启动。

恭喜你! 现在,您已经从命令行创建,启动,安装并启动了模拟器。 您可以尝试以下其他选项:

  • terminate:这将使用bundle identifier终止应用程序。
  • erase:这将擦除设备内容。
  • uninstall:这将卸载应用程序。 您需要指定应用程序的bundle identifier

接下来,您将学习一些适用于您的应用的出色命令。

2. Taking Screenshots

您的应用的屏幕截图在几种情况下很有用。 例如,在AppStore中提交应用程序时需要截图。

输入以下命令:

xcrun simctl io Demo screenshot screenshot.png --type="png"

这将获取当前屏幕的屏幕截图,并将其保存到文件screenshot.png中。 除了png外,screenshot命令还支持其他文件格式,例如TIFF,BMP,GIF和JPEG

屏幕快照可以显示您的应用程序的外观,而一张图片值一千字。 但是,视频的价值更高。

3. Recording Video

您可以使用simctl录制应用程序的视频。 要开始记录,请输入以下命令:

xcrun simctl io Demo recordVideo Demo.mov --codec="h264"

在模拟器上与RayWonders进行交互。 完成后,在终端中按Control-C。 这会将视频保存在Demo.mov中。

另外,您可以在录制时指定所需的编解码器。 默认编解码器为hevc

recordVideo命令有助于避免使用QuickTime播放器录制应用程序的视频。

4. Customizing the Status Bar

在模拟器中检查应用程序的状态栏。 您会看到电池已充满,设备具有最佳信号,并且模拟器上的时间是本地Mac时间。 如果要覆盖这些内容怎么办?

在终端中输入以下命令:

xcrun simctl status_bar Demo override \
  --dataNetwork 4g --cellularBars 2 --batteryState charging \
  --batteryLevel 25 --time 12:05

这会覆盖要设置的状态栏:

  • 数据网络为4G
  • 蜂窝信号到两个条形图
  • 电池状态为充电状态,电池电量为25%
  • 在模拟器上的时间为12:05

在自己的项目中执行此操作时,可以将状态栏信息设置为所需的任何内容。

当您要自定义应用屏幕截图和视频的外观时,status_bar命令可能非常方便。

完成后,通过输入以下命令将状态栏恢复为默认外观:

xcrun simctl status_bar Demo clear

Debugging and Diagnosing

Simctl还提供了一些命令来帮助调试和诊断问题。

返回终端,然后输入以下内容:

xcrun simctl get_app_container Demo com.raywenderlich.RayWonders

get_app_container打印app bundle的路径。

您可以使用此路径检查应用容器中的数据。 此外,在调试问题时,模拟器日志确实非常有用。

现在,在终端中键入以下内容:

xcrun simctl spawn Demo log stream

这将开始从Demo模拟器流式传输所有日志。

但是,这可能是要检查的数据过多。 按Control-C停止流。

请尝试以下操作:

xcrun simctl spawn Demo log stream | grep com.raywenderlich.RayWonders

这将过滤日志并仅显示来自RayWonders的日志。

与应用互动时,您会在终端中看到更多日志。 在提交bug或向Apple提供反馈时,这些日志非常有用。

输入以下内容:

xcrun simctl diagnose

diagnose命令收集一堆数据,包括日志和崩溃。 它还会生成可帮助Apple调试问题的文件。 默认情况下,收集的日志用于引导的设备。 通过指定设备的UDID,您可以将日志收集限制到该特定设备。

您现在已成为模拟器命令行专家! 您可以通过使用Bash脚本自动执行常见操作来更进一步。


Automating Using a Bash Script

现在,您将创建一个脚本来克隆模拟器并在其他区域启动RayWonders

首先,打开终端,然后输入以下内容:

touch sim_utility.sh

这将创建一个名为sim_utility的新文件。 .sh扩展名表示它是一个shell脚本。

注意:您可以在文件系统中的任何位置创建脚本。 为了便于示例项目的组织,该脚本位于“Scripts”文件夹下。

接下来,运行:

chmod +x sim_utility.sh

这使sim_utility.sh可执行,因此您可以运行它。

在编辑器中打开sim_utility.sh,并添加以下内容:

#!/bin/bash

#1
COMMAND="$1"
SIMULATOR_NAME="$2"

#2
get_id_of_simulator() {
  #TODO
}

get_status_of_simulator() {
  #TODO
}

launch() {
  #TODO
}

#3
case $COMMAND in
"launch") launch "$3" "$4" "$5" ;;
*)
  launch_help
  exit 1
  ;;
esac

这是怎么回事:

  • 1) 该脚本采用命令名称和模拟器名称作为命令行参数。
  • 2) get_id_of_simulator,get_status_of_simulatorlaunch是接下来要实现的空存根。
  • 3) 该脚本当前支持一个名为launch的命令。 此launch使用三个附加参数,您将很快实现这些参数。

get_id_of_simulator中,将#TODO替换为以下内容:

xcrun simctl list | grep "$SIMULATOR_NAME" | \
awk 'match($0, /\(([-0-9A-F]+)\)/) {print substr( $0, RSTART + 1, RLENGTH-2 )}'

get_id_of_simulator搜索模拟器列表以匹配指定为参数的名称。 如果找到匹配项,则子字符串操作将获得模拟器的唯一标识符。

get_status_of_simulator方法中,将#TODO替换为以下内容:

xcrun simctl list | grep "$SIMULATOR_NAME" | \
awk 'match($0, /\(([a-zA-Z]+)\)/) {print substr( $0, RSTART + 1, RLENGTH - 2 )}'

get_status_of_simulator在设备列表中搜索名称匹配的模拟器。 如果找到匹配项,它将获得模拟器的状态 —— bootedshutdown。 您将在launch中使用它们。

1. Implementing Launch

launch时,将#TODO替换为以下内容:

#1
BUNDLE_ID="$1"
LOCALE="$2"
LANGUAGE="$3"
CLONE_NAME="$LOCALE"

#2
SIMULATOR_ID=$(get_id_of_simulator)
if [ -z "$SIMULATOR_ID" ]; then
  echo "No device matching the name: $SIMULATOR_NAME"
  return
fi

echo "Simulator id: $SIMULATOR_ID"
echo "Bundle id: $BUNDLE_ID"
echo "Locale: $LOCALE"

#3
SIMULATOR_STATUS=$(get_status_of_simulator)
echo "Simulator Status: $SIMULATOR_STATUS"
if [ "$SIMULATOR_STATUS" = "Booted" ]; then
  echo "Making sure the device is shutdown first..."
  xcrun simctl shutdown "$SIMULATOR_NAME"
fi

#4
echo "Cloning the device with name $LOCALE..."
xcrun simctl clone "$SIMULATOR_NAME" "$CLONE_NAME"

#5
echo "Booting device..."
xcrun simctl boot "$SIMULATOR_NAME"
xcrun simctl boot "$CLONE_NAME"

#6
echo "Launching app..."
xcrun simctl launch "$SIMULATOR_NAME" "$BUNDLE_ID"

#7
xcrun simctl launch "$CLONE_NAME" "$BUNDLE_ID" -AppleLocale "$LOCALE" \
  -AppleLanguages "($LANGUAGE)"

保存文件。这是逐步执行的操作。它:

  • 1) 提供其他launch参数,包括应用程序的bundle ID,语言环境和启动应用程序所使用的语言。它声明一个本地变量CLONE_NAME,它是克隆设备的名称。在这种情况下,克隆设备的名称就是您指定的语言环境;
  • 2) 通过调用get_id_of_simulator来获取模拟器的ID。然后,将结果存储在SIMULATOR_ID中。如果没有匹配的模拟器,则退出程序。
  • 3) 通过调用get_status_of_simulator获取模拟器的状态。如果状态为Booted,则它将运行shutdown命令将其关闭。要克隆,必须关闭设备。
  • 4) 使用clone命令克隆模拟器;
  • 5) 使用boot命令引导原始模拟器和克隆的模拟器;
  • 6) 使用launch命令在原始模拟器上启动应用程序;
  • 7) 使用克隆的模拟器上的launch命令启动应用程序。它指定启动应用程序的语言环境和语言。

2. Launching RayWonders in a Different Locale

打开终端,然后运行以下命令:

./sim_utility.sh launch Demo com.raywenderlich.RayWonders hi_IN hi

RayWonders在印地语上使用克隆的模拟器hi_IN启动。

无需再进行设置并切换设备语言来测试您的应用。 现在,您可以同时以多种语言查看您的应用。 很好!

注意:如果在运行脚本时遇到问题,请确保只有一个名为Demo的模拟器。 您可以通过运行xcrun simctl list | grep Demo检查可用的模拟器数量。删除所有重复的模拟器,然后重试。

RayWonders还支持其他几种语言。 运行以下命令:

./sim_utility.sh launch Demo com.raywenderlich.RayWonders ja_Jp ja

RayWonders现在在新的模拟器上以日语启动。

作为一个有趣的挑战,在sim_utility脚本中实现另外两个命令。

  • Cleanup:这将删除给定模拟器名称的模拟器。
  • Help:这将打印脚本支持的所有命令的菜单。

您可以在下载资料的Scripts文件夹中找到该脚本的最终版本以及所有这些选项。

在本教程中,您学习了很多有用的模拟器和命令行选项。 要了解更多信息,请观看以下WWDC视频:

后记

本篇主要讲述了Xcode Simulator的高级功能,感兴趣的给个赞或者关注~~~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容