AFNetworking 3.x 详解二

比如我们发生一个GET请求时,会调用到如下代码:

__block NSURLSessionDataTask *dataTask = nil;
dataTask = [self dataTaskWithRequest:request
                      uploadProgress:uploadProgress
                    downloadProgress:downloadProgress
                   completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
    if (error) {
        if (failure) {
            failure(dataTask, error);
        }
    } else {
        if (success) {
            success(dataTask, responseObject);
        }
    }
}]; 

该方法就是使用指定的请求去创建一个NSURLSessionDataTask,那么内部是如何实现的呢?

** AFURLSessionManager ** 基于指定的 NSURLSessionConfiguration 创建和管理了一个 NSURLSession ,并遵循了NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate 这四个协议。

NSURLSession & NSURLSessionTask Delegate 方法

AFURLSessionManager 实现了以下代理方法:

NSURLSessionDelegate

  • URLSession:didBecomeInvalidWithError:
  • URLSession:didReceiveChallenge:completionHandler:
  • URLSessionDidFinishEventsForBackgroundURLSession:

NSURLSessionTaskDelegate

  • URLSession:willPerformHTTPRedirection:newRequest:completionHandler:
  • URLSession:task:didReceiveChallenge:completionHandler:
  • URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:
  • URLSession:task:needNewBodyStream:
  • URLSession:task:didCompleteWithError:

NSURLSessionDataDelegate

  • URLSession:dataTask:didReceiveResponse:completionHandler:
  • URLSession:dataTask:didBecomeDownloadTask:
  • URLSession:dataTask:didReceiveData:
  • URLSession:dataTask:willCacheResponse:completionHandler:

NSURLSessionDownloadDelegate

  • URLSession:downloadTask:didFinishDownloadingToURL:
  • URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesWritten:totalBytesExpectedToWrite:
  • URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:

AFURLSessionManager 包含如下属性:

  • NSURLSession *session; 创建的NSURLSession对象
  • NSOperationQueue *operationQueue;回调队列
    • id <AFURLResponseSerialization> responseSerializer;服务器返回的响应序列化
    • AFSecurityPolicy *securityPolicy;安全政策
    • AFNetworkReachabilityManager *reachabilityManager;监测网络的可达性
    • NSArray <NSURLSessionTask *> *tasks;当前session下的所以任务组
    • NSArray <NSURLSessionDataTask *> *dataTasks;当前正在执行datatask数组
    • NSArray <NSURLSessionUploadTask *> *uploadTasks当前正在执行上传task数组
    • NSArray <NSURLSessionDownloadTask *> *downloadTasks;当前正在执行的下载task数组
    • dispatch_queue_t completionQueue; The dispatch queue for completionBlock
    • dispatch_group_t completionGroup; The dispatch group for completionBlock
    • BOOL attemptsToRecreateUploadTasksForBackgroundSessions;是否尝试重试后台会话的上载任务的创建

我们从初始化方法开始分析:

- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
    self = [super init];
    if (!self) {
        return nil;
    }

    if (!configuration) {   //不存在则初始化默认会话配置
        configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    }

    self.sessionConfiguration = configuration;

    self.operationQueue = [[NSOperationQueue alloc] init];
    self.operationQueue.maxConcurrentOperationCount = 1;    //设置最大并发数为1,即为串行执行

    self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];

    self.responseSerializer = [AFJSONResponseSerializer serializer];
    //设置证书认证
    self.securityPolicy = [AFSecurityPolicy defaultPolicy];

    #if !TARGET_OS_WATCH
    self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
    #endif

    self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
    //添加锁,确保mutableTaskDelegatesKeyedByTaskIdentifier读写线程安全
    self.lock = [[NSLock alloc] init];
    self.lock.name = AFURLSessionManagerLockName;
    
    //为每个task设置代码块,同时也保证后台`session`安全
    [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
        for (NSURLSessionDataTask *task in dataTasks) {
            [self addDelegateForDataTask:task uploadProgress:nil downloadProgress:nil completionHandler:nil];
        }

        for (NSURLSessionUploadTask *uploadTask in uploadTasks) {
            [self addDelegateForUploadTask:uploadTask progress:nil completionHandler:nil];
        }

        for (NSURLSessionDownloadTask *downloadTask in downloadTasks) {
            [self addDelegateForDownloadTask:downloadTask progress:nil destination:nil completionHandler:nil];
        }
    }];

    return self;
}

接下来我们回到请求的方法中:

- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
                               uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
                             downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
                            completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject,  NSError * _Nullable error))completionHandler {

    __block NSURLSessionDataTask *dataTask = nil;
    url_session_manager_create_task_safely(^{
        //根据请求创建task
        dataTask = [self.session dataTaskWithRequest:request];
    });

    //会初始化`AFURLSessionManagerTaskDelegate`对象,添加对应的下载进度,下载完成回调,设置代理,线程安全的根据`task.taskIdentifier`添加delegate,并通过AFNSURLSessionTaskDidResumeNotification和AFNSURLSessionTaskDidSuspendNotification设置暂停和恢复的通知方法。也就是说`mutableTaskDelegatesKeyedByTaskIdentifier `通过key为`taskIdentifier `来管理对应的task。
    [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];

    return dataTask;
}

然后返回dataTask,并且启动[dataTask resume];

接下来便是启动下载任务执行对应的回调方法,然后在相应的方法中通过block进行通信。

  • NSURLSessionDelegate
  • ``NSURLSessionTaskDelegate`
  • NSURLSessionDataDelegate
  • ``NSURLSessionDownloadDelegate`
屏幕快照 2017-07-12 上午10.50.42.png

举个🌰:

- (void)URLSession:(NSURLSession *)session
didBecomeInvalidWithError:(NSError *)error
{
  //当当前Sesssion由于错误原因不可用时则会调用sessionDidBecomeInvalid进行通知
    if (self.sessionDidBecomeInvalid) {
        self.sessionDidBecomeInvalid(session, error);
    }

    [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDidInvalidateNotification object:session];
}

@property (readwrite, nonatomic, copy) AFURLSessionDidBecomeInvalidBlock sessionDidBecomeInvalid;

typedef void (^AFURLSessionDidBecomeInvalidBlock)(NSURLSession *session, NSError *error);

//当访问HTTPS的路径时会调用该方法,处理服务器返回的证书
//NSURLAuthenticationChallenge : 授权质问
- (void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
 completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
    NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
    __block NSURLCredential *credential = nil;

    if (self.sessionDidReceiveAuthenticationChallenge) {
      //sessionDidReceiveAuthenticationChallenge存在则返回当前证书信任类型
        disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential);
    } else {
     //从服务器返回的受保护空间中拿到证书的类型,判断是否是受信任的,这里涉及到`AFSecurityPolicy`
        if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
            if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
              //如果服务端返回的证书是可信任的,则创建一个指定握手是可信的NSURLCredential,并返回证书处理方式
                credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
                if (credential) {
                    disposition = NSURLSessionAuthChallengeUseCredential;
                } else {
                    disposition = NSURLSessionAuthChallengePerformDefaultHandling;
                }
            } else {
                disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
            }
        } else {
            disposition = NSURLSessionAuthChallengePerformDefaultHandling;
        }
    }

    if (completionHandler) {
        completionHandler(disposition, credential);
    }
}dReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
#pragma mark - NSURLSessionTaskDelegate
//HTTP请求尝试对另一个URL执行重定向时调用
- (void)URLSession:(NSURLSession *)session
              task:(NSURLSessionTask *)task
