您的位置:首页 > 职场人生

iOS面试题总结(持续更新)

2018-02-01 10:45 447 查看
过段时间打算跳槽,找了一些面试题来做,在这里做个总结方便review,希望能对要面试的童鞋有帮助。

以下为面试题:

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

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:1.f repeats:YES block:^(NSTimer * _Nonnull timer) {

}];
[NSTimer scheduledTimerWithTimeInterval:1.f repeats:NO block:^(NSTimer * _Nonnull timer) {

}];
[[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:2.f] interval:1.f repeats:YES block:^(NSTimer * _Nonnull timer) {

}];
[NSTimer timerWithTimeInterval:1.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:0 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);
}


  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 == 1) {
return -1;
}
int leftSum = 0;
int rightSum = 0;
for (int i = 0, j = n - 1; ; i++, j--) {
leftSum += a[i];
rightSum += a[j];
if (i < j) {//继续加
continue;
}else{
if (leftSum == rightSum) {//相等
if (i == j) {//奇数个
return i + 1;
}else{//偶数个
return n;
}
}else{//不等
return -1;
}
}
}
return -1;
}

int main(int argc, const char * argv[]) {
@autoreleasepool {

int a[] = {-7, 1, 5, 2, -4, 3, 0};
int b[] = {1, 2, 3, 3, 2, 1};

int found1 = findBalancePoint(a, 7);
int found2 = findBalancePoint(b, 6);
NSLog(@"%i %i", found1, found2);

}
return 0;
}


  

    
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: