RunLoop(二)

RunLoop的运行逻辑

本文Demo代码可见gitHub_Demo

runloop_03.png
runloop_04.png
runloop_05.png

查看源码:

CF-1151.16 CFRunLoop.c

1.CFRunLoopRunSpecific(RunLoop的实现)
2.CFRunLoopRunInMode(用指定的Mode启动,允许设置RunLoop超时时间)
3.__CFRunLoopRun(RunLoop的执行)
runloop_06.png

  • 苹果用 RunLoop 实现的功能(详见:深入理解RunLoop

    • 手势识别
    • 界面更新
    • 定时器
    • PerformSelector
    • AutoreleasePool
    • 事件响应
    • 关于GCD
    • 网络请求
    • ...
  • 我们在开发中的使用

    • NSTimer在滑动时停止工作的问题
    • 控制线程生命周期(线程保活)
    • 监控应用卡顿
    • 性能优化
    • ...

     NSTimer失效:
     我们现在界面先创建一个UITextView、
     
     static int count = 0;
     NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
            NSLog(@"%d", ++count);
     }];
    
     //该模式下:滚动UITextView-->timer不会打印、松开则继续打印
     [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
     
     //该模式下:可以正常的打印
     // NSDefaultRunLoopMode、UITrackingRunLoopMode才是真正存在的模式
     // NSRunLoopCommonModes并不是一个真的模式,它只是一个标记
     // timer能在_commonModes数组中存放的模式下工作
     [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
     
     
     ==================================
    应用卡顿检测:
    平时所说的“卡顿"主要是因为在主线程执行了比较耗时的操作 
    可以添加Observer到主线程RunLoop中,通过监听RunLoop状态切换的耗时,以达到监控卡顿的目的
    使用:https://github.com/UIControl/LXDAppFluecyMonitor
    
    ===================================
    
    性能优化:
    使用:https://www.jianshu.com/p/30f7ced70083
    Demo地址:https://github.com/qiaoyoung/RunLoop 
    

    下面我们看看怎样,线程保活?

    Test01:

    YDThread1.h
    #import <Foundation/Foundation.h>
    @interface YDThread1 : NSThread
    @end
    
    YDThread1.m
    #import "YDThread1.h"
    @implementation YDThread1
    -(void)dealloc{
        NSLog(@"%s",__FUNCTION__);
    }
    @end
    
    TestVC01.m
    #import "TestVC01.h"
    #import "YDThread1.h"
    
    @implementation TestVC01
    -(void)viewDidLoad{
        [super viewDidLoad];
        
        self.view.backgroundColor = [UIColor whiteColor];
        
    }
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        YDThread1 *thread = [[YDThread1 alloc]initWithTarget:self selector:@selector(run) object:nil];
        [thread start];
    }
    -(void)run{
        NSLog(@"%s - %@",__func__,[NSThread currentThread]);
    }
    @end
    
    打印:
    Runloop[2520:239171] -[TestVC01 run] - <YDThread1: 0x6000018c2440>{number = 3, name = (null)}
    Runloop[2520:239171] -[YDThread1 dealloc]
    Runloop[2520:239175] -[TestVC01 run] - <YDThread1: 0x6000018c1b40>{number = 4, name = (null)}
    Runloop[2520:239175] -[YDThread1 dealloc]
    Runloop[2520:239205] -[TestVC01 run] - <YDThread1: 0x6000018ce9c0>{number = 5, name = (null)}
    Runloop[2520:239205] -[YDThread1 dealloc]
    
    说明:
    每次点击总会创建一个子线程来处理事件(执行完会自动释放)
    这样会很消耗资源
    

    Test02:

    YDThread2.h
    #import <Foundation/Foundation.h>
    @interface YDThread2 : NSThread
    @end
    
    YDThread2.m
    #import "YDThread2.h"
    @implementation YDThread2
    -(void)dealloc{
        NSLog(@"%s",__FUNCTION__);
    }
    @end
    
    TestVC02.m
    #import "TestVC02.h"
    #import "YDThread2.h"
    
    @interface TestVC02 ()
    @property (nonatomic,strong)YDThread2 *thread;
    @end
    
    @implementation TestVC02
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.view.backgroundColor = [UIColor whiteColor];
        self.thread = [[YDThread2 alloc]initWithTarget:self selector:@selector(run) object:nil]; //循环引用啦
        [self.thread start];
    }
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        [self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:NO];
    //    [self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:YES]; //YES会崩溃
       //如果CPU在运行当前线程对象的时候线程任务执行完毕\异常强制退出,则当前线程对象进入死亡状态
        NSLog(@"123");
    }
    
    -(void)test{
        NSLog(@"%s - %@",__func__,[NSThread currentThread]);
    }
    
    -(void)run{
        NSLog(@"%s - %@",__func__,[NSThread currentThread]);
    }
    
    -(void)dealloc{
        NSLog(@"%s",__func__);
    }
    @end
    
    打印:
    Runloop[2666:248192] -[TestVC02 run] - <YDThread2: 0x600002265480>{number = 4, name = (null)}
    Runloop[2666:247106] 123
    Runloop[2666:247106] 123
    Runloop[2666:247106] 123
    Runloop[2666:247106] 123
    Runloop[2666:247106] 123
    Runloop[2666:247106] 123
    

    Test03:

    YDThread3.h
    #import <Foundation/Foundation.h>
    @interface YDThread3 : NSThread
    @end
    
    YDThread3.m
    #import "YDThread3.h"
    @implementation YDThread3
    -(void)dealloc{
        NSLog(@"%s",__FUNCTION__);
    }
    @end
    
    TestVC03.h
    #import <UIKit/UIKit.h>
    @interface TestVC03 : UIViewController
    @end
    
    TestVC03.m
    #import "TestVC03.h"
    #import "YDThread3.h"
    
    @interface TestVC03 ()
    @property (nonatomic,strong)YDThread3   *thread;
    @end
    
    @implementation TestVC03
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.view.backgroundColor = [UIColor whiteColor];
        self.thread = [[YDThread3 alloc] initWithBlock:^{
            NSLog(@"%s %@", __func__, [NSThread currentThread]);
            NSLog(@"%@----begin----", [NSThread currentThread]);
            // 往RunLoop里面添加Source\Timer\Observer (这样RunLoop才有效)
            [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
            [[NSRunLoop currentRunLoop] run];
            NSLog(@"%s ----end----", __func__);
        }];
        [self.thread start];
    }
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        [self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:NO];
        NSLog(@"123");
    }
    
    -(void)test{
        NSLog(@"%s - %@",__func__,[NSThread currentThread]);
    }
    
    /*
    //这样控制器可以被释放了,但是thread没有被释放
    -(void)dealloc{
        NSLog(@"%s",__func__);
    }
     */
    
    
    /*
    //这样也不会被释放  代码一直会卡在 [[NSRunLoop currentRunLoop] run];这里,任务还没结束,线程就不会死
    -(void)dealloc{
        NSLog(@"%s",__func__);
        self.thread = nil ;
    }
    */
    
    //这样 写也不对:这样只是在停掉主线程的RunLoop并非该RunLoop
    -(void)dealloc{
        // 停止RunLoop
        CFRunLoopStop(CFRunLoopGetCurrent());
        NSLog(@"%s",__func__);
    }
    @end
    

    Test04:

    YDThread4.h
    #import <Foundation/Foundation.h>
    @interface YDThread4 : NSThread
    @end
    
    YDThread4.m
    #import "YDThread4.h"
    @implementation YDThread4
    -(void)dealloc{
        NSLog(@"%s",__FUNCTION__);
    }
    @end
    
    TestVC04.h
    #import <UIKit/UIKit.h>
    @interface TestVC04 : UIViewController
    @end
    
    TestVC04.m
    #import "TestVC04.h"
    #import "YDThread4.h"
    
    @interface TestVC04 ()
    @property (nonatomic,strong)YDThread4   *thread;
    @end
    
    @implementation TestVC04
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.view.backgroundColor = [UIColor whiteColor];
        
        //控制器销毁了,但是线程还在  NSRunLoop的run方法是无法停止的,它专门用于开启一个永不销毁的线程(NSRunLoop)
        self.thread = [[YDThread4 alloc] initWithBlock:^{
            NSLog(@"%s %@", __func__, [NSThread currentThread]);
            NSLog(@"%@----begin----", [NSThread currentThread]);
            // 往RunLoop里面添加Source\Timer\Observer (这样RunLoop才有效)
            [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
            [[NSRunLoop currentRunLoop] run];
            NSLog(@"%s ----end----", __func__);
        }];
        [self.thread start];
        
        /*
        //这样写:只会执行一次,自动会结束的哟
        self.thread = [[YDThread4 alloc] initWithBlock:^{
            NSLog(@"%@----begin----", [NSThread currentThread]);
                // 往RunLoop里面添加Source\Timer\Observer
                [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
            
                [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
                NSLog(@"%@----end----", [NSThread currentThread]);
            }];
        [self.thread start];
       */
        
        UIButton *btn = [[UIButton alloc]initWithFrame:CGRectMake(100, 100, 100, 60)];
        [self.view addSubview:btn];
        [btn setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
        [btn setTitle:@"stop" forState:UIControlStateNormal];
        [btn addTarget:self action:@selector(stopClick) forControlEvents:UIControlEventTouchUpInside];
    }
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        [self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:NO];
        NSLog(@"123");
    }
    
    -(void)test{
        NSLog(@"%s - %@",__func__,[NSThread currentThread]);
    }
    
    - (void)stopClick{
        [self performSelector:@selector(stop1) onThread:self.thread withObject:nil waitUntilDone:NO];
    }
    
    // 用于停止子线程的RunLoop
    -(void)stop1{
        // 停止RunLoop
        CFRunLoopStop(CFRunLoopGetCurrent());
        NSLog(@"%s - %@",__func__,[NSThread currentThread]);
    }
    
    -(void)dealloc{
        NSLog(@"%s",__func__);
    }
    @end
    

    Test05:

    YDThread5.h
    #import <Foundation/Foundation.h>
    @interface YDThread5 : NSThread
    @end
    
    YDThread5.m
    #import "YDThread5.h"
    @implementation YDThread5
    -(void)dealloc{
        NSLog(@"%s",__FUNCTION__);
    }
    @end
    
    TestVC05.h
    #import <UIKit/UIKit.h>
    @interface TestVC05 : UIViewController
    @end
    
    TestVC05.m
    #import "TestVC05.h"
    #import "YDThread5.h"
    
    @interface TestVC05 ()
    @property (nonatomic,strong)YDThread5   *thread;
    @property (assign, nonatomic, getter=isStoped) BOOL stopped;
    @end
    
    @implementation TestVC05
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.view.backgroundColor = [UIColor whiteColor];
        
        __weak typeof(self) weakSelf = self;
        self.stopped = NO;
        self.thread = [[YDThread5 alloc] initWithBlock:^{
            NSLog(@"%s %@", __func__, [NSThread currentThread]);
            NSLog(@"%@----begin----", [NSThread currentThread]);
            while (!weakSelf.isStoped) {
                [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
            }
            NSLog(@"%s ----end----", __func__);
         }];
        [self.thread start];
    
        UIButton *btn = [[UIButton alloc]initWithFrame:CGRectMake(100, 100, 100, 60)];
        [self.view addSubview:btn];
        [btn setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
        [btn setTitle:@"stop" forState:UIControlStateNormal];
        [btn addTarget:self action:@selector(stopClick) forControlEvents:UIControlEventTouchUpInside];
    }
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        [self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:NO];
        NSLog(@"123");
    }
    
    -(void)test{
        NSLog(@"%s - %@",__func__,[NSThread currentThread]);
    }
    
    - (void)stopClick{
        [self performSelector:@selector(stop1) onThread:self.thread withObject:nil waitUntilDone:NO];
    }
    
    // 用于停止子线程的RunLoop
    -(void)stop1{
        // 设置标记为NO
        self.stopped = YES;
        // 停止RunLoop
        CFRunLoopStop(CFRunLoopGetCurrent());
        NSLog(@"%s - %@",__func__,[NSThread currentThread]);
        
        //这样:主动点击stop,线程会跟随控制器而销毁,
        
        //但是如果直接点击返回,会崩溃,
        // [self performSelector:@selector(stop1) onThread:self.thread withObject:nil waitUntilDone:NO];
        //这样并未等子线程执行完 就执行了 "}"了,之后执行dealloc方法,控制器为nil,所以会出现坏内存访问 (vc没了)
    }
    
    -(void)dealloc{
        NSLog(@"%s",__func__);
        [self stopClick];
    }
    @end
    

    Test06(终结版本):

    YDThread6.h
    #import <Foundation/Foundation.h>
    @interface YDThread6 : NSThread
    @end
    
    YDThread6.m
    #import "YDThread6.h"
    @implementation YDThread6
    -(void)dealloc{
        NSLog(@"%s",__FUNCTION__);
    }
    @end
    
    
    TestVC06.h
    #import <UIKit/UIKit.h>
    @interface TestVC06 : UIViewController
    @end
    
    TestVC06.m
    #import "TestVC06.h"
    #import "YDThread6.h"
    
    @interface TestVC06 ()
    @property (nonatomic,strong)YDThread6 *thread;
    @property (assign, nonatomic, getter=isStoped) BOOL stopped;
    @end
    
    @implementation TestVC06
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.view.backgroundColor = [UIColor whiteColor];
        __weak typeof(self) weakSelf = self;
        self.stopped = NO;
        self.thread = [[YDThread6 alloc] initWithBlock:^{
            NSLog(@"%@----begin----", [NSThread currentThread]);
            while (weakSelf && !weakSelf.isStoped) {
                [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
            }
            NSLog(@"%s ----end----", __func__);
        }];
        [self.thread start];
        
        UIButton *btn = [[UIButton alloc]initWithFrame:CGRectMake(100, 100, 100, 60)];
        [self.view addSubview:btn];
        [btn setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
        [btn setTitle:@"stop" forState:UIControlStateNormal];
        [btn addTarget:self action:@selector(stopClick) forControlEvents:UIControlEventTouchUpInside];
        
    }
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        if (!self.thread) return;
        [self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:NO];
    }
    
    -(void)test{
        NSLog(@"%s - %@",__func__,[NSThread currentThread]);
    }
    
    -(void)stopClick{
        if (!self.thread) return;
        //在子线程调用stop(waitUntilDone设置为YES,代表子线程的代码执行完毕后,这个方法才会往下走)
        [self performSelector:@selector(stopThread) onThread:self.thread withObject:nil waitUntilDone:YES];
    }
    
    -(void)stopThread{
        // 设置标记为NO
        self.stopped = YES;
        // 停止RunLoop
        CFRunLoopStop(CFRunLoopGetCurrent());
        NSLog(@"%s - %@",__func__,[NSThread currentThread]);
        // 清空线程
        self.thread = nil;
    }
    
    -(void)dealloc{
        NSLog(@"%s",__func__);
        [self stopClick];
    }
    @end
    

    封装

    YDPermenantThread.h
    #import <Foundation/Foundation.h>
    typedef void (^YDPermenantThreadTask)(void);
    @interface YDPermenantThread : NSObject
    /**
     在当前子线程执行一个任务
     */
    - (void)executeTask:(YDPermenantThreadTask)task;
    
    /**
     结束线程
     */
    - (void)stop;
    @end
    
    YDPermenantThread.m
    #import "YDPermenantThread.h"
    @interface YDThread : NSThread
    @end
    
    @implementation YDThread
    -(void)dealloc{
        NSLog(@"%s",__FUNCTION__);
    }
    @end
    
    @interface YDPermenantThread()
    @property (nonatomic,strong)YDThread  *myThread;
    @property (assign, nonatomic, getter=isStopped) BOOL stopped;
    @end
    
    @implementation YDPermenantThread
    -(instancetype)init{
        if(self = [super init]){
            self.stopped = NO;
            __weak typeof(self) weakSelf = self;
            self.myThread = [[YDThread alloc]initWithBlock:^{
                [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc]init] forMode:NSDefaultRunLoopMode];
                while (weakSelf && !weakSelf.isStopped) {
                     [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
                }
            }];
            [self.myThread start];
        }return self;
    }
    
    - (void)executeTask:(YDPermenantThreadTask)task{
        if(!self.myThread || !task)return;
        [self performSelector:@selector(__executeTask:) onThread:self.myThread withObject:task waitUntilDone:NO];
    }
    
    - (void)stop{
        if (!self.myThread) return;
        [self performSelector:@selector(__stop) onThread:self.myThread withObject:nil waitUntilDone:YES];
    }
    
    - (void)dealloc{
        NSLog(@"%s", __func__);
        [self stop];
    }
    
    #pragma mark - private methods
    - (void)__stop{
        self.stopped = YES;
        CFRunLoopStop(CFRunLoopGetCurrent());
        self.myThread = nil;
    }
    
    - (void)__executeTask:(YDPermenantThreadTask)task{
        task();
    }
    @end
    
    
    TestVC07.h
    #import <UIKit/UIKit.h>
    @interface TestVC07 : UIViewController
    @end
    
    TestVC07.m
    #import "TestVC07.h"
    #import "YDPermenantThread.h"
    @interface TestVC07 ()
    @property (nonatomic,strong)YDPermenantThread  *thread;
    @end
    
    @implementation TestVC07
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.view.backgroundColor = [UIColor whiteColor];
        self.thread = [[YDPermenantThread alloc]init];
        
        UIButton *btn = [[UIButton alloc]initWithFrame:CGRectMake(100, 100, 100, 60)];
        [self.view addSubview:btn];
        [btn setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
        [btn setTitle:@"stop" forState:UIControlStateNormal];
        [btn addTarget:self action:@selector(stopClick) forControlEvents:UIControlEventTouchUpInside];
    }
    
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        [self.thread executeTask:^{
             NSLog(@"Do something- %@", [NSThread currentThread]);
        }];
    }
    
    -(void)stopClick{
        [self.thread stop];
    }
    
    -(void)dealloc{
        NSLog(@"%s",__FUNCTION__);
    }
    @end
    

    总结:

    1.线程的任务一旦执行完毕,生命周期就结束,无法再使用(没有使用runloop保活)
    2.保住线程的命为什么要用runloop,用强指针不就好了么?
      准确来讲,使用runloop是为了让线程保持激活状态
      主线程几乎所有的事情都是交给了runloop去做,比如UI界面的刷新、点击时间的处理、performSelector等等
    

    友情链接:

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