willPerformHTTPRedirection:(NSHTTPURLResponse *)response
        newRequest:(NSURLRequest *)request
 completionHandler:(void (^)(NSURLRequest *))completionHandler
{
    NSURLRequest *redirectRequest = request;

    if (self.taskWillPerformHTTPRedirection) {
        redirectRequest = self.taskWillPerformHTTPRedirection(session, task, response, request);
    }

    if (completionHandler) {
        completionHandler(redirectRequest);
    }
}
//当一个session task需要发送一个新的request body stream到服务器端的时候,调用该代理方法。
- (void)URLSession:(NSURLSession *)session
              task:(NSURLSessionTask *)task
 needNewBodyStream:(void (^)(NSInputStream *bodyStream))completionHandler
{
    NSInputStream *inputStream = nil;

    if (self.taskNeedNewBodyStream) {
        inputStream = self.taskNeedNewBodyStream(session, task);
    } else if (task.originalRequest.HTTPBodyStream && [task.originalRequest.HTTPBodyStream conformsToProtocol:@protocol(NSCopying)]) {
        inputStream = [task.originalRequest.HTTPBodyStream copy];
    }

    if (completionHandler) {
        completionHandler(inputStream);
    }
}
//定期发送通知通知上传进度,这些信息也可作为任务的属性
- (void)URLSession:(NSURLSession *)session
              task:(NSURLSessionTask *)task
   didSendBodyData:(int64_t)bytesSent
    totalBytesSent:(int64_t)totalBytesSent
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
{

    int64_t totalUnitCount = totalBytesExpectedToSend;
    if(totalUnitCount == NSURLSessionTransferSizeUnknown) {
      //读取出回调的总字节数,如果为NSURLSessionTransferSizeUnknown,则使用HTTP header中的Content-Length作为totalUnitCount
        NSString *contentLength = [task.originalRequest valueForHTTPHeaderField:@"Content-Length"];
        if(contentLength) {
            totalUnitCount = (int64_t) [contentLength longLongValue];
        }
    }
    //根据task.taskIdentifier线程安全的从mutableTaskDelegatesKeyedByTaskIdentifier中取出AFURLSessionManagerTaskDelegate
    AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
    
    if (delegate) {
      //delegate存在则执行该方法为self.uploadProgress的totalUnitCount和completedUnitCount赋值,通知总共要发送多少,已经发送了多少,将该代理方法转发到自定义的delegate中
        [delegate URLSession:session task:task didSendBodyData:bytesSent totalBytesSent:totalBytesSent totalBytesExpectedToSend:totalBytesExpectedToSend];
    }
//同时在taskDidSendBodyData中把上传进度等回调
    if (self.taskDidSendBodyData) {
        self.taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalUnitCount);
    }
}
//作为与特定任务相关的最后一条消息发送。错误可能为nil,这意味着此任务完成没有发生错误
- (void)URLSession:(NSURLSession *)session
              task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
    AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];

    //在后台完成任务时,委托可能为nil
    if (delegate) {
        //将该方法转发给AFURLSessionManagerTaskDelegate类
        [delegate URLSession:session task:task didCompleteWithError:error];
        //根据task.taskIdentifier从mutableTaskDelegatesKeyedByTaskIdentifier线程安全的移除delegate
        [self removeDelegateForTask:task];
    }

    if (self.taskDidComplete) {
        self.taskDidComplete(session, task, error);
    }
}

我们来看下AF内部在回调完成做了什么:

#pragma mark - NSURLSessionTaskDelegate
//第186行
- (void)URLSession:(__unused NSURLSession *)session
              task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
  //因为self.manager使用的weak属性修饰符,为了防止中途释放__strong一次
    __strong AFURLSessionManager *manager = self.manager;

    __block id responseObject = nil;

    __block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
    userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;

    //Performance Improvement from #2672
    NSData *data = nil;
    if (self.mutableData) {
        data = [self.mutableData copy];
        //We no longer need the reference, so nil it out to gain back some memory.
        self.mutableData = nil;
    }

    if (self.downloadFileURL) {
        userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
    } else if (data) {
        userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data;
    }

    if (error) {
        userInfo[AFNetworkingTaskDidCompleteErrorKey] = error;
        //处理错误,如果completionGroup不存在则使用私有的url_session_manager_completion_group,completionQueue为空则使用dispatch_get_main_queue(),去异步更新AFURLSessionTaskCompletionHandler
        dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
            if (self.completionHandler) {
                self.completionHandler(task.response, responseObject, error);
            }

            dispatch_async(dispatch_get_main_queue(), ^{
                [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
            });
        });
    } else {
      //正确情况下
        dispatch_async(url_session_manager_processing_queue(), ^{
          //根据对应的task和data将response根据AFHTTPResponseSerializer解析成可用的数据格式
            NSError *serializationError = nil;
            responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];

            if (self.downloadFileURL) {
                responseObject = self.downloadFileURL;
            }

            if (responseObject) {
                userInfo[AFNetworkingTaskDidCompleteSerializedResponseKey] = responseObject;
            }

            if (serializationError) {
                userInfo[AFNetworkingTaskDidCompleteErrorKey] = serializationError;
            }

            dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
                if (self.completionHandler) {
                    self.completionHandler(task.response, responseObject, serializationError);
                }

                dispatch_async(dispatch_get_main_queue(), ^{
                    [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
                });
            });
        });
    }
}
#pragma mark - NSURLSessionDataDelegate
//任务已收到响应并且直到完成块被调用时不会收到消息
- (void)URLSession:(NSURLSession *)session
          dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
 completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler
{
    NSURLSessionResponseDisposition disposition = NSURLSessionResponseAllow;

    if (self.dataTaskDidReceiveResponse) {
        disposition = self.dataTaskDidReceiveResponse(session, dataTask, response);
    }

    if (completionHandler) {
        completionHandler(disposition);
    }
}

