zoukankan      html  css  js  c++  java
  • iOS面试题总结(持续更新)

    过段时间打算跳槽,找了一些面试题来做,在这里做个总结方便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:1.f repeats:YES block:^(NSTimer * _Nonnull timer) {
                //创建一个timer并且在当前的runloop中执行。
            }];
            [NSTimer scheduledTimerWithTimeInterval:1.f repeats:NO block:^(NSTimer * _Nonnull timer) {
                
            }];
         //timer加入到runloop才能fire成功 [[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:
    2.f] interval:1.f repeats:YES block:^(NSTimer * _Nonnull timer) { }];
          //创建timer必须加入到run loop才能执行 [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);
        }

      还有一种方式是主动打破循环引用,将调用的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 == 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;
    }

      

     

        

  • 相关阅读:
    Spring中的InitializingBean接口的使用
    解决MyBatis异常:The content of elements must consist of well-formed character data or markup.
    idea2019版与maven3.6.2版本不兼容问题
    IntelliJ IDEA更新maven依赖包
    JVM里的垃圾回收机制
    ASP.NET Core 中文文档 第四章 MVC(2.2)模型验证【转载】
    asp.net mvc Model验证总结及常用正则表达式【转载】
    SQL一次性插入大量数据【转载】
    ASP.NET Core依赖注入解读&使用Autofac替代实现【转载】
    WCF、WebAPI、WCFREST、WebService之间的区别【转载】
  • 原文地址:https://www.cnblogs.com/pretty-guy/p/8397843.html
Copyright © 2011-2022 走看看