过段时间打算跳槽,找了一些面试题来做,在这里做个总结方便review,希望能对要面试的童鞋有帮助。

以下为面试题:

  1. 运行以下代码会有什么结果

    NSString *str1 = @"str1";
    NSString *str2 = [NSString stringWithFormat:@"str1"];
    NSString *str3 = @"str1";
    NSLog(@"str1 == str2 --- %d", str1 == str2);
    NSLog(@"str1 == str3 --- %d", str1 == str3);
    NSLog(@"str1 isEqualToString str2 --- %d", [str1 isEqualToString:str2]);
    NSLog(@"str1 isEqualToString str3 --- %d", [str1 isEqualToString:str3]);

  第一眼看这道题,只能确定使用isEqualToString:来比较字符串是比较每一个字符,所以isEqualToString肯定是true,而在OC里使用==号用于判断是否指向同一个地址,那么问题就来了,使用字面量创建的字符串与调用方法创建的有什么区别呢?

  实践出真知,老老实实敲代码,打上断点来一探究竟

  

  可以看到使用字面量创建的字符串为常量字符串,而用方法创建的则是指针字符串。常量字符串会在app销毁后释放,在app存在期间会一直存在,且相同的常量字符串都指向同一个地址。

  运行结果就如下了

  

  2. 以下方式创建的timer有什么区别

        [NSTimer scheduledTimerWithTimeInterval:.f repeats:YES block:^(NSTimer * _Nonnull timer) {
//创建一个timer并且在当前的runloop中执行。
}];
[NSTimer scheduledTimerWithTimeInterval:.f repeats:NO block:^(NSTimer * _Nonnull timer) { }];
     //timer加入到runloop才能fire成功
[[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:.f] interval:.f repeats:YES block:^(NSTimer * _Nonnull timer) { }];
      //创建timer必须加入到run loop才能执行
[NSTimer timerWithTimeInterval:.f target:self selector:@selector(performTimer:) userInfo:nil repeats:YES]

  我之前对timer的理解就是指定timer启动的时间、是否重复执行,是否立刻执行或手动fire。再深入一点就是可以将timer加入到不同的runloop,这样就能在scrollview滑动的时候也不影响timer执行了。查资料发现,NSTimer在repeats为YES的状态下会对target强引用,并且在没有invalidate的情况下是不会释放的,因此使用timer的时候就可能会出现循环引用的情况。例如,控制器A强引用timer,同时timer的target为A,这就产生了循环引用,当控制器被pop后,该控制器也不会销毁,就会造成内存泄漏。所以在使用timer的时候就需要在适当的时机来释放timer。仅仅是invalidate依然会造成循环引用,只能把timer置为nil才可以。拿之前的例子来说,就需要在控制器的生命周期来做这件事,viewWillAppear创建timer,在viewWillDisappear将timer置为nil。

  3. 如有需求“一段文字中的指定位置插入一张图片”,请写出实现思路。

  可以利用富文本NSAttributeString与NSTextAttachment来实现,先找到要插入图片的位置,然后利用NSTextAttachment来包装图片,最后用NSTextAttachment来生成NSAttributeString即可。

  4. WebView内存管理问题说说自己的经验和看法

   UIWebView本来就有内存泄漏的问题,只能通过一些手段来减少内存泄漏,并不能完全的解决,要解决的办法就是使用WKWebView。优化的方法如下:

   (1) 收到内存警告时清除缓存

- (void)applicationDidReceiveMemoryWarning:(UIApplication*)application
{
[[NSURLCache sharedURLCache] removeAllCachedResponses];
}

  (2)释放webView时

self.webView.delegate = nil;
[self.webView loadHTMLString:@"" baseURL:nil];
[self.webView stopLoading];
[self.webView removeFromSuperview];
[[NSURLCache sharedURLCache] removeAllCachedResponses];
[self.webView release];

   (3) webViewDidFinishLoad时