//如下的几个代理方法都是类似的,`didBecomeDownloadTask` `didReceiveData` `willCacheResponse` 都是接受数据之后使用block将其进行回调通知
//如果接收到了-application:handleEventsForBackgroundURLSession:completionHandler:消息,这就意味着之前这个session中已经入队的所有消息都转发出去了,此时调用以前存储的completion handler是安全的,也可以启动任何导致completion handler处理程序的内部更新
- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {
    if (self.didFinishEventsForBackgroundURLSession) {
        dispatch_async(dispatch_get_main_queue(), ^{
            self.didFinishEventsForBackgroundURLSession(session);
        });
    }
}
#pragma mark - NSURLSessionDownloadDelegate
//当完成下载任务时调用。委托应该从指定位置复制或移动文件到一个新位置,因为当委托消息返回时,该文件将被删除。
- (void)URLSession:(NSURLSession *)session
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
    AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];
    if (self.downloadTaskDidFinishDownloading) {
        NSURL *fileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
        if (fileURL) {
            delegate.downloadFileURL = fileURL;
            NSError *error = nil;
            //如果移动文件到指定位置失败则发送对应通知
            if (![[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&error]) {
                [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:error.userInfo];
            }

            return;
        }
    }

    if (delegate) {
      //delegate去转发给实现的代理方法
        [delegate URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location];
    }
}

对应的这两个方法的功能也是类似的:

//定期发送通知下载进度的委托
- (void)URLSession:(NSURLSession *)session
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
      didWriteData:(int64_t)bytesWritten
 totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
//用于断点续传,当下载任务恢复时调用,如果下载因为错误失败,那么userInfo字典将包含一个NSURLSessionDownloadTaskResumeData的key,value是恢复的数据
- (void)URLSession:(NSURLSession *)session
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
 didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes

网络请求的问题解决了,那如何进行下载和暂停呢,换句话说断点续传是如何控制的呢?之前在第697行有个方法,- (void)addNotificationObserverForTask:(NSURLSessionTask *)task该方法是注册通知的形式,如果需要调用则需要发送AFNSURLSessionTaskDidResumeNotificationAFNSURLSessionTaskDidSuspendNotification,那我们顺藤摸瓜来找下,发现在**_AFURLSessionTaskSwizzling **中有我们想要的[[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidResumeNotification object:self];

_AFURLSessionTaskSwizzling

+ load 作为 Objective-C 中的一个方法,与其它方法有很大的不同。它只是一个在整个文件被加载到运行时,在 main 函数调用之前被 ObjC 运行时调用的钩子方法,load 会在类或分类被添加到runtime时调用


+ (void)load {
    if (NSClassFromString(@"NSURLSessionTask")) {
        /**
         Some Assumptions:
            - No implementations of `resume` or `suspend` call super. If this were to change in a future version of iOS, we'd need to handle it.
            - No background task classes override `resume` or `suspend`
        **/
        NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
        NSURLSession * session = [NSURLSession sessionWithConfiguration:configuration];
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnonnull"
      //通过`NSURLSession`来创建一个NSURLSessionDataTask实例
        NSURLSessionDataTask *localDataTask = [session dataTaskWithURL:nil];
#pragma clang diagnostic pop
      //获取`af_resume`的方法实现的指针
        IMP originalAFResumeIMP = method_getImplementation(class_getInstanceMethod([self class], @selector(af_resume)));
        Class currentClass = [localDataTask class];
        //检查当前类是否实现了`resume`
        while (class_getInstanceMethod(currentClass, @selector(resume))) {
          //如果实现了。则获取当前类的父类
            Class superClass = [currentClass superclass];
          //获取当前实现`resume`的当前类指针
            IMP classResumeIMP = method_getImplementation(class_getInstanceMethod(currentClass, @selector(resume)));
          //获取当前实现`resume`的父类指针
            IMP superclassResumeIMP = method_getImplementation(class_getInstanceMethod(superClass, @selector(resume)));
          //如果当前类实现的`resume`和父类实现的`resume`不相等 && 当前类实现的`resume`和`af_resume`实现也不相等则交换方法
            if (classResumeIMP != superclassResumeIMP &&
                originalAFResumeIMP != classResumeIMP) {
                [self swizzleResumeAndSuspendMethodForClass:currentClass];
            }
          //设置当前类为当前类的父类,重复以上步骤
            currentClass = [currentClass superclass];
        }
        
        [localDataTask cancel];
        [session finishTasksAndInvalidate];
    }AFHTTPResponseSerializer
}
- (void)af_resume {
    NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
    NSURLSessionTaskState state = [self state];
    [self af_resume];
    //这里需要说明一下,因为之前已经调用`method_exchangeImplementations`对换了`resume`方法和`af_resume`,所以该处调用[self af_resume]实际上就是调用系统的resume方法
    if (state != NSURLSessionTaskStateRunning) {
        [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidResumeNotification object:self];
    }
}

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

推荐阅读更多精彩内容