面试题集锦1(更新)

1.类扩展和分类的区别

  • 类扩展:没有名字
    • 可以为某个类增加额外的属性、成员变量和方法
  • 分类:有名字
    • 只能扩充方法,不能扩充成员变量,如果在分类中声明了一个属性,分类只会生成这个属性的getter/setter方法的声明。

2.UIView的frame和bounds区别

  • frame:控件矩形框在父控件中的位置和尺寸——是以父控件的左上角为坐标原点
  • bonus:控件矩形框的位置和尺寸——是以自己的左上角为坐标原点

3.Xcode引用资源的时候,每个选项的含义

  • copy:
    • 勾选copy,会把资源拷贝一份到项目的文件夹中(建议勾选,因为这样修改项目中的资源不会影响源资源)
  • Added folder:
    • 如果勾选Create groups ,只会会创建一个虚拟的文件夹,程序打包后,安装包中不存在这个文件夹
    • 如果勾选Create folder references ,真的创建一个文件夹,程序打包后,安装包中真的有这个文件夹
  • Add to targets :
    • 要不要把资源打包到软件安装包中去,一定要勾选,不勾选到时候程序打包后,安装包中没有这个资源。

4.如何监听scrollView停止滚动

  • 使用代理方法监听scrollView停止滚动
  • 设置代理属性
  • 遵守UIScrolLViewDelegate协议
  • 实现代理方法
    • scrollViewDidEndDragging:已经停止拖拽的时候调用

      • decelerate == NO 的时候,停止滚动
    • scrollViewDidEndDecelerating:减速完毕的时候调用

5.定时器一般有什么作用?以及如何使用定时器

  • 作用:每隔一段时间去做一事件
  • 开启定时器 [NSTimer scheduleTimerWithTimeInterval:target:selector:uerInfo:repeats:]返回的是一个自动执行任务的定时器
  • 关闭定时器:invlidate

6.scrollView的使用场景

  • app首页最新新闻页的分页浏览
  • 音乐播放器下拉刷新
  • 图片缩放

7.UIScrolLView无法滚动的原因

  • uerInteractionEnabled = no
  • scrollEnable = no
  • 没有设置contentSize
  • contentSize的尺寸小于等于scrollView的尺寸

8.scrollEnable和userInteractionEnable的区别

  • scrollViewEnabled 是不能滚动,界面还可与用户进行交互
  • userInteractionEnable如果设置为no,那么用户不能与外界进行交互了,即scrollView以及内部的所有子控件都不能和用户交互了

9.alwaysBounceVertical 和alwaysBounceHorizontal作用

  • 默认是NO
  • 如果设置为YES,可以应用于下拉刷新,并且不管有没有设置contentSize,总是有弹簧效果

10.如何监听UIScrollView的各种行为

  • 通过代理监听scrollView的滚动
  • scrollViewDidScroll:当scrollView正在滚动的时候调用,调用非常频繁
  • scrollViewWillBeginDragging:用户即将开始拖拽的时候调用
  • scrollViewDidEndDragging:已经停止拖拽的时候调用
  • scrollViewDidEndDecelerating:减速完毕的时候调用

11.代理的注意点

  • 任何对象都可以成为scrollView的代理
  • 系统提供的代理属性是用weak修饰的,如果用strong会导致循环引用问题

12.利用UIScrollView如何实现内容缩放

  • 创建scrolLView,在scrollView上面添加子控件
  • 设置contentSize
  • 设置代理属性
  • 遵守代理UIScrollViewDelegate代理协议
  • 实现代理方法
    • viewForZoomingInScrollView:返回需要缩放的子控件
  • 设置缩放比例
    • maxmumScale
    • minmuScale

13.如何监听控件的行为

  • 用delegate属性的可以使用代理方法监听控件的行为
  • 继承自UIConroll的可以使用addTarget:方法

14.用一个属性引用UI控件的时候为什么可以用weak?

  • 因为UI控件添加到父控件中以后,会有强指针指向这个对象,就可以保证这个对象不会被销毁
  • 再搞一个属性引用这个对象,用弱引用就可以

15.如何隐藏一个控件

  • 设置hidden = YES
  • 设置alpha = 0.0

16.如何用按钮来实现图片上、文字下的效果

  • 自定义按钮实现
    • -(CGRect)titleRectForContentRect:(CGRect)contentRect{
      // 返回文字的frame
      }
    • -(CGRect)imageRectForContentRect:(CGRect)contentRect{
      // 返回图片的frame
      }
  • 自定义按钮,实现layoutSubViews方法调整按钮内部子控件的位置和尺寸

