iOS面试题-开发实用篇

简述APP生命周期

一张图即可反映APP生命周期:

APP

APP启动过程及简单优化

1. 解析Info.plist
加载相关信息,如闪屏
沙箱建立,权限检查
2. Mach-O加载
如果是胖二进制,寻找合适CUP类别的部分
加载所有依赖Mach-O文件
定位内部,外部指针引用,如字符串,函数
执行声明函数
加载扩展类
C++静态对象加载,调用Objc的+(load)函数
3. 程序执行
main函数
执行UIApplicationMain函数
UIApplicationDelegate对象开始处理监听事件

如果APP启动缓慢,可以想到的因素
main() 函数内有耗时操作;
动态库加载太多;
rootViewControlle以及childViewController的加载,view和subViews的加载耗时;
优化:
移除不需要用到的动态库
移除不需要用到的类
合并功能类似的类和扩展(Category)
压缩图片资源
优化applicationWillFinishLaunching
优化rootViewController加载

UIViewController 的生命周期

0.loadView:加载视图
1.loadViewIfNeeded(iOS9后):重新加载视图包括viewDidLoad
2.viewDidLoad:视图控制器中的视图加载完成,viewController自带的view加载完成
3.viewWillAppear:视图将要出现
4.viewWillLayoutSubviews:即将布局其 Subviews
5.viewDidLayoutSubviews:已经布局其 Subviews
6.viewDidAppear:视图已经出现
7.viewWillDisappear:视图将要消失
8.viewDidDisappear:视图已经消失
这个面试点在实际开发中还是比较重要的,毕竟写个视图都会有自己的生命周期,从而更好的优化视图加载.

一张老图:

生命周期

loadView什么作用

loadView在View为nil时调用,早于ViewDidLoad,通常用于代码实现控件,收到内存警告时会再次调用。
loadView默认做的事情是:如果此Viewcontroller存在一个对应的nib文件,那么就加载这个nib。否则,就创建一个UIView对象。
如果你用Interface Builder来创建界面,那么不应该重载这个方法。

如果你想自己创建View对象,那么可以重载这个方法,此时你需要自己给View属性赋值。
你自定义的方法不应该调用super。如果你需要对View做一些其他定制操作,在ViewDidload中去做

根据上面说明可以知道,有两种情况:

1、如果你用了nib文件,重载这个方法就没有太大意义。因为loadView的作用就是加载nib。
如果你重载了这个方法不调用super,那么nib文件就不会被加载。
如果调用了super,那么view已经加载完了,你需要做的其他事情在viewDidLoad里面做更合适。

2、如果你没有用nib,这个方法默认就是创建一个空的view对象。如果你想自己控制view对象的创建,
例如创建一个特殊尺寸的view,那么可以重载这个方法,自己创建一个UIView对象,然后指定 self.view = myView;
但这种情况也没有必要调用super,因为反正你也不需要在super方法里面创建的view对象。如果调用了super,那么就是浪费了一些资源而已

View中的加载顺序

1.initWithCoder(如果没有storyboard就会调用initWithFrame)
2.awakeFromNib:作为第一个方法的助手,方便处理一些额外的设置。
3.layoutSubviews:一般设置子控件的frame。
4.drawRect:UI控件都是画上去的,在这一步就是把所有的东西画上去。
layoutSubviews方便数据计算,drawRect方便视图重绘。
drawRect在以下情况下会被调用:
1、如果在UIView初始化时没有设置rect大小,将直接导致drawRect不被自动调用。drawRect 掉用是在Controller->loadView,Controller->viewDidLoad两方法之后掉用的.
  所以不用担心在 控制器中,这些View的drawRect就开始画了.这样可以在控制器中设置一些值给View(如果这些View?draw的时候需要用到某些变量 值).
2、该方法在调用sizeToFit后被调用,所以可以先调用sizeToFit计算出size。然后系统自动调用drawRect:方法。
3、通过设置contentMode属性值为UIViewContentModeRedraw。那么将在每次设置或更改frame的时候自动调用drawRect:。
4、直接调用setNeedsDisplay,或者setNeedsDisplayInRect:触发drawRect:,但是有个前提条件是rect不能为0。
在实际开发中layoutSubviews个人用的比较多,用来重新设置控件大小.
drawRect一般用来做绘图个人用的不多.

layoutSubviews 何时会被调用

当要调整 subViews 时候,需要重写 layoutSubviews 方法。 
1:初始化 init 方法时候不会触发。
2:滚动 UIScrollView 时会触发
3:旋转 UIScreen 时会触发
4:当改变 view 的值时候会触发,前提是 frame 前后值发生了变化 
5:当改变 UIview 的大小时候会触发

