面试

作者: focusHYD | 来源:发表于2019-03-27 17:03 被阅读96次

    十三 如何重写类方法

    十四 NSTimer创建后,会在哪个线程运行。

    十五 id和NSObject*的区别

    typedef struct objc_object *id

    十六.ios开发逆向传值的几种方法整理

    第一种:代理传值

    第二个控制器:

    @protocol WJSecondViewControllerDelegate <NSObject>

    - (void)changeText:(NSString*)text;

    @end

    @property(nonatomic,assign)id<WJSecondViewControllerDelegate>delegate;

    - (IBAction)buttonClick:(UIButton*)sender {

    _str = sender.titleLabel.text;

    [self.delegate changeText:sender.titleLabel.text];

    [self.navigationController popViewControllerAnimated:YES];

    }

    第一个控制器:

    - (IBAction)pushToSecond:(id)sender {

    WJSecondViewController *svc = [[WJSecondViewController alloc]initWithNibName:@"WJSecondViewController" bundle:nil];

    svc.delegate = self;

    svc.str = self.navigationItem.title;

    [self.navigationController pushViewController:svc animated:YES];

    [svc release];

    }

    - (void)changeText:(NSString *)text{

    self.navigationItem.title = text;

    }

    第二种:通知传值

    第一个控制器:

    //注册监听通知

    [[NSNotificationCenter defaultCenter] addObserver:self        selector:@selector(limitDataForModel:) name:@"NOV" object:nil];

    - (void)limitDataForModel:(NSNotification *)noti{

    self.gamesInfoArray = noti.object;

    }

    第二个控制器:

    //发送通知

      [[NSNotificationCenter defaultCenter]    postNotificationName:@"NOV" object:gameArray];

    第三种:单例传值

    Single是一个单例类,并且有一个字符串类型的属性titleName

    在第二个控制器:

    - (IBAction)buttonClick:(UIButton*)sender {

    Single *single = [Single sharedSingle];

    single.titleName = sender.titleLabel.text;

    [self.navigationController popViewControllerAnimated:YES];

    }

    第一个控制器:

    - (void)viewWillAppear:(BOOL)animated{

    [super viewWillAppear:animated];

    Single *single = [Single sharedSingle];

    self.navigationItem.title = single.titleName;

    }

    第四种:block传值

    第二个控制器:

    @property (nonatomic,copy) void (^changeText_block)(NSString*);

    - (IBAction)buttonClick:(UIButton*)sender {

    _str = sender.titleLabel.text;

    self.changeText_block(sender.titleLabel.text);

    [self.navigationController popViewControllerAnimated:YES];

    }

    第一个控制器:

    - (IBAction)pushToSecond:(id)sender {

    WJSecondViewController *svc = [[WJSecondViewController alloc]initWithNibName:@"WJSecondViewController" bundle:nil];

    svc.str = self.navigationItem.title;

    [svc setChangeText_block:^(NSString *str) {

        >self.navigationItem.title = str;

    }];

    [self.navigationController pushViewController:svc animated:YES];

    }

    第五种:extern传值

    第二个控制器:

    extern NSString *btn;

    - (IBAction)buttonClick:(UIButton*)sender {

    btn = sender.titleLabel.text;

    [self.navigationController popViewControllerAnimated:YES];

    }

    第一个控制器:

    NSString *btn = nil;

    - (void)viewWillAppear:(BOOL)animated{

    [super viewWillAppear:animated];

    self.navigationItem.title = btn;

    }

    第六种:KVO传值

    第一个控制器:

    - (void)viewDidLoad {

    [super viewDidLoad];

    _vc =[[SecondViewController alloc]init];

    //self监听vc里的textValue属性

    [_vc addObserver:self forKeyPath:@"textValue" options:0 context:nil]; 

    }

    第二个控制器:

    - (IBAction)buttonClicked:(id)sender {

    self.textValue = self.textField.text;

    [self.navigationController popViewControllerAnimated:YES];

    }

    Method1:performSelector

    [self performSelector:@selector(delayMethod) withObject:nil/*可传任意类型参数*/ afterDelay:2.0];

    注:此方法是一种非阻塞的执行方式,未找到取消执行的方法。

    Method2:NSTimer定时器

    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(delayMethod) userInfo:nil repeats:NO];

    注:此方法是一种非阻塞的执行方式,

    Method3:NSThread线程的sleep

    [NSThread sleepForTimeInterval:2.0];

    注:此方法是一种阻塞执行方式,建议放在子线程中执行,否则会卡住界面。但有时还是需要阻塞执行,如进入欢迎界面需要沉睡3秒才进入主界面时。

    没有找到取消执行方式。

    Method4:GCD

    __block ViewController/*主控制器*/ *weakSelf = self;

    dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0/*延迟执行时间*/ * NSEC_PER_SEC));

    dispatch_after(delayTime, dispatch_get_main_queue(), ^{

        [weakSelf delayMethod];

    });`

    十七.NSPersistentStoreCoordinator  ,   NSManaged0bjectContext 和NSManaged0bject中的那些需要在线程中创建或者传递

     答:NSPersistentStoreCoordinator是持久化存储协调者,主要用于协调托管对象上下文和持久化存储区之间的关系。NSManagedObjectContext使用协调者的托管对象模型将数据保存到数据库,或查询数据。

    十八.您是否做过一部的网络处理和通讯方面的工作?如果有,能具体介绍一下实现策略么 

    答:使用NSOperation发送异步网络请求,使用NSOperationQueue管理线程数目及优先级,底层是用NSURLConnetion,

    81.你使用过Objective-C的运行时编程(Runtime Programming)么?如果使用过,你用它做了什么?你还能记得你所使用的相关的头文件或者某些方法的名称吗?

     答:Objecitve-C的重要特性是Runtime(运行时),在#import <objc/runtime.h> 下能看到相关的方法,用过objc_getClass()和class_copyMethodList()获取过私有API;使用  

    ```objective-c

    Method method1 = class_getInstanceMethod(cls, sel1);

    Method method2 = class_getInstanceMethod(cls, sel2);

    method_exchangeImplementations(method1, method2);  

    ```   

    代码交换两个方法,在写unit test时使用到。

    82.Core开头的系列的内容。是否使用过CoreAnimation和CoreGraphics。UI框架和CA,CG框架的联系是什么?分别用CA和CG做过些什么动画或者图像上的内容。(有需要的话还可以涉及Quartz的一些内容)

    答:UI框架的底层有CoreAnimation,CoreAnimation的底层有CoreGraphics。    

    UIKit | 

    ------------ | 

    Core Animation | 

    Core Graphics |

    Graphics Hardware|  

    使用CA做过menu菜单的展开收起(太逊了)  

    十九.是否使用过CoreText或者CoreImage等?如果使用过,请谈谈你使用CoreText或者CoreImage的体验。

    答:CoreText可以解决复杂文字内容排版问题。CoreImage可以处理图片,为其添加各种效果。体验是很强大,挺复杂的。

    二十.NSNotification和KVO的区别和用法是什么?什么时候应该使用通知,什么时候应该使用KVO,它们的实现上有什么区别吗?如果用protocol和delegate(或者delegate的Array)来实现类似的功能可能吗?如果可能,会有什么潜在的问题?如果不能,为什么?(虽然protocol和delegate这种东西面试已经面烂了…)

    答:NSNotification是通知模式在iOS的实现,KVO的全称是键值观察(Key-value observing),其是基于KVC(key-value coding)的,KVC是一个通过属性名访问属性变量的机制。例如将Module层的变化,通知到多个Controller对象时,可以使用NSNotification;如果是只需要观察某个对象的某个属性,可以使用KVO。

    对于委托模式,在设计模式中是对象适配器模式,其是delegate是指向某个对象的,这是一对一的关系,而在通知模式中,往往是一对多的关系。委托模式,从技术上可以现在改变delegate指向的对象,但不建议这样做,会让人迷惑,如果一个delegate对象不断改变,指向不同的对象。

    二十一.你用过NSOperationQueue么?如果用过或者了解的话,你为什么要使用NSOperationQueue,实现了什么?请描述它和G.C.D的区别和类似的地方(提示:可以从两者的实现机制和适用范围来描述)。

    答:使用NSOperationQueue用来管理子类化的NSOperation对象,控制其线程并发数目。GCD和NSOperation都可以实现对线程的管理,区别是 NSOperation和NSOperationQueue是多线程的面向对象抽象。项目中使用NSOperation的优点是NSOperation是对线程的高度抽象,在项目中使用它,会使项目的程序结构更好,子类化NSOperation的设计思路,是具有面向对象的优点(复用、封装),使得实现是多线程支持,而接口简单,建议在复杂项目中使用。

    项目中使用GCD的优点是GCD本身非常简单、易用,对于不复杂的多线程操作,会节省代码量,而Block参数的使用,会是代码更为易读,建议在简单项目中使用。

    二十二.既然提到G.C.D,那么问一下在使用G.C.D以及block时要注意些什么?它们两是一回事儿么?block在ARC中和传统的MRC中的行为和用法有没有什么区别,需要注意些什么?

    答:使用block是要注意,若将block做函数参数时,需要把它放到最后,GCD是Grand Central Dispatch,是一个对线程开源类库,而Block是闭包,是能够读取其他函数内部变量的函数。

    二十三. 对于Objective-C,你认为它最大的优点和最大的不足是什么?对于不足之处,现在有没有可用的方法绕过这些不足来实现需求。如果可以的话,你有没有考虑或者实践过重新实现OC的一些功能,如果有,具体会如何做?

    答:最大的优点是它的运行时特性,不足是没有命名空间,对于命名冲突,可以使用长命名法或特殊前缀解决,如果是引入的第三方库之间的命名冲突,可以使用link命令及flag解决冲突。

    二十四. 你实现过一个框架或者库以供别人使用么?如果有,请谈一谈构建框架或者库时候的经验;如果没有,请设想和设计框架的public的API,并指出大概需要如何做、需要注意一些什么方面,来使别人容易地使用你的框架。

    答:抽象和封装,方便使用。首先是对问题有充分的了解,比如构建一个文件解压压缩框架,从使用者的角度出发,只需关注发送给框架一个解压请求,框架完成复杂文件的解压操作,并且在适当的时候通知给是哦难过者,如解压完成、解压出错等。在框架内部去构建对象的关系,通过抽象让其更为健壮、便于更改。其次是API的说明文档。

    KVO就是cocoa框架实现的观察者模式,一般同KVC搭配使用,通过KVO可以监测一个值的变化,比如View的高度变化。是一对多的关系,一个值的变化会通知所有的观察者。

    NSNotification是通知,也是一对多的使用场景。在某些情况下,KVO和NSNotification是一样的,都是状态变化之后告知对方。NSNotification的特点,就是需要被观察者先主动发出通知,然后观察者注册监听后再来进行响应,比KVO多了发送通知的一步,但是其优点是监听不局限于属性的变化,还可以对多种多样的状态变化进行监听,监听范围广,使用也更灵活。

    delegate 是代理,就是我不想做的事情交给别人做。比如狗需要吃饭,就通过delegate通知主人,主人就会给他做饭、盛饭、倒水,这些操作,这些狗都不需要关心,只需要调用delegate(代理人)就可以了,由其他类完成所需要的操作。所以delegate是一对一关系。

    block是delegate的另一种形式,是函数式编程的一种形式。使用场景跟delegate一样,相比delegate更灵活,而且代理的实现更直观。

    KVO一般的使用场景是数据,需求是数据变化,比如股票价格变化,我们一般使用KVO(观察者模式)。delegate一般的使用场景是行为,需求是需要别人帮我做一件事情,比如买卖股票,我们一般使用delegate。

    Notification一般是进行全局通知,比如利好消息一出,通知大家去买入。delegate是强关联,就是委托和代理双方互相知道,你委托别人买股票你就需要知道经纪人,经纪人也不要知道自己的顾客。Notification是弱关联,利好消息发出,你不需要知道是谁发的也可以做出相应的反应,同理发消息的人也不需要知道接收的人也可以正常发出消息。

    GCD方法,通过向主线程队列发送一个block块,使block里的方法可以在主线程中执行。

    NSOperation 方法

    NSThread 方法

    RunLoop方法

    计时器只能调用实例方法,但是可以在这个实例方法里面调用静态方法。

    使用计时器需要注意,计时器一定要加入RunLoop中,并且选好model才能运行。scheduledTimerWithTimeInterval方法创建一个计时器并加入到RunLoop中所以可以直接使用。

    如果计时器的repeats选择YES说明这个计时器会重复执行,一定要在合适的时机调用计时器的invalid。不能在dealloc中调用,因为一旦设置为repeats 为yes,计时器会强持有self,导致dealloc永远不会被调用,这个类就永远无法被释放。比如可以在viewDidDisappear中调用,这样当类需要被回收的时候就可以正常进入dealloc中了。

    在子类中实现一个同基类名字一样的静态方法

    在调用的时候不要使用类名调用,而是使用[self class]的方式调用。原理,用类名调用是早绑定,在编译期绑定,用[self class]是晚绑定,在运行时决定调用哪个方法。

    用scheduledTimerWithTimeInterval创建的,在哪个线程创建就会被加入哪个线程的RunLoop中就运行在哪个线程

    自己创建的Timer,加入到哪个线程的RunLoop中就运行在哪个线程。

    id是一个 objc_object 结构体指针,定义是

    id可以理解为指向对象的指针。所有oc的对象 id都可以指向,编译器不会做类型检查,id调用任何存在的方法都不会在编译阶段报错,当然如果这个id指向的对象没有这个方法,该崩溃还是会崩溃的。

    NSObject *指向的必须是NSObject的子类,调用的也只能是NSObjec里面的方法否则就要做强制类型转换。

    不是所有的OC对象都是NSObject的子类,还有一些继承自NSProxy。NSObject *可指向的类型是id的子集。

    二十五.说说你理解weak属性?

    weak实现原理:

    Runtime维护了一个weak表,用于存储指向某个对象的所有weak指针。weak表其实是一个hash(哈希)表,Key是所指对象的地址,Value是weak指针的地址(这个地址的值是所指对象的地址)数组。

    1、初始化时:runtime会调用objc_initWeak函数,初始化一个新的weak指针指向对象的地址。

    2、添加引用时:objc_initWeak函数会调用 objc_storeWeak() 函数, objc_storeWeak() 的作用是更新指针指向,创建对应的弱引用表。

    3、释放时,调用clearDeallocating函数。clearDeallocating函数首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。

    追问的问题一:

    1.实现weak后,为什么对象释放后会自动为nil?

    runtime?对注册的类, 会进行布局,对于?weak?对象会放入一个?hash?表中。 用?weak?指向的对象内存地址作为?key,当此对象的引用计数为?0?的时候会?dealloc,假如?weak?指向的对象内存地址是?a?,那么就会以?a?为键, 在这个?weak?表中搜索,找到所有以?a?为键的?weak?对象,从而设置为?nil?。

    追问的问题二:

    2.当weak引用指向的对象被释放时,又是如何去处理weak指针的呢?

    1、调用objc_release

    2、因为对象的引用计数为0,所以执行dealloc

    3、在dealloc中,调用了_objc_rootDealloc函数

    4、在_objc_rootDealloc中,调用了object_dispose函数

    5、调用objc_destructInstance

    6、最后调用objc_clear_deallocating,详细过程如下:

    a. 从weak表中获取废弃对象的地址为键值的记录

    b. 将包含在记录中的所有附有 weak修饰符变量的地址,赋值为 nil

    c. 将weak表中该记录删除

    d. 从引用计数表中删除废弃对象的地址为键值的记录

    二十六.假如Controller太臃肿,如何优化?

    1.将网络请求抽象到单独的类中

    方便在基类中处理公共逻辑;

    方便在基类中处理缓存逻辑,以及其它一些公共逻辑;

    方便做对象的持久化。

    2.将界面的封装抽象到专门的类中

    构造专门的 UIView 的子类,来负责这些控件的拼装。这是最彻底和优雅的方式,不过稍微麻烦一些的是,你需要把这些控件的事件回调先接管,再都一一暴露回 Controller。

    3.构造 ViewModel

    借鉴MVVM。具体做法就是将 ViewController 给 View 传递数据这个过程,抽象成构造 ViewModel 的过程。

    4.专门构造存储类

    专门来处理本地数据的存取。

    5.整合常量

    二十七.项目中网络层如何做安全处理?

    1、尽量使用https

    https可以过滤掉大部分的安全问题。https在证书申请,服务器配置,性能优化,客户端配置上都需要投入精力,所以缺乏安全意识的开发人员容易跳过https,或者拖到以后遇到问题再优化。https除了性能优化麻烦一些以外其他都比想象中的简单,如果没精力优化性能,至少在注册登录模块需要启用https,这部分业务对性能要求比较低。

    2、不要传输明文密码

    不知道现在还有多少app后台是明文存储密码的。无论客户端,server还是网络传输都要避免明文密码,要使用hash值。客户端不要做任何密码相关的存储,hash值也不行。存储token进行下一次的认证,而且token需要设置有效期,使用refresh

    token去申请新的token。

    3、Post并不比Get安全

    事实上,Post和Get一样不安全,都是明文。参数放在QueryString或者Body没任何安全上的差别。在Http的环境下,使用Post或者Get都需要做加密和签名处理。

    4、不要使用301跳转

    301跳转很容易被Http劫持攻击。移动端http使用301比桌面端更危险,用户看不到浏览器地址,无法察觉到被重定向到了其他地址。如果一定要使用,确保跳转发生在https的环境下,而且https做了证书绑定校验。

    5、http请求都带上MAC

    所有客户端发出的请求,无论是查询还是写操作,都带上MAC(Message Authentication

    Code)。MAC不但能保证请求没有被篡改(Integrity),还能保证请求确实来自你的合法客户端(Signing)。当然前提是你客户端的key没有被泄漏,如何保证客户端key的安全是另一个话题。MAC值的计算可以简单的处理为hash(request

    params+key)。带上MAC之后,服务器就可以过滤掉绝大部分的非法请求。MAC虽然带有签名的功能,和RSA证书的电子签名方式却不一样,原因是MAC签名和签名验证使用的是同一个key,而RSA是使用私钥签名,公钥验证,MAC的签名并不具备法律效应。

    6、http请求使用临时密钥

    高延迟的网络环境下,不经优化https的体验确实会明显不如http。在不具备https条件或对网络性能要求较高且缺乏https优化经验的场景下,http的流量也应该使用AES进行加密。AES的密钥可以由客户端来临时生成,不过这个临时的AES

    key需要使用服务器的公钥进行加密,确保只有自己的服务器才能解开这个请求的信息,当然服务器的response也需要使用同样的AES

    key进行加密。由于http的应用场景都是由客户端发起,服务器响应,所以这种由客户端单方生成密钥的方式可以一定程度上便捷的保证通信安全。

    7、AES使用CBC模式

    不要使用ECB模式,记得设置初始化向量,每个block加密之前要和上个block的秘文进行运算。

    二十八.main()之前的过程有哪些?

    1、main之前的加载过程

    1)dyld 开始将程序二进制文件初始化

    2)交由ImageLoader 读取 image,其中包含了我们的类,方法等各种符号(Class、Protocol 、Selector、 IMP)

    3)由于runtime 向dyld 绑定了回调,当image加载到内存后,dyld会通知runtime进行处理

    4)runtime 接手后调用map_images做解析和处理

    5)接下来load_images 中调用call_load_methods方法,遍历所有加载进来的Class,按继承层次依次调用Class的+load和其他Category的+load方法

    6)至此 所有的信息都被加载到内存中

    7)最后dyld调用真正的main函数

    1、Runtime相关面试问题

    Runtime是什么?见名知意,其概念无非就是“因为 Objective-C 是一门动态语言,所以它需要一个运行时系统……这就是 Runtime 系统”云云。对博主这种菜鸟而言,Runtime 在实际开发中,其实就是一组C语言的函数。胡适说:“多研究些问题,少谈些主义”,云山雾罩的概念听多了总是容易头晕,接下来我们直接上runtime思维导图帮助大家理清思路:

    runtime思维导图

    2、多线程相关面试问题

    多线程是一个比较轻量级的方法来实现单个应用程序内多个代码执行路径, 从技术角度来看,一个线程就是一个需要管理执行代码的内核级和应用级数据结 构组合。

    多线程思维导图

    3、RunLoop相关面试问题

    我相信大多数开发者一样,迷惑于runloop,最初只了解可以通过runloop一些监听事件的通知来做一些事情,优化性能。关于runloop源码的基础知识,可以参考下面的思维导图:

    runloop思维导图

    4、设计模式相关面试问题

    设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。

    使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的;模式使代码编制真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。

    设计模式思维导图

    5、架构/框架相关面试问题

    “100个读者就有100个哈姆雷特”一样,对于架构的理解不同的软件工程师有不同的看法。架构设计往往是一个权衡的过程,每一个架构设计者都要考虑到各个因素,比如团队成员的技术水平、具体的业务场景、项目的成长阶段和开发周期。下图是小编的一些架构理念,仅供参考:

    架构/框架思维导图

    6、算法相关面试问题

    算法思维导图

    7、第三方库相关面试问题

    相关文章

      网友评论

        本文标题:面试

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