17.通过代码如何设置内边距

  • contentEdgeInsets = UIEdgeInsetsMake()
  • titleEdgeInsets = UIEdgeInsetsMake()
  • imageEdgeInsets = UIEdgeInsetsMake()

18.如何处理图片拉伸问题

  • 创建可拉伸的图片
  • resizableImageWithCapInsets:UIEdgeInsetsMake(10,10,10,10) resizingMode:..

19.在xcode中如何配置拉伸图片

  • 选中图片
  • 右边(Slicing)
  • Slices:Horizontal and Vertical
  • 上下左右设置

20.KVC的作用

  • Key Value Coding: 键值编码,可以修改属性的值,并且可以修改私有的成员变量,可以取值

21.通过autolayout如何实现UILabel内容包裹?

  • 设置最大宽度约束为:< = 某一个固定的值
  • 只给UILabel设置位置约束,系统会根据显示的文字,自动计算高度和宽度
  • 动态修改Label显示的文字

22.Masonry使用

  • 注意点:通过代码添加约束,一定要禁止掉translatesAutoresizingMaskIntoConstraints属性
  • 框架内部已经禁止了autoresizingMask转为对应约束的属性
  • mas_makeConstraints:
  • make.top.equalTo(self.view.mas_top).multiplied(1.0).offset(20);
    -make.left.equalTo(self.view.mas_left).multiplied(1.0).offset(20);
  • make.right.equalTo(self.view.mas_right).offset(-20);
    -make.bottom.equalTo(self.view.mas_bottom).offset(20);
- make:谁调用这个方法,就指谁
- multiplied(1.0)可以不写,默认就是乘以一
- (self.view.mas_left)可以写为(self.view)
- equalTo(self.view.mas_top)可以不写,不告诉参照默认就是父控件
- 可以合并
    - make.top.and.left.offset(20);
    - make.right.and.bottom.offset(-20)
- and也可以省略
    - make.top.left.offset(20);
    - make.right.bottom.offset(-20)
- 一句话
    - 完整make.edges.equalTo(self.view).insets(UIEdgeInsets(20,20,-20,-20,))
    - 简单make.edges.insets(UIEdgeInsets(20,20,-20,-20))
  • 补充
    • mas_equalTo(传入基本数据类型进行包装,包装成对象)
    • equalTo(只能传入对象,不能将数据类型包装成对象)
    • 将框架PCH文件中的宏导入项目中,就不用写mas_前缀了
    • 包含了宏之后,equalTo也可以对基本数据类型进行包装
      • MAS_SHORTHAND
      • MAS_SHORTHAND_GLOBALS
    • 更新约束:updateContraints:
      • 特点:如果之前有这个约束会直接更新,如果没有会添加新的约束
    • 删除之前所有的约束,添加新的约束remakeConstraints:

23.什么是适配

  • 适配:适应兼容各种不同的情况
  • 分类:
    • 屏幕适配
    • 系统适配
    • 设备的分辨率

24.点和像素的区别

  • 在用户眼中
    • 屏幕是由无数个像素组成的,像素越多,屏幕越清晰
  • 在开发者眼中
    • 屏幕是由无数个点组成的,点又是由像素组成的
    • 像素越多,屏幕越清晰

25.什么是Autolayout?

  • autolayout是一种自动布局技术,专门用来布局UI界面
  • 能够轻松的解决屏幕适配的各种问题
  • 解决任何控件之间的相对关系问题

26.简单描述一下Autolayout的两个核心概念:约束和参照?

  • 约束:通过给控件添加约束,决定控件的位置和尺寸
  • 参照:添加的约束是相对于谁来添加的,是父控件还是同一级的控件

27.Autolayout的警告和错误

  • 警告
    • 控件的frame不匹配所添加的约束
  • 错误
    • 缺乏必要的约束,约束条件不够,至少要有四个
    • 两个约束之间造成冲突,重复设置更新约束

28.通过代码添加约束的原则

  • 对于两个同层级控件之间的约束关系,添加到他们的父控件上
  • 对于两个不同层级控件之间的约束关系,添加到他们最近的共同的父控件上
  • 对于有层次关系的两个view之间的约束关系,添加到层次较高的父控件上