[[NSUserDefaults standardUserDefaults] setInteger: forKey:@"WebKitCacheModelPreferenceKey"];
[[NSUserDefaults standardUserDefaults] synchronize];

  5. RunLoop和线程的关系

  通常情况下线程在执行完代码后就会销毁,RunLoop其实就是事件处理循环,只有当接受到退出事件时才会退出。在iOS开发中,RunLoop与线程是一一对应的关系,一个RunLoop对应一个线程。需要注意的是只有主线程会在默认状态下创建RunLoop,其他的辅助线程需要自己显示的调用

[NSRunLoop currentRunLoop]来获得与当前线程绑定的RunLoop(ps:如果RunLoop不存在则创建一个新的)。创建出来的RunLoop需要在其中添加有Timer/Source/Observer或者march port,不然RunLoop会销毁。

  6. ARC通过什么方式管理内存

  不只是ARC,MRC也是利用引用计数来进行内存管理的,只是ARC管理就不需要自己来Release和Retain。系统会在自动释放池结束时,对没有强引用的对象统一进行释放。

  7. 使用Block时,什么情况会造成循环引用,如何解决?

  因为block会对所有在block中使用到的对象进行强引用(capture),所以当block被一个对象持有,同时这个对象又在Block中被使用时就会出现强引用。解决方式就是在Block中只用弱引用,代码如下  

__weak typeof(self)weakSelf = self;
void (^block)(void) = ^{
NSLog(@"%@", weakSelf);
}

  还有一种方式是主动打破循环引用,将调用的block置为nil

  8.使用synthesize和dynamic分别有什么作用

  @synthesize的作用:为Property指定生成要生成的成员变量名,并生成getter和setter方法。用途:对于只读属性,如果同时重新setter和getter方法,就需要使用synthesize来手动合成成员变量,代码如下

@interface Person : NSObject

@property (nonatomic, assign, readonly) NSInteger age;

@end

@implementation Person
@synthesize age = _age;
- (void)setAge:(NSInteger)age
{
_age = age;
} - (NSInteger)age
{
return _age;
}
@end

  @dynamic的作用:告诉编译器,不用为指定的Property生成getter和setter。使用方式:当我们在分类中使用Property为类扩展属性时,编译器默认不会为此property生成getter和setter,这时就需要用dynamic告诉编译器,自己合成了,代码如下

  

@interface Person (Extension)

@property (strong, nonatomic) NSString *name;

@end

@implementation Person (Extension)
@dynamic name; - (void)test
{
NSLog(@"%@", self.name);
} @end

  9. runtime如何通过Selector找到对应的IMP地址?(分别考虑类方法和实例方法)

  这就要从类的结构来说了,先来看下面这张官方给出的

  10. initialize和load的区别

  initialize会在Class第一次收到消息时调用,父类会比子类先调用,如果子类没有重新实现initialize方法,此方法会在子类接受消息时被多次调用。load方法会在类被加载到运行时环境中时调用,在整个运行时期间都只会调用一次。

  11. 如何申明私有变量和私有方法?以及外部如何调用

  申明私有变量总的来说有3种方式,一种是在@interface中利用@private 关键字来申明,第二种方式是在@implementation声明。私有方法声明的话,就只能够在@implementation中声明了。访问私有变量可以通过提供getter和setter方法,KVO中key使用成员变量名也可以访问,调用私有方法只能通过暴露方法,或者是利用runtime,以及NSObject提供的方法performSelector系列的方法。

  12.以下代码运行结果

@interface Student : Person

@end

@implementation Student