什么是KVC和KVO?

KVC 也就是 key-value-coding ,即键值编码,通常是用来给某一个对象的属性进行赋值.
开发中我们可以对私有属性进行赋值的,修改一些控件的内部属性,还可以用于字典转模型.
KVO,即 key-value-observing,利用一个 key 来找到某个属性并监听其值得改变。其实这也是一种典型的观察者模式。
大致用法:
1.添加观察者
2.在观察者中实现监听方法,observeValueForKeyPath: ofObject: change: context:
3.移除观察者
简单实现原理:
当一个类的属性被观察的时候,系统会通过 runtime 动态的创建一个该类的派生类,并且会在这个类中重写基类被观察的属性的 setter 方法,
而且系统将这个类的 isa 指针指向了派生类,从而实现了给监听的属性赋值时调用的是派生类的setter方法。
重写的 setter 方法会在调用原 setter 方法前后,通知观察对象值得改变。
KVC/KVO实现的根本是 Objective-C 的动态性和 runtime

开发中,保存数据有哪几种方式?

所谓的持久化,就是将数据保存到磁盘中,使得在应用程序重启后可以继续访问之前保存的数据.
iOS本地数据保存有多种方式,比如NSUserDefaults、Plist文件保存、归档(NSKeyedArchiver)、SQLite、CoreData、KeyChain(钥匙串)等多种方式。
NSUserDefaults:
  NSUserDefaults 是一个单例对象,在整个应用程序的生命周期中都只有一个实例。
  NSUserDefaults保存的数据类型有:NSNumber, 基本数据类型(int,NSInter,float,double,CGFlat......),   NSString, NSData, NSArray, NSDictionary, NSURL。
  NSUserDefaults一般保存配置信息,比如用户名、密码、是否保存用户名和密码、是否离线下载等一些配置条件信息。
plist文件保存:
  plist文件是将某些特定的类,通过XML文件的方式保存在目录中。
  plist主要保存的数据类型为NSString、NSNumber、NSData、NSArray、NSDictionary。
可以看出NSUserDefaults和plist有一定局限性.
归档:在iOS中是另一种形式的序列化,只要遵循了NSCoding协议的对象都可以通过它实现序列化。
  需要注意的:必须遵循并实现NSCoding协议;保存文件的扩展名可以任意指定;继承时必须先调用父类的归档解档方法
上面的几个存储方法,都是覆盖存储。如果想要增加一条数据就必须把整个文件读出来,然后修改数据后再把整个内容覆盖写入文件。所以它们都不适合存储大量的内容。
因此保存大量数据可以优先考虑用数据库,sql语句对查询操作有优化作用,所以从查询速度或者插入效率都是很高的。
SQLite:在iOS中要使用SQLite3,需要添加库文件:libsqlite3.dylib并导入主头文件,这是一个C语言的库,所以直接使用SQLite3还是比较麻烦的。
  不过在一般开发过程中,使用的都是第三方开源库 FMDB,封装了这些基本的c语言方法,使得我们在使用时更加容易理解,提高开发效率。
CoreData:
  CoreData框架提供了对象-关系映射(ORM)的功能,即能够将OC对象转化成数据,保存在SQLite3数据库文件中,也能将保存在数据库中的数据还原成OC对象.在次数据操作期间,不需要编写任何SQL语句.
上面几种都是保存到沙盒中.
KeyChain:钥匙串是苹果公司Mac OS中的密码管理系统。一个钥匙串可以包含多种类型的数据:密码(包括网站,FTP服务器,SSH帐户,网络共享,无线网络,加密磁盘镜像等),私钥,电子证书和加密笔记等。
  当应用程序被删除后,保存到 KeyChain 里面的数据不会被删除,所以KeyChain是保存到沙盒范围以外的地方。安全性也比较高.
  KeyChain还有一个用途,就是替代UDID。UDID已经被废除了,所以只能用UUID代替,所以我们可以把UUID用KeyChain保存。

关键字const/static/extern、UIKIT_EXTERN区别和用法以及与宏的区别

const:
  1.const用来修饰右边的基本变量或指针变量
  2.被修饰的变量只读,不能被修改
  int  const  *p   //  *p只读 ;p变量
  int  *const  p  // *p变量 ; p只读
  const  int   *const p //p和*p都只读
  int  const  *const  p   //p和*p都只读
  开发者经常定义只读变量:
  const CGFloat kWidth = 10.0;