29.什么是VFL

  • Visual Format Language :可视化格式语言
  • 苹果公司为了简化Autolayout的编码而推出的抽象语言

30.通过约束如何实现动画

  • [UIView animateWithDuration:1.0 animations:^{

    [添加了约束的父控件 layoutIfNeeded];强制刷新

}
];

31.xib加载原理

  • 解析器,解析xib,检测最外侧的view是什么类型
    • 创建一个这种类型的控件:initWithColer:
    • 将frame、背景色等设置转为对应的代码
    • initWithCoder:创建子控件
    • 将xib中的设置转为对应的代码
    • 将子控件加载到父控件上去
    • xib中子控件是否有连线,相当于代码中给父控件的属性是否赋值
    • 通过代码创建一个控件,是不会主动去加载xib的,即使类名和xib名字一样,也不会去加载

32.通过xib或者storyboard创建控件,初始化操作可以在initWithFrame:方法中做吗?

  • 如果是通过storyboard或者xib创建控件,初始化的时候是不会调用initWithFrame,会调用initWithCoder
  • 初始化完毕会调用awakeFromNib方法,建议在awakeFromNib中做初始化操作

33.通过alloc/init 或者alloc/initWithFrame创建控件会不会主动加载xib

  • 通过alloc/init或者alloc/initWithFrame创建控件不会主动加载xib,即使xib的名称和控件的类名一样

34.UITableViewController的认识

  • UITableViewController继承自UIViewController
  • 控制器内部有tableView属性,tableView强引用UITableView对象
  • 控制器的view就是tableView
  • 默认遵守了代理协议和数据源协议
  • 不需要设置数据源和代理,控制器内部的UITableView对象已经设置好数据源和代理就是当前控制器

35.性能优化的思路

  • 首先了解苹果内部创建cell的方式,是每当一个cell进入视野范围内就会调用一次cellForRowAtIndexPath:这个方法,当cell要显示的时候去创建cell,导致重复创建和销毁cell,很耗费性能,造成UI卡顿
  • 假设一个UI界面上显示4个cell
  • 当用户向上滚动的时候,可以tableView可以去缓存池中看有没有可以利用的cell,如果没有,就创建一个新的cell,当第五个cell完全显示出来的时候,把第一个cell放入到缓存池当中,绑定标识,注意重用标识要一致,当滚动到第六个cell即将要显示的时候,tableView可以去缓存池中看有没有可以重复利用的cell,根据标识,会找到第一个cell
  • 重复利用第一个cell,保留cell的属性,使得第六个cell的地址和第一个cell的地址相同
  • 如此循环利用cell可以优化性能,防止cell重复创建销毁,耗费性能

36.UITableVeiw的性能优化的实现步骤

  • 1.首先去缓存池中看有没有可以循环利用的cell
    • dequeueReuseableCellWithIdentifier:这个方法会返回一个cell给我们
  • 2.如果缓存池中没有可循环利用的cell,需要自己创建一个新的cell
    • if (cell == nil){
      cell = [[UITableViewCell alloc]initWithStyle:reuseIndentifier:];
      }

37.registerClass:的作用

  • 根据标识去缓存池中取,如果缓存池中没有,希望tableView内部帮我们创建
  • [tableView registerClass:[UITableViewCell class]forCellReuseIdentifier:]
  • 根据标识,注册cell的类型为UITableViewCell

38.通过注册的方式,如何实现cell的重用

  • 根据标识去缓存池中取,如果缓存池中没有,希望tableView内部帮我们创建
  • [tableView registerClass:[UITableViewCell class]forCellReuseIdentifier:]
  • 根据标识,注册cell的类型为UITableViewCell
  • dequeueReuseableCellWithIdentifier:会根据ID去缓存池中去取可循环利用的cell,如果没有可循环利用的cell,会判断有没有根据这个标识注册对应的cell类型,如果注册了会自动创建这种类型的cell,并且绑定ID这个标识返回给我们。
  • if语句不用写了

39.如何监听tableView内部cell的点击事件?

  • 设置代理属性 self.tableView.delegate = self;
    • 遵守<UITableViewDelegate>
    • 实现代理方法:didSelectRowAtIndexPath:
      • 当用户点击选中某一行cell的时候,就会自动调用这个方法
      • indexPath.row
    • didDeselectRowAtIndexPath:不常用,当用户取消选中某一行的时候,调用这个方法