- (instancetype)init
{
self = [super init];
if (self) {
NSLog(@"%@", NSStringFromClass([self class]));
NSLog(@"%@", NSStringFromClass([super class]));
}
return self;
} @end

  运行结果都为Student,原因是self表示此方法从自己的方法表开始找,super则表示方法先从父类的方法表里找,因为class方法两个类都没有实现,最终方法的都是会NSObject中找到,所以结果都是调用的类名Student。

 14. 用代码实现一个冒泡算法(明天来)

 15. readwrite,readonly, assign,retain, copy, nonatomic,strong的作用

  readwrite表示此属性可以读写,会自动生成getter与setter

  readonly表示此属性只可读,外部只能方法getter方法

  assign在MRC中用于表示引用计数不用加一,以及用于除类之外的声明。在ARC中用于除了类之外的声明

  retain在MRC中表示引用计数加一,在ARC中表示强引用

  copy在MRC中不会影响调用copy方法的对象的引用计数,在ARC中表示在setter方法中会调用传入对象的copy方法,常用于需要不可变对象的属性NSString、NSArray等

  atomic表示属性读写的原子性,然而并不能保证线程安全

  nonatomic则不保证线程安全

  16. 请写出UIViewController的生命周期

   init->viewDidLoad->ViewWillAppear->ViewDidappear->viewWillDisappear->viewDidDisappear->dealloc

  17. 请写出一个单例实现

  利用dispatch_once,其他的实现方式可以重写allocWithZone方法

+ (instancetype)sharedInstance
{
static Student *instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}

  18.UIView相关 

 

  19. 当前维护的App的崩溃率是多少?怎么追踪并解决的?线上崩溃如何解决的?

  崩溃率这个就不谈了,开发过程中遇到的崩溃问题主要查看崩溃的栈信息,利用异常断点来解决。线上崩溃的话,就是利用dSYMS文件来符号化苹果的崩溃日志来解决了

  20. 什么是事件响应链?当用户与iPhone的触屏产生互动时?都发生了什么?事件是如何传递的?

  讲响应者链条前,需要知道iOS中事件响应是基于UIResponder对象的及子类,包括UIResponder的子类UIView、UIViewController、UIWindow、UIApplication,当iOS App接收到触摸事件时,UIKit会自动的找到最合适的第一响应者。没有处理的事件会沿着当前激活状态的响应者链条传递下去。

  如下图所示,这是app中默认的事件响应链条,如果在UILabel上触发了为处理的事件,那么这个事件会传递给label的父视图UIView,然后是UIWindow对象。对于根视图而言,事件会先传递给UIViewController,然后才是window。如果UIWindow也没有处理,那就会传递给UIApplication,application也未处理的话,如果application的代理对象是UIResponder子类并且没有出现在之前的响应者链条中。

  当用户触摸屏幕时,UIKit会根据默认规则来找到第一事件响应者,此规则是基于hit-testing来决定的。UIKit会在touch发生的view的视图层级中比较touch location与View 的bounds。 hitTest:withEvent:方法会遍历整个视图层级找到最深层次的包含此次触摸的子视图,这个子视图就会是第一事件响应者。

  

  21.RunLoop是什么? 使用RunLoop的目的是什么?何时使用?使用要注意些什么?

  RunLoop是一个事件处理的循环,这个循环会不停的从一个地方收到事件,收到事件就做相应的处理,只有收到退出事件时,这个循环才会退出。

  在iOS的开发中,主线程的RunLoop会自动创建,辅助线程的RunLoop只有在主动获取时才会被创建。

  RunLoop由RunLoopMode构成,RunLoopMode又由Timer/Source/Observer构成。RunLoop同时只能够运行在一个Mode下,app主线程的 RunLoop 里有两个预置的 Mode:kCFRunLoopDefaultMode 和 UITrackingRunLoopMode。当TableView处于滑动过程中时,RunLoop的mode为UITrackingRunLoopMode,其余时间Mode为:kCFRunLoopDefaultMode。

  在app开发中默认用到RunLoop的地方有:NSTimer、NSObject提供的performSelectorOnMainThread:withObject:waitUntilDone:方法。当我们使用NSTimer在指定时间执行时,其实是在RunLoop中添加这个timer,并在到达指定的时间点后执行回调。NSObject执行perform等方法的时候同样是在指定时间来执行那个回调,只是在执行perform方法是当前线程必须要存在RunLoop才行,不然无效。

  系统中使用RunLoop的地方有AutoReleasePool,RunLoop会在每一次进入RunLoop时创建自动释放池,然后在RunLoop进入waiting状态时释放旧的释放池并重新创建自动释放池,最后在ExitRunLoop时释放自动释放池。

  在日常的iOS开发中,默认创建的Timer是添加在kCFRunLoopDefaultMode模式下的,所以在滑动TableView时,RunLoop会切换为UITrackingRunLoopMode,timer会被暂停。要想Timer能够在UITrackingRunLoopMode下正常运行有两种办法,一是将Timer分别添加到以上两种Mode中,二是将 Timer 加入到顶层的 RunLoop 的 “commonModeItems” 中。”commonModeItems” 被 RunLoop 自动更新到所有具有”Common”属性的 Mode 里去。

  22.说说你对线程和进程的理解?

  总的来说,进程是程序分配资源的最小单元,线程是程序运行的最小单元,进程可以有多个线程组成。

  23.对大量数据列表有什么优化方案?

   优化1. 利用UITableView重用cell的机制

   优化2. 分批次异步加载数据

   优化3. 缓存高度

   优化4. 将耗时操作放在异步线程来做

  24.平时工作中使用的动画库有哪些?

  faceBook的pop动画、

  25.objc实现多重继承

  oc不支持多重继承,只支持多层继承。要变相的实现多重继承,可以利用protocol来实现

  26.数组查找平衡点

  