static:
  1.修饰局部变量,保证局部变量永远只初始化一次,在程序的运行过程中永远只有一份内存,  生命周期类似全局变量了,但是作用域不变。
  2.修饰全局变量使全局变量的作用域仅限于当前文件内部,即当前文件内部才能访问该全局变量。
  3.修饰函数时,被修饰的函数被称为静态函数,使得外部文件无法访问这个函数,仅本文件可以访问
   另外在开发中经常在单例中使用.
extern:它的作用是声明外部全局变量。这里需要特别注意extern只能声明,不能用于实现,而且定义和分配内存都在原来类中。
  UIKIT_EXTERN:可以解决重复定义的问题,可以参照苹果的做法,比如系统预置的通知:
  UIKIT_EXTERN NSString *const   UIKeyboardWillShowNotification;
  UIKIT_EXTERN NSString *const  UIKeyboardDidShowNotification;
宏:1.宏在编译开始之前就会被替换,而const只是变量进行修饰; 
  2.宏可以定义一些函数方法,const不能 
  3.宏编译时只替换不做检查不报错,也就是说有重复定义问题。而const会编译检查,会报错  
定义不对外公开的常量的时候,我们应该尽量先考虑使用 static 方式声名const来替代使用宏定义。const不能满足的情况再考虑使用宏定义。
例如:
  static const CGFloat kWidth = 10.0;
  可以代替 #define WIDTH 10.0

关键字组合static inline

inline函数, 即内联函数, 他可以向编译器申请, 将使用inline修饰的函数内容, 内联到函数调用的位置
内联函数的作用类似于#define, 但是他比#define有一些优点
相对于函数直接调用: inline修饰的函数, 不会再调用这个函数的时候, 调用call方法, 就不会将函数压栈, 产生内存消耗。
这样就减少了调用的开销,提高效率.所以执行速度确比一般函数的执行速度要快.
相对于宏的优点:
1.宏需要预编译, 而内联函数是一个函数, 不许要预编译
2.编译器调用内联函数的时候, 会检查函数的传参是否正确, 但是宏就不会提醒参数
3.可以使用所在类的保护成员及私有成员。
但是内联函数的使用也有限制:
1.内联函数只是我们向编译器提供的申请,编译器不一定采取inline形式调用函数.
2.内联函数只能对一些小型的函数起作用, 如果函数中消耗的内存很大, 比如for循环, 则内联函数就会默认失效
3.内联函数的定义须在调用之前.另外如果调用次数多的话,会使可执行文件变大。
使用static的原因:
static 只是为了表明该函数只在该文件中可见!也就是说,在同一个工程中,就算在其他文件中也出现同名、同参数的函数也不会引起函数重复定义的错误!
另外和普通函数的区别:
1.普通函数调用需要开辟栈帧和回收栈帧,内联函数不开辟和回收栈帧,在调用出展开代码
2.普通函数会在编译完生成函数名对应的符号,链接的时候在符号表上可以找到,内联函数不生成符号

block的实质是什么?一共有几种block?都是什么情况下生成的?

block对象是一个c语言级别的语法和运行机制。它与标准c函数类似,不同之处在于,它除了有可执行的代码之外,还包含了与堆、栈内存绑定的变量。
作为一个回调,block特别的有用,因为block既包含了回调期间的代码,又包含了执行期间需要的数据。
iOS中有三种:
NSStackBlock    存储于栈区
NSGlobalBlock   存储于程序数据区
NSMallocBlock   存储于堆区
NSGlobalBlock:
  block 内部没有引用外部变量的 Block 类型都是 NSGlobalBlock 类型,存储于全局数据区,由系统管理其内存,retain、copy、release操作都无效。
NSStackBlock:
  block 内部引用外部变量,retain、release 操作无效,存储于栈区,变量作用域结束时,其被系统自动释放销毁.但在ARC下block 变量在赋值的时候系统自动将其拷贝到堆区了
NSMallocBlock:
  [block retain],[blockA copy]操作后变量类型变为 NSMallocBlock,支持retain、release.

代理、通知、block和单例使用场景

代理:这是很常用的方式,特点是一对一的形式,而且逻辑结构非常清晰。需要定义协议方法并且实现协议方法,会使代码结构变复杂.
通知:通知中心实际上是在程序内部提供了消息广播的一种机制。通知中心是基于观察者模式的,它允许注册、删除观察者。它是多对多关系.
block:是一段特殊的代码块。使用起来有点像函数.特点也是一对一的,代码结构更加紧凑,不需要额外定义方法.
这几个在开发中经常用于数据传递,如果只是单个参数往往使用block,如果参数内容较多则使用代理传值.而如果是跨界面传值往往使用通知传值.
当然传值的方式还有单例,数据存储来做传值.

frame和bounds有什么不同?