40.性能优化的具体实现

  • 不直接创建cell,去缓存池中看有没有可以直接利用的cell
  • 如果缓存池中没有可循环利用的cell,需要自己创建一个新的cell
  • 把可循环利用的cell绑定一个标识,根据标识找到可以循环利用的cell

41.UITableView如何展示数据

  • 设置数据源属性
  • 遵守<UITableViewDataSource>
  • 实现数据源方法
    • numberOfSectionsInTableView:返回一共有多少组
    • numberOfRowsInSection:返回每一组有多少行
    • cellForRowAtIndexPath:展示每一行显示的内容

42.UITableView的cell有哪几种样式?

  • group样式
  • plain样式

43.通过代码自定义cell能在initWithFrame:方法中添加子控件吗?

  • 不可以,自定义cell不同于自定义控件,cell的创建时通过initWithStyle:reuseIdentifier:方法,而不是通过initWithFrame:方法。因此,添加子控件只能通过重写initWithStyle:方法,并且要调用父类的initWithStyle:reuseIdentifier:根据绑定的标识创建添加对应的子控件

44.通过代码自定义cell能在initWithStyle:reuseIdentifier:方法中计算子控件的位置和尺寸吗?

  • frame方式不可以,通过代码创建的自定义cell,布局子控件的方法要写在layoutSubViews方法,initWithStyle:reuseIdentifier:方法初始化方法,只能用于添加子控件,或者是一次性的统一的设置
  • masonry方式可以

45.通过代码自定义cell,frame和Autolayout的方式有什么区别吗?

  • frame方式是通过CGRectMake()的方法设置控件的x,y,宽度以及高度,布局子控件的方法必须写在layoutSubViews方法中
  • autolayout设置约束的方式,可以在initWithStyle:reuseIndentifier方法中计算子控件的位置和尺寸,但是要等到控件添加到父控件以后才可以添加约束

46.通过xib和storyboard自定义cell对比?

  • 通过xib方式自定义cell,可以通过传统的方式定义重用标识,根据ID区缓存池中去取可循环利用的cell,如果没有cell就自己创建;也可以通过注册的方式在viewDidLoad中根据ID注册一个xib文件,然后根据ID去缓存池中去取,如果缓存池中没有可利用的cell,就找有没有xib文件,如果没有xib文件,就注册创建一个xib文件,同时必须要设置rowHeight.
  • 通过stroyboard自定义cell,定义一个重用标识,绑定标识,然后根据ID去缓存池中去取dequeue,如果缓存池中没有,方法内部会判断有没有注册的cell。如果没有注册,会自动去storyboard中找有没有这种标识类型的cell,如果有系统会加载这种类型的cell。如果主动注册了cell,就会加载注册的cell,就不会去storyboard中去找,注册的优先级大于storyboard,storyboard中加载的cell,可以不写rowHeight的代码

47.通过storyboard的方式如何加载cell?

  • 根据ID去缓存池中去取dequeue,如果缓存池中没有,方法内部会判断有没有注册的cell。如果没有注册,会自动去storyboard中找有没有这种标识类型的cell,如果有系统会加载这种类型的cell。如果主动注册了cell,就会加载注册的cell,就不会去storyboard中去找,注册的优先级大于storyboard

48.字典转模型第三方框架的了解

  • Mantle
    • 所有模型都必须继承自MTModel
    • 给NSObject写了一个子类
  • JSONModel
    • 所有模型都必须继承JSONModel
    • 对项目有污染性
  • MJExtension
    • 不需要强制继承任何其他类
    • 导入框架
    • 导入框架的主头文件
    • 给NSObject写了一个分类
    • mj_objectArrayWithKeyValuesArray:
      • 把传入的字典数组转成模型数组
      • 用什么类去调用就转成什么类型的模型
-  `mj_objectArrayWithFile:`
    - 把plist路径传进去就可以了
    - 一般看到file就是全路径
- `mj_objectArrayWithFilename:@".plist"`

    - 直接传入plist文件名称就可以
  • 复杂的字典转模型

    • 不用为模型提供类方法
    • 模型里面写属性就可以
  • 第一种方法:

    • mj_objectArrayWithFilename:
    • 模型里面有数组属性,数组中装的是哪个类型框架不知道
    • 告诉框架数组属性装的哪个类型
      • 导入主头文件
      • 实现类方法mj_objectClassArray
      • return @{@"cars":[Car class]};
      • return @{@"cars":@"Car"}不用导入Car模型的头文件
      • 当删除框架,不用这个框架的时候,可以删掉框架的主头文件,代码不会报错
  • 另一种方法:

    • 在mj_objectArrayWithFilename:之前告诉框架
    • mj_setupObjectClassInArray:^NSDictionary *{
      return @{@"cars":[Car class]};
      }