int findBalancePoint(int a[], int n)
{
if (n == ) {
return -;
}
int leftSum = ;
int rightSum = ;
for (int i = , j = n - ; ; i++, j--) {
leftSum += a[i];
rightSum += a[j];
if (i < j) {//继续加
continue;
}else{
if (leftSum == rightSum) {//相等
if (i == j) {//奇数个
return i + ;
}else{//偶数个
return n;
}
}else{//不等
return -;
}
}
}
return -;
} int main(int argc, const char * argv[]) {
@autoreleasepool { int a[] = {-, , , , -, , };
int b[] = {, , , , , }; int found1 = findBalancePoint(a, );
int found2 = findBalancePoint(b, );
NSLog(@"%i %i", found1, found2); }
return ;
}

  

    

最新文章

  1. 哈,我自己翻译的小书,马上就完成了,是讲用python处理大数据框架hadoop,spark的
  2. java_method_数据去重
  3. Using LINQ to SharePoint
  4. [强连通分量] POJ 2762 Going from u to v or from v to u?
  5. HTML5自学笔记[ 10 ]简单的购物车拖拽
  6. 比较不错的JS 曲线图
  7. oracle通过query导出指定条件的数据
  8. 坑爹的 Hardware Reserved Memory (查看内存等)
  9. 【Java编程思想笔记】反射
  10. Python 有道翻译 爬虫 有道翻译API 突破有道翻译反爬虫机制
  11. core里使用log4net
  12. python requests的content和text方法的区别
  13. Lua 随机数生成问题
  14. 强化学习论文(Scalable agent alignment via reward modeling: a research direction)
  15. 什么是spu和sku
  16. 去除QT不使用参数的警告
  17. Kettle定时抽取两个库中的两个表到目标库SYS_OPLOG表
  18. 【问题定位】tcpdump 抓包简易命令
  19. Redis学习(7)-通用命令
  20. 推荐算法——非负矩阵分解(NMF)

热门文章

  1. Unity 游戏对象的组件列表
  2. ubuntu安装软件依赖解决
  3. Murano Weekly Meeting 2015.09.08
  4. 自己的spring boot starter
  5. redis开机启动,有密码
  6. Linux 拷贝
  7. HTTP的GET和POST格式解析
  8. form提交
  9. &lt;Android 基础(十七)&gt; ViewPager介绍
  10. 转:ArcGIS10.1正式版安装与破解