frame指的是:该view在父view坐标系统中的位置和大小。(参照点是父亲的坐标系统)
bounds指的是:该view在本身坐标系统中 的位置和大小。(参照点是本身坐标系统)
理解这两个概念在实际开发中还是比较重要的.

UIView、CALayer和UIWindow是什么关系?

UIView是iOS系统中界面元素的基础, 所有的界面元素都继承自它, UIView本身完全是由CoreAnimation来实现. 真正的绘图部分, 是由一个CALayer类来管理.
最大的区别是UIView继承自UIResponder, 能接收并响应事件, 负责显示内容的管理, 
而CALayer继承自NSObject, 不能响应事件, 负责显示内容的绘制.UIView是基于CALayer的高层封装。而CALayer不支持自动布局.
另外UIWindow是一种特殊的UIView,通常在一个程序中只会有一个UIWindow,但可以手动创建多个UIWindow,同时加到程序里面。UIWindow在程序中主要起到三个作用:
1、作为容器,包含app所要显示的所有视图
2、传递触摸消息到程序中view和其他对象
3、与UIViewController协同工作,方便完成设备方向旋转的支持

isMemberOfClass 、 isKindOfClass和 isSubclassOfClass 联系与区别

联系:都能检测一个对象是否是某个类的成员
区别:
isKindOfClass:确定一个对象是否是一个类的成员,或者是派生自该类的成员.
isSubclassOfClass和isKindOfClass的作用基本上是一致的,只不过一个是类方法,一个是对象方法。
isMemberOfClass:确定一个对象是否是当前类的成员.他的筛选条件更为苛刻,只有当类型完全匹配的时候才会返回YES。

将一个函数在主线程执行的几种方法

//GCD方法,通过向主线程队列发送一个block块,使block里的方法可以在主线程中执行。
dispatch_async(dispatch_get_main_queue(), ^{
    //需要执行的方法
});
//NSOperation 方法
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
    //需要执行的方法
}];
[mainQueue addOperation:operation];
//NSThread 方法
[self performSelector:@selector(method) onThread:[NSThread mainThread] withObject:nil waitUntilDone:YES modes:nil];
[self performSelectorOnMainThread:@selector(method) withObject:nil waitUntilDone:YES];
[[NSThread mainThread] performSelector:@selector(method) withObject:nil];
//RunLoop方法
[[NSRunLoop mainRunLoop] performSelector:@selector(method) withObject:nil];

+initialize 与 +load有什么用处,区别

通常情况下,我们在开发过程中可能不必关注这两个方法。如果有需要定制,我们可以在自定义的 NSObject 子类中给出这两个方法的实现,
这样在类的加载和初始化过程中,自定义的方法可以得到调用。
+load:通过函数地址直接调用,是在 runtime 加载类分类的时候调用,只会调用一次
1.当父类和子类都实现 load 函数时,父类的 load 方法执行顺序要优先于子类
2.当子类未实现 load 方法时,不会调用父类 load 方法
3.类中的 load 方法执行顺序要优先于类别(Category)
4.当有多个类别(Category)都实现了 load 方法,这几个 load 方法都会执行,但执行顺序不确定(其执行顺序与类别在 Compile Sources 中出现的顺序一致)
5.当然当有多个不同的类的时候,每个类 load  执行顺序与其在 Compile Sources 出现的顺序一致

+ initialize:会在第一次接收到消息时调用,会通过 objc_msgSend 调用,每个类只会 initialize 一次(父类可能多次,但不代表初始化多次)。
1.父类的 initialize 方法会比子类先执行
2.当子类未实现 initialize 方法时,会调用父类 initialize 方法( 可能会多次调用),子类实现 initialize 方法时,会覆盖父类 initialize 方法.
3.当有多个 Category 都实现了 initialize 方法,会覆盖类中的方法,只执行一个(会执行Compile Sources 列表中最后一个Category 的 initialize 方法)

+load + initialize
调用时机 被添加到 runtime 时(main前) 到第一条消息前,可能永远不调用
调用顺序 父类->子类->分类 父类->本类(如果有分类,则调用分类)
若自身未实现,是否沿用父类的方法
类别中的定义 全都执行,但后于本类的方法(顺序与Compile Sources出现的顺序一致) 覆盖本类的方法,只执行一个(执行Compile Sources 列表中最后一个Category 的initialize方法)

更多请看:iOS类方法load和initialize详解

layoutIfNeeded和setNeedsLayout的区别

首先我们布局总会重新触发layoutSubviews方法
setNeedsLayout:
标记为需要重新布局,异步调用layoutIfNeeded刷新布局,不立即刷新,在下一轮runloop结束前刷新,
对于这一轮runloop之内的所有布局和UI上的更新只会刷新一次,layoutSubviews一定会被调用。