49.设计框架需要考虑的问题

  • 侵入性
    • 侵入性大就意味着很难离开这个框架
  • 易用性
    • 比如少量代码实现N多功能
  • 扩展性
    • 很容易给这个框架增加新功能

50.简述registerNib:(nullable UINib *)nib forCellReuseIdentifier:(NSString *)identifier和registerClass:(nullable Class)cellClass forCellReuseIdentifier:(NSString *)identifier这2个方法的区别?

  • registerClass 这个方法是根据ID 注册对应的cell类型,系统创建cell的方式是通过alloc/initWithStyle...
  • registerNib 这个方法是根据ID 注册一个xib文件,系统创建cell的方式是通过加载xib文件.

51.如何计算一段文字的宽度和高度?

  • 第一种情况:如果label 只有一行,通过sizeWithAttributes:这个方法,告知这段文字的字体和字体大小就可以计算这段文件的尺寸.
  • 第二种情况:如果label需要换行计算高度,通过boundingRectWithSize: options: attributes:attributes context: 这个方法,告知这段文字的字体和字体大小,并且在一个限制的尺寸内计算这段文字的尺寸.

52.什么是自定义cell?

  • 继承自系统的cell写一个自己的cell,给系统的cell扩充一些额外的功能

53.reloadData这个方法的作用

  • 全局刷新,刷新整个表格,所有cell的数据都会刷新。

54.如何做动画刷新

  • 通过局部刷新的方法,设置动画刷新
    • 添加insert
    • 删除delete
    • 更新reload

55.如何实现左滑删除功能

  • 用代理的方法实现左滑功能
    • 设置代理
    • 遵守代理协议
    • 实现代理方法
      • 只要实现了commitEditingStyle:forRowAtIndexPath:方法,就拥有左滑删除功能。

56.如何实现左滑出现N个按钮

  • editActionsForRowAtIndexPath:返回的是一个数组,数组里装的是UITableViewRowAction对象

    • 创建UITableViewAction对象:rowActionWithStyle:title:handler:^(UITableViewRowAction * action ,NSIndexPath *indexPath){
      }
    • 实现了这个方法,系统的titleForDeleteConfirmationsButtonForRowAtIndexPath以及commitEditingStyle:forRowAtIndexPath:的方法就没有作用了
    • return @[action,action1];
    • 删除:destructive和默认的default样式是一样的
    • 关注:Normal样式
  • ios9之前必须实现commitEditingStyle:forRowAtIndexPath:这个方法,才能实现左滑删除功能,ios9之后,实现editActionsForRowAtIndexPath:这个方法,也可有左滑删除功能。

57.编辑模式的理解

  • 编辑模式进入前,要隐藏删除按钮,点击编辑模式,显示删除按钮

  • self.tableView.editing = NO;默认是NO

    • 一旦进入编辑状态就会改为YES

58.如何实现在编辑模式中多选

  • 可以设置tableView在编辑模式下可以多选
    • allowsMultipleSelectionDuringEditing = YES

59.简单描述下你对MVC的认识

  • M :Model ,用来保存数据的,将数据传给控制器
  • V :View ,展示数据
  • C :Control ,创建模型,把模型传给view来展示数据

60.iOS8以后,通过storyboard自定不等高的cell,对比通过storyboard自定义等高的cell有哪些区别?

  • ios8以后storyboard定义不等高cell,对比等高cell,需要增加一个估算高度约束的设置和自动布局的设置
  • 估算高度是为了让用户在数据较多的时候,有一个更好的用户体验,通过显示的滚动条来判断内容的多少
  • 自动布局是通过设置的约束,以及约束的更新来自动布局,不用计算cell的高度,也不用返回cell的高度值

61.为什么在遍历一个数组的时候,不能一边一边删除,这样可能会导致什么问题?

  • 开发中有一个原则,就是千万不能一边遍历一边删除,遍历的时候要保证数组的长度不变,因为每删除一个元素,其他元素在数组中的索引可能会发生变化

62.设置tableView的cell估算高度的作用

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

推荐阅读更多精彩内容