面试题

作者: 木日一勿 | 来源:发表于2020-03-09 18:46 被阅读0次

    1.dispatch_barrier_async的作用是什么?

    dispatch_barrier_async 函数配合 Concurrent Dispatch Queue 一起使用可以在并行的任务中插入中间任务。

    dispatch_queue_t queue=dispatch_queue_create("com.example.gcd.ForBarrier",DISPATCH_QUEUE_CONCURRENT);dispatch_async(queue,blk0_for_reading);dispatch_async(queue,blk1_for_reading);dispatch_async(queue,blk2_for_reading);dispatch_async(queue,blk3_for_reading);dispatch_barrier_async(queue,blk_for_writing);dispatch_async(queue,blk4_for_reading);dispatch_async(queue,blk5_for_reading);dispatch_async(queue,blk6_for_reading);dispatch_async(queue,blk7_for_reading);

    dispatch_barrier_async 函数会等待当前 Concurrent Dispatch Queue 中并行执行的读取任务(blk0-3_for_reading)都结束后,再将指定的 blk_for_writing 任务添加到 Concurrent Dispatch Queue 中,然后只有在这个任务执行完毕后,后面添加到 Concurrent Dispatch Queue 的任务(blk4-7_for_reading)才恢复正常的并行执行的模式。可见,Concurrent Dispatch Queue 和 dispatch_barrier_async 搭配使用可以使编码非常清晰,同时可以实现高效率的数据库访问和文件访问。

    2.什么是 RunLoop?

    RunLoop 就是一个事件处理的循环,相当于do-while。用来不停的调度工作以及处理输入事件。使用 RunLoop 的目的是让你的线程在有工作的时候忙于工作,而没工作的时候处于休眠状态。 runloop 的设计是为了减少 cpu 无谓的空转。使用场景:1、需要使用 Port 或者自定义 InputSource 与其他线程进行通讯;2、子线程中使用了定时器;3、Cocoa 中使用任何performSelector 到了线程中运行方法;4、线程执行周期性任务。仅当在为你的程序创建辅助线程的时候,你才需要显式运行一个RunLoop。

    3.id 声明的对象有什么特性?

    id声明的对象可以是任意类型的OC对象;具有运行时的特点,在程序运行时才确定对象的类型。

    4.什么情况使用 weak 关键字,相比 assign 有什么不同?

    1、在 ARC 中,在有可能出现循环引用的时候,往往要通过让其中一端使用 weak 来解决,比如: delegate 代理属性

    2、自身已经对它进行一次强引用,没有必要再强引用一次,此时也会使用 weak,自定义 IBOutlet 控件属性一般也使用 weak;当然,也可以使用strong。

    不同点:

    1、weak此特质表明该属性定义了一种“非拥有关系” (nonowning relationship)。为这种属性设置新值时,设置方法既不保留新值,也不释放旧值。此特质同assign类似,然而在属性所指的对象遭到摧毁时,属性值也会清空(nil out)。而assign的“设置方法”只会执行针对“纯量类型” (scalar type,例如 CGFloat 或NSlnteger 等)的简单赋值操作。

    2、assign 可以用非 OC 对象,而 weak 必须用于 OC 对象

    5.属性关键字 readwrite,readonly,assign,retain,copy,nonatomic 各是什么作用,在那种情况下用?

    ①.readwrite 是可读可写特性;需要生成 getter 方法和 setter 方法时

    ②. readonly 是只读特性 只会生成 getter 方法 不会生成 setter 方法 ;不希望属性在类外改变

    ③.assign 是赋值特性,setter 方法将传入参数赋值给实例变量;仅设置变量时;

    ④.retain 表示持有特性,setter 方法将传入参数先保留,再赋值,传入参数的 retaincount 会+1;

    ⑤.copy 表示赋值特性, setter 方法将传入对象复制一份;需要完全一份新的变量时。

    ⑥.nonatomic 非原子操作,决定编译器生成的 settergetter 是否是原子操作,atomic 表示多线程安全,一般使用 nonatomic

    6.常见的 Objective-C 的数据类型有那些,和C的基本数据类型有什么区别?如:NSInteger和int

    object-c的数据类型有NSString, NSNumber, NSArray, NSMutableArray,NSData 等等,这些都是 class,创建后便是对象,而 C 语言的基本数据类型 int,只是一定字节的内存空间,用于存放数值;NSInteger 是基本数据类型,并不是 NSNumber 的子类,当然也不是 NSObject 的子类。 NSInteger 是基本数据类型 Int 或者 Long 的别名(NSInteger 的定义typedeflongNSInteger),它的区别在于,NSInteger 会根据系统是 32位还是 64 位来决定是本身是 int 还是 Long。

    7.Objective-C的类可以多重继承么?可以实现多个接口么?Category是什么?重写一个类的方式用继承好还是分类好?为什么?

    Objective-c的类不可以有多继承,OC里面都是单继承,多继承可以用protocol委托代理来模拟实现可以实现多个接口,可以通过实现多个接口完成OC的多重继承Category是类别,也叫类目,用Category重写类的方法,它仅仅只对本Category有效,并不会影响到其他类和原有类的关系,如果是要在不修改原有类的基础上增加其他原有类没有的方法,就要用类目,继承是可以重写父类的方法,只是子类继承父类的方法来使用。

    8.tableView的重用机制?

    UITableView 通过重用单元格来达到节省内存的目的: 通过为每个单元格指定一个重用标识符,即指定了单元格的种类,当屏幕上的单元格滑出屏幕时,系统会把这个单元格添加到重用队列中,等待被重用,当有新单元格从屏幕外滑入屏幕内时,从重用队列中找看有没有可以重用的单元格,如果有,就拿过来用,如果没有就创建一个来使用。

    9.iOS中常用的数据存储方式有哪些?

    所有的本地持久化数据存储的本质都是写文件,而且只能存到沙盒中。

    沙盒机制是苹果的一项安全机制,本质就是系统给每个应用分配了一个文件夹来存储数据,而且每个应用只能访问分配给自己的那个文件夹,其他应用的文件夹是不能访问的。

    数据存储的核心都是写文件。主要有四种持久化方式:属性列表,对象序列化,SQLite 数据库, CoreData

    属性列表:应用于少量数据存储,比如登陆的用户信息,应用程序配置信息等。只有NSString ,NSArray,NSDictory,NSData,可以WriteToFile;存储的依旧是plist文件,plist文件可以存储的7种数据类型:array,dictory,string,bool,data,date,number。

    10.Objective-C 如何对内存管理的,说说你的看法和解决方法?

    答:Objective-C的内存管理主要有三种方式ARC(自动内存计数)、手动内存计数、内存池。

    1). (Garbage Collection)自动内存计数:这种方式和java类似,在你的程序的执行过程中。始终有一个高人在背后准确地帮你收拾垃圾,你不用考虑它什么时候开始工作,怎样工作。你只需要明白,我申请了一段内存空间,当我不再使用从而这段内存成为垃圾的时候,我就彻底的把它忘记掉,反正那个高人会帮我收拾垃圾。遗憾的是,那个高人需要消耗一定的资源,在携带设备里面,资源是紧俏商品所以iPhone不支持这个功能。所以“Garbage Collection”不是本入门指南的范围,对“Garbage Collection”内部机制感兴趣的同学可以参考一些其他的资料,不过说老实话“Garbage Collection”不大适合适初学者研究。

    解决:通过alloc – initial方式创建的,创建后引用计数+1,此后每retain一次引用计数+1,那么在程序中做相应次数的release就好了.

    2). (Reference Counted)手动内存计数:就是说,从一段内存被申请之后,就存在一个变量用于保存这段内存被使用的次数,我们暂时把它称为计数器,当计数器变为0的时候,那么就是释放这段内存的时候。比如说,当在程序A里面一段内存被成功申请完成之后,那么这个计数器就从0变成1(我们把这个过程叫做alloc),然后程序B也需要使用这个内存,那么计数器就从1变成了2(我们把这个过程叫做retain)。紧接着程序A不再需要这段内存了,那么程序A就把这个计数器减1(我们把这个过程叫做release);程序B也不再需要这段内存的时候,那么也把计数器减1(这个过程还是release)。当系统(也就是Foundation)发现这个计数器变 成员了0,那么就会调用内存回收程序把这段内存回收(我们把这个过程叫做dealloc)。顺便提一句,如果没有Foundation,那么维护计数器,释放内存等等工作需要你手工来完成。

    解决:一般是由类的静态方法创建的,函数名中不会出现alloc或init字样,如[NSString string]和[NSArray arrayWithObject:],创建后引用计数+0,在函数出栈后释放,即相当于一个栈上的局部变量.当然也可以通过retain延长对象的生存期.

    3). (NSAutoRealeasePool)内存池:可以通过创建和释放内存池控制内存申请和回收的时机.

    解决:是由autorelease加入系统内存池,内存池是可以嵌套的,每个内存池都需要有一个创建释放对,就像main函数中写的一样.使用也很简单,比如[[[NSString alloc]initialWithFormat:@”Hey you!”] autorelease],即将一个NSString对象加入到最内层的系统内存池,当我们释放这个内存池时,其中的对象都会被释放.

    11.HTTP协议中 POST 方法和 GET 方法有那些区别?

    1.get:请求指定的页面信息,并返回实体主体

    2.post:向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。post请求可能会导致新的资源的建立和/或已有资源的修改

    通俗地说,get方法一般用来负责获取数据,或者将一些简短的数据放到URL参数中传递到服务器。而由于get方法最多在url中携带1024字节数据,且将数据放到url中传递太不安全,数据量大时url也会变得冗长。所以传递数据量大或者安全性要求高的数据的时候,最好使用post方法来传递数据。



    12.实现一个大数相乘的算法

    要点:按位相乘,进位处理———乘法的结合律。一起来回顾一下,额,小学数学:   123 * 456 =(3+20+100)*456 = (3*456)+(20*456)+(100*456)

    将大数A个位分别与大数B的每一位进行相乘(沿用大数相加的进位法),获得一个字符串(这里先叫做“个位字符串”),放入空数组中;将大数A十位分别与大数B的每一位进行相乘,得到下一个字符串(“十位字符串”),也放数组中;以此类推,最终得到一个字符串数组,然后给十位字符串乘10(就是后面插入一个“0”),百位字符串乘100(就是后面插入两个“0”)....  得到一个新的字符串,最后用前面大数用大数相加函数(前面已经提及)相加,将全部加起来即可。就这样,思路还是挺简单。

    + (NSString*)mutiplyOfString:(NSString*)strOne AndString:(NSString*)strTwo{

        NSMutableString *One = [NSMutableString stringWithFormat:@"%@",strOne ];

        NSMutableString *Two = [NSMutableString stringWithFormat:@"%@",strTwo ];

        int jin =0;

        NSMutableString *strJ = [NSMutableString new];

        NSMutableString *strT = [NSMutableString new];  // strJ的正序

        NSString *sum = [NSString new];

        NSMutableArray * strJArr = [NSMutableArray array];

        for(NSInteger i = One.length-1; i >=0; i--) {

            strJ = [NSMutableString new];

            strT = [NSMutableString new];

            jin =0;

            unichar  onenum= [One characterAtIndex:i];

            for(NSInteger j = Two.length-1; j >=0; j--){

                unichartwonum = [Two characterAtIndex:j];

                int onum = [[NSString stringWithFormat:@"%c",onenum]intValue];

                int tnum = [[NSString stringWithFormat:@"%c",twonum]intValue];

                int c = onum * tnum +jin;

                int z = c%10;

                jin = c/10;

                if(j !=0) {

                    [strJ appendFormat:@"%d",z];

                }else{

                    //是否最后一位

                    [strJ appendFormat:@"%d",c%10];

                    if(c/10!=0) {

                        [strJ appendFormat:@"%d",c/10];

                    }

                }

            }

            // 正序操作

            for(NSInteger a = strJ.length-1; a>=0;a--) {

                unicharc = [strJ characterAtIndex:a];

                [strT appendFormat:@"%c",c];

            }

            // 将strT放入数组,稍后加0后进行大数相加;

            [strJArr addObject:strT];

        }

        if(strJArr.count==0){

            return @"您的输入有误!";

        }

        for(NSIntegerk =0; k < strJArr.count; k++){

            NSMutableString*strP = strJArr[k];

            // 高位数补0

            for(NSInteger i = k;i >0;i--) {

                [strP insertString:[NSString stringWithFormat:@"0"] atIndex:strP.length];

            }

            // 大数相加

            sum = [BigNumber AddadditionOfString:sumAndString:strP];

        }

        return sum;

    }

    13.在ARC下,不显示的指定任何属性关键字时,默认的关键字有哪些?

    对应基本数据类型默认关键字是:atomic, readwrite, assign。

    对于普通的 Objective-C 对象默认关键字是:atomic, readwrite, strong。

    14.用@property声明NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?

    经常使用copy关键字原因:

    1、因为父类指针可以指向子类对象,使用copy的目的是为了让本对象的属性不受外界影响,使用copy无论给我传入是一个可变对象还是不可对象,我本身持有的就是一个不可变的副本.

    如果改用strong关键字,可能造成什么问题?

    2、如果我们使用是strong,那么这个属性就有可能指向一个可变对象,如果这个可变对象在外部被修改了,那么会影响该属性.

    copy使用原理:

    3、

    -->copy此特质所表达的所属关系与strong类似。

    -->然而设置方法并不保留新值,而是将其“拷贝” (copy)。

    -->当属性类型为NSString时,经常用此特质来保护其封装性,因为传递给设置方法的新值有可能指向一个NSMutableString类的实例。

    -->这个类是NSString的子类,表示一种可修改其值的字符串,此时若是不拷贝字符串,那么设置完属性之后,字符串的值就可能会在对象不知情的情况下遭人更改。

    -->所以,这时就要拷贝一份“不可变” (immutable)的字符串,确保对象中的字符串值不会无意间变动。

    -->只要实现属性所用的对象是“可变的” (mutable),就应该在设置新属性值时拷贝一份。

    15.在一个类别中如何添加一个新属性

    ios中利用类别给已有的类扩展方法是可以的,但是如果直接的添加属性是会报错的。利用runtime可以达到添加属性的目的。

    1.先创建一个分类,以下以UIImage为例子。

    2.增加一个属性。

    3.导入runtime框架,重写set方法和get方法

    @interface UIImage (Name)

    @property (nonatomic,copy) NSString * name;

    @end

    #import <objc/runtime.h>

    @implementation UIImage (Name)

    static const void *classNameKey = &classNameKey;

    -(void)setName:(NSString *)name{

        objc_setAssociatedObject(self, classNameKey, name,  OBJC_ASSOCIATION_COPY_NONATOMIC);

    }

    -(NSString *)name{

      return  objc_getAssociatedObject(self, classNameKey);

    }

    @end

    16.什么是事件传递响应链

    事件传递

    当iOS程序中发生触摸事件后,系统会将事件加入到UIApplication管理的一个任务队列中

    UIApplication将处于任务队列最前端的事件向下分发。即UIWindow。

    UIWindow将事件向下分发,即UIView。

    UIView首先看自己是否能处理事件,触摸点是否在自己身上。如果能,那么继续寻找子视图。

    遍历子控件,重复以上两步。

    如果没有找到,那么自己就是事件处理者。如果自己不能处理,那么不做任何处理。

    这个从父控件到子控件寻找处理事件最合适的view的过程,如果父视图不接受事件处理(上面三种情况),则子视图也不能接收事件。事件只要触摸了就会产生,关键在于是否有最合适的view来处理和接收事件,如果遍历到最后都没有最合适的view来接收事件,则该事件被废弃。

    响应链

    是从最合适的view开始传递,处理事件传递给下一个响应者,响应者链的传递方法是事件传递的反方法,如果所有响应者都不处理事件,则事件被丢弃。我们通常用响应者链来获取上几级响应者,方法是UIResponder的nextResponder方法。

    17.使用block时什么情况会发生引用循环,如何解决

    一个对象中强引用了block,在block中又强引用了该对象,就会发射循环引用。

    解决方法是将该对象使用__weak或者__block修饰符修饰之后再在block中使用。

    id weak weakSelf = self; 或者 weak __typeof(&*self)weakSelf = self该方法可以设置宏

    id __block weakSelf = self;

    或者将其中一方强制制空xxx = nil。

    检测代码中是否存在循环引用问题,可使用 Facebook 开源的一个检测工具FBRetainCycleDetector

    18.简述objc中向一个对象发送消息,发生了什么?

    objc在向一个对象发送消息时,runtime库会根据对象的isa指针找到该对象实际所属的类,然后在该类中的方法列表以及其父类方法列表中寻找方法运行,然后在发送消息的时候,objc_msgSend方法不会返回值,所谓的返回内容都是具体调用时执行的。

    19.一个objc对象的isa的指针指向什么?有什么作用?

    指向他的类对象,从而可以找到对象上的方法

    20.什么是runloop,它和线程有什么关系

    线程和 RunLoop 之间是一一对应的,其关系是保存在一个全局的 Dictionary 里。线程刚创建时并没有 RunLoop,如果你不主动获取,那它一直都不会有。RunLoop 的创建是发生在第一次获取时,RunLoop 的销毁是发生在线程结束时。你只能在一个线程的内部获取其 RunLoop(主线程除外)

    21.iOS Block和代理的区别与使用

    NotificationCenter 通知中心:“一对多”,在APP中,很多控制器都需要知道一个事件,应该用通知;

    delegate 代理委托:“一对一”,对同一个协议,一个对象只能设置一个代理delegate,所以单例对象就不能用代理;代理更注重过程信息的传输:比如发起一个网络请求,可能想要知道此时请求是否已经开始、是否收到了数据、数据是否已经接受完成、数据接收失败

    block(闭包) block和delegate一样,一般都是“一对一”之间通信交互,相比代理block有以下特点

    1.写法更简练,不需要写protocol、函数等等

    2.block注重结果的传输:比如对于一个事件,只想知道成功或者失败,并不需要知道进行了多少或者额外的一些信息

    3.block需要注意防止循环引用

    优先使用block。

    如果回调的状态很多,多于三个使用代理。

    如果回调的很频繁,次数很多,像UITableview,每次初始化、滑动、点击都会回调,使用代理。

    block和代理都各有优缺点,所以我们一定要理解区分使用场景,应用适合的回调方式。优化APP的性能,提高流畅性,从点滴做起。


    22.谈一谈怎么实现预加载

    预加载是提前加载资源,对于图片数量大的网站极具优势,保证了图片快速展示,牺牲服务器性能去提升用户体验。

    (1)预加载就是提前加载图片,用户查看时可直接从本地缓冲中渲染。

    (2)使用预加载的好处:对图片画廊及图片占据很大比例的网站来说十分有利,它保证了图片快速、无缝地发布,也可帮助用户在浏览你网站内容时获得更好的用户体验。

    (3)实现预加载的方法:

    方法一:用CSS和JavaScript实现预加载

    方法二:仅使用JavaScript实现预加载

    方法三:使用Ajax实现预加载

    相关文章

      网友评论

          本文标题:面试题

          本文链接:https://www.haomeiwen.com/subject/yfapdhtx.html