layoutIfNeeded:
如果有需要刷新的标记,立即调用layoutSubviews进行布局(如果没有标记,不会调用layoutSubviews)。

如果需要立即刷新则调用
[self setNeedsLayout];
[self layoutIfNeeded];

是开发中如果需要在 Masonry 添加约束后如果想获取frame可以直接调用
layoutIfNeeded 即可立即获取frame

优化你是从哪几方面着手

1.不要阻塞主线程
2.尽量view设置为完全不透明,减少GPU渲染的消耗
3.懒加载和延迟加载。
4.处理内存警告
5.重用开销大的对象,例如NSDateFormatter,NSNumberFormatter等等,写成单例。
6.App启动时间优化

UITableView优化:
1.正确使用reuseIdentifier来重用cell
2.尽量view设置为完全不透明
3.缓存行高
4.如果cell内现实的内容来自web,使用异步加载,缓存请求结果
5.异步绘制,遇到复杂界面,遇到性能瓶颈时,可能就是突破口;
6.尽量不要动态的 add 或者 remove 子控件。最好在初始化时就添加完,然后通过hidden来控制是否显示。

常见的循环引用,及解决办法

1.父类与子类
如:在使用UITableView 的时候,将 UITableView 给 Cell 使用,cell 中的 strong 引用会造成循环引用。
解决:strong 改为 weak
2.block
self将block作为自己的属性变量,而在block的方法体里面又引用了 self 本身,此时就很简单的形成了一个循环引用。
解决:__weak typeof(self) weakSelf = self;
     __strong typeof (weakSelf) strongSelf = weakSelf;
3.Delegate
@property (nonatomic, weak) id <TestDelegate> delegate;
如果将 weak 改为 strong,则会造成循环引用
4.NSTimer
NSTimer 的 target 对传入的参数都是强引用(即使是 weak 对象)
解决方法:1.自己封装成block timer
2.使用iOS10后系统的block timer
3.使用GCD封装计时器

isEqual,isEqualToString和==区别

isEqual:默认情况下是比较两个对象的内存地址;isEqual:就是提供了一个可以自定义相等标准的方法。
系统自带的类(比如 Foundation 中 的 NSString, NSArray 等)重写了这个方法,改变了这个方法的判断规则,
一般改为比较两个对象的内容,不是内存地址.

isEqualToString: 字符串比较,只比较字符串本身的内容是否一致,不比较内存地址.

==:如果两个对象的内存地址是一样,返回true,如果内存地址不一样,返回false.

pod update 和 pod install 的区别

pod install用于工程第一次安装pod库和修改podfile(添加,更新,移除pod库)的时候。
pod update用于更新特定的pod(或所有的pod)版本时。

CoreGraphics, CoreAnimation区别

CoreGraphics(核心图形):它是iOS的核心图形库,包含Quartz2D绘图API接口,常用的是point,size,rect等这些图形,都定义在这个框架中,
类名以CG开头的都属于CoreGraphics框架,它提供的都是C语言函数接口
CoreAnimation(核心动画):
1. CoreAnimation是跨平台的,既可以支持IOS,也支持MAC OS。
2. CoreAnimation执行动画是在后台,不会阻塞主线程。
3. CoreAnimation作用在CALayer,不是UIView。
4. CoreGraphics和CoreAnimation的关系:它们都是跨iOS和Mac OS 使用的,这点区别于UIKit,并且CoreAnimation中大量使用到CoreGraphics中的类,因为实现动画要用到图形库中的东西。
5. CoreGraphics是底层绘制框架,我们实际会用到的也就是CG开头的一些底层绘制函数和变量,这是一个纯C语言框架。

更多可以参考总结 iOS 面试相关总结

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

推荐阅读更多精彩内容

  • 1.OC里用到集合类是什么? 基本类型为:NSArray,NSSet以及NSDictionary 可变类型为:NS...
    轻皱眉头浅忧思阅读 1,354评论 0 3
  • 1. 父类实现深拷贝时,子类如何实现深度拷贝。父类没有实现深拷贝时,子类如何实现深度拷贝。 1.1 深拷贝同浅拷贝...
    iYeso阅读 1,886评论 0 13
  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 27,121评论 29 470
  • 1,NSObject中description属性的意义,它可以重写吗?答案:每当 NSLog(@"")函数中出现 ...
    eightzg阅读 4,131评论 2 19
  • android调试debug快捷键 1. 【Ctrl+Shift+B】:在当前行设置断点或取消设置的断点。 2. ...
    CQ_TYL阅读 969评论 0 0