iOS面试题整理---[难度]**

问题[※]:在一个函数中的局部变量,需要return,那么这个局部变量什么时候会被释放?它是分配在哪里?

 

问题[※]:UIView和CALayer的层级关系和区别?

1.由于UIView继承自UIResponse,所以它是可以相应时间的,而CALayer是继承自NSObject,没有可以相应时间的接口。

2.UIView侧重于展示内容,而CALayer则侧重于图形和界面的绘制。

3.当View展示的时候,View是layer的CALayerDelegate,View展示的内容是由CALayer进行display的。

4.View内容的展示依赖于CALayer对于内容的绘制,UIView的frame也是由内部的CALayer进行绘制的。

5.对于UIView的属性修改,不会引起动画效果,但是对于CALayer的属性修改,是支持默认动画效果的,在VIew执行动画的时候,VIew是layer的代理,layer通过actionForLayer:forkey相对应的代理VIew请求动画action。

 

问题[※]:layoutsubview何时被调?

layoutSubviews在以下情况下会被调用:
1、init初始化不会触发layoutSubviews 但是用initWithFrame 进行初始化时,当rect的值不为CGRectZero时,也会触发
2、addSubview会触发layoutSubviews
3、设置view的Frame会触发layoutSubviews,当然前提是frame的值设置前后发生了变化
4、滚动一个UIScrollView会触发layoutSubviews
5、旋转Screen会触发父UIView上的layoutSubviews事件
6、改变一个UIView大小 以及位置的时候也会触发父UIView上的layoutSubviews事件
layoutSubviews方法调用先于drawRect,也就是先布局子视图,在重绘。

 

 

 

问题[※]:抓包工具的原理是啥?
我们分析了HTTPS的安全通信过程,知道了HTTPS可以有效防止中间人攻击。但用过抓包工具的人都知道,比如Charles,Fiddler是可以抓取HTTPS请求并解密的,它们是如何做到的呢?

首先来看Charles官网对HTTPS代理的描述:
Charles can be used as a man-in-the-middle HTTPS proxy, enabling you to view in plain text the communication between web browser and SSL web server.

Charles does this by becoming a man-in-the-middle. Instead of your browser seeing the server’s certificate, Charles dynamically generates a certificate for the server and signs it with its own root certificate (the Charles CA Certificate). Charles receives the server’s certificate, while your browser receives Charles’s certificate. Therefore you will see a security warning, indicating that the root authority is not trusted. If you add the Charles CA Certificate to your trusted certificates you will no longer see any warnings – see below for how to do this.

Charles作为一个中间人代理,当浏览器和服务器通信时,Charles接收服务器的证书,但动态生成一张证书发送给浏览器,也就是说Charles作为中间代理在浏览器和服务器之间通信,所以通信的数据可以被Charles拦截并解密。由于Charles更改了证书,浏览器校验不通过会给出安全警告,必须安装Charles的证书后才能进行正常访问。
下面来看具体的流程:
  1.     客户端向服务器发起HTTPS请求
  2.     Charles拦截客户端的请求,伪装成客户端向服务器进行请求
  3.     服务器向“客户端”(实际上是Charles)返回服务器的CA证书
  4.     Charles拦截服务器的响应,获取服务器证书公钥,然后自己制作一张证书,将服务器证书替换后发送给客户端。(这一步,Charles拿到了服务器证书的公钥)
  5.     客户端接收到“服务器”(实际上是Charles)的证书后,生成一个对称密钥,用Charles的公钥加密,发送给“服务器”(Charles)
  6.     Charles拦截客户端的响应,用自己的私钥解密对称密钥,然后用服务器证书公钥加密,发送给服务器。(这一步,Charles拿到了对称密钥)
  7.     服务器用自己的私钥解密对称密钥,向“客户端”(Charles)发送响应
  8.     Charles拦截服务器的响应,替换成自己的证书后发送给客户端

    至此,连接建立,Charles拿到了 服务器证书的公钥 和 客户端与服务器协商的对称密钥,之后就可以解密或者修改加密的报文了。

HTTPS抓包的原理还是挺简单的,简单来说,就是Charles作为“中间人代理”,拿到了 服务器证书公钥 和 HTTPS连接的对称密钥,前提是客户端选择信任并安装Charles的CA证书,否则客户端就会“报警”并中止连接。这样看来,HTTPS还是很安全的。

 

问题[※]:应用之间如何唤起,如何把scheme加入白名单?

URLScheme的作用
我们都知道苹果手机中的APP都有一个沙盒,APP就是一个信息孤岛,相互是不可以进行通信的。但是iOS的APP可以注册自己的URL Scheme,URL Scheme是为方便app之间互相调用而设计的。我们可以通过系统的OpenURL来打开该app,并可以传递一些参数。
 URL Scheme必须能唯一标识一个APP,如果你设置的URL Scheme与别的APP的URL Scheme冲突时,你的APP不一定会被启动起来。因为当你的APP在安装的时候,系统里面已经注册了你的URL Scheme。一般情况下,是会调用先安装的app。但是iOS的系统app的URL Scheme肯定是最高的。所以我们定义URL Scheme的时候,尽量避开系统app已经定义过的URL Scheme。
URL Schemes 有两个单词:
    URL,我们都很清楚,http://www.apple.com 就是个 URL,我们也叫它链接或网址;
    Schemes,表示的是一个 URL 中的一个位置——最初始的位置,即 ://之前的那段字符。比如 http://www.apple.com 这个网址的 Schemes 是 http。

注册URLScheme
1.在info.plist里添加URL types
每一个项目里面都会有一个info.plist配置文件。找到info.plist,右键选择Add Row,然后选择URL types。如图所示:


2.添加URL Schemes
添加完URL types,点击展开。右键选择Add Row,添加URL Schemes:

3.设置URL Schemes
设置URL Schemes为iOSDevTip


4.设置URL Identifier
URL Identifier是自定义的 URL scheme 的名字,一般采用反转域名的方法保证该名字的唯一性,比如 com.iOSStrongDemo.www
在Info->URL Types下编辑


添加成功启动提示
为了方便测试,我们在AppDelegate里面添加一个UIAlertView,当app被成功打开时,会提出提示:

    - (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL*)url
    {
        // 接受传过来的参数
        NSString *text = [[url host] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"打开啦"
                                               message:text
                                              delegate:nil
                                     cancelButtonTitle:@"OK"
                                     otherButtonTitles:nil];
        [alertView show];
        return YES;
    }

Safari启动自定义的URLSchemes APP

既然已经配置好URL Schemes,那么我们可以来款速测试一下,我们设置的URL Schemes是否有效。打开Safari(有点浏览器打不开,应该是浏览器内部给屏蔽了吧),在地址栏里输入:iOSDevTip://

成功进入:


打开注册iOSDevTip的APP格式为: URL Scheme://URL identifier,直接调用URL Scheme也可打开程序, URL identifier是可选的。
URL传参格式

     // 被启动的APP处理传过来的参数
     - (BOOL)application:(UIApplication *)application
            openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
    {
        NSLog(@"sourceApplication: %@", sourceApplication);
        NSLog(@"URL scheme:%@", [url scheme]);
        NSLog(@"URL query: %@", [url query]);
     
        // 接受传过来的参数
        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"打开啦"
                                                            message:[url query]
                                                           delegate:nil
                                                  cancelButtonTitle:@"OK"
                                                  otherButtonTitles:nil];
        [alertView show];
     
        return YES;
    }

当APP被启动是,会调用代理方法application:openURL:sourceApplication:annotation:。参数URL就是启动APP的URL,参数sourceApplication就是来源APP的Bundle ID。
我们依然通过Safari来测试,在Safari的地址栏中输入:iOSDevTip://?name=ligang&phone=13888888888

即可打开APP,看看参数是否传递过来:

URLSchemes白名单配置
iOS9系统策略更新,限制了http协议的访问,此外应用需要在“Info.plist”中将要使用的URL Schemes列为白名单,才可正常检查其他应用是否安装。
受此影响,当你的应用在iOS 9中需要使用 QQ/QQ空间/支付宝/微信SDK的相关能力(分享、收藏、支付、登录等)时,需要在“Info.plist”里增加如下代码:

问题[※]:怎么判断某个cell是否显示在屏幕上?

    1.- (NSArray*)visibleCells;
    //UITableView的方法,这个最直接,返回一个UITableviewcell的数组。
    对于自定制的cell,之后的处理可能稍微繁琐些。
    2.- (NSArray*)indexPathsForVisibleRows;
    //UITableview的又一个方法,这个比较好用了,返回一个NSIndexPath的数组,可以直接用indexpath.row去调你的table_related_Array里的数据了。比较方便用于自定制的cell。

    3.- (CGRect)rectForRowAtIndexPath:(NSIndexPath*)indexPath;
      CGRect cellR = [myTV rectForRowAtIndexPath:indx];
      if (myTV.contentOffset.y - cellR.origin.y < myCell.frame.size.height || cellR.origin.y - myTV.contentOffset.y         >myTV.size.height) {
      //这个时候myCell应该是不在myTV的可视区域了。
      } else {

     //myCell在可视区域时,业务处理
    }
    //这个方法可以用在代理回调较多的设计中。
Tip: 1和2在自动根据数据伸长的cell中好像不太准确。

 

问题[※]:谈谈你对 Objective-C 的动态绑定理解?

1. 在objective-c中,一个对象内否调用指定的方法不是由编译器决定而是由运行时决定,这被称作是方法的动态绑定。

2. 在objective-c里,对象不调用方法,而是接收消息,消息 表达式为: [reciver message];运行时系统首先确定接收者的类型(动态类型识别),然 后根据消息名在类的方法列表里选择相依的方法执行,所以在源代码里消息也称为选择器(selector)

3. 消息函数的作用:

– 首先通过第一个参数的receiver,找到它的isa 指针,然后在isa 指向的Class 对象中使用第二个参数selector 查找方法;

– 如果没有找到,就使用当前Class 对象中的新的isa 指针到上一级的父类的Class 对象中查找;

– 当找到方法后,再依据receiver 的中的self 指针找到当前的对象,调用当前对象的具体实现的方法(IMP),然后传递参数,调用实现方法。

– 假如一直找到NSObject 的Class 对象,也没有找到你调用的方法,就会报告不能识别发送消息的错误。

4. Objetive-C中的Method结构

struct objc_method{

SEL method_name;//方法名

char *method_types; //方法地址

IMP method_imp; //方法地址(IMP)

};

typedef objc_method Method;

5. 什么是IMP

– IMP是”implementation”的缩写,它是objetive-C 方法 (method)实现代码块的地址,类似函数指针,通过它可以 直接访问任意一个方法。免去发送消息的代价。

6. 获取方法的IMP

– -(IMP)methodForSelector:(SEL)aSelector;

SEL print_sel =NSSelectorFromString(@“print:”);//获得SEL

IMP imp=[person methodForSelector:print_sel];//得到IMP

imp(person,print_sel,@“*********”);//通过IMP直接调用方法 等效调用:[person print_sel:@“*********”];

– imp的第一参数是对象自己(self),第二参数是方法标示, 第三个是方法的参数

四、动态加载:运行时加载新类

在运行时创建一个新类,只需要3步:

1、为 class pair分配存储空间 ,使用 objc_allocateClassPair函数

2、增加需要的方法使用class_addMethod函数,增加实 例变量用class_addIvar

3 、用objc_registerClassPair函数注册这个类,以便它能被别人使用。

注意:使用这些函数请引#import <objc/runtime.h>

 

问题[※]:ARC是一种编译时特性还是运行时特性

ARC 是编译器特性,而不是 iOS 运行时特性

 

问题[※]:能否向编译后得到的类中增加实例变量?能否向运行时创建的类中添加实例变量?为什么?

不能向编译后得到的类中增加实例变量; 能向运行时创建的类中添加实例变量;
原因如下:因为编译后的类已经注册在 runtime 中,类结构体中的 objc_ivar_list 实例变量的链表 和 instance_size 实例变量的内存大小已经确定,同时runtime 会调用 class_setIvarLayout 或 class_setWeakIvarLayout 来处理 strong weak 引用。所以不能向存在的类中添加实例变量;
运行时创建的类是可以添加实例变量,调用 class_addIvar 函数。但是得在调用 objc_allocateClassPair 之后,objc_registerClassPair 之前,原因同上。
 

问题[※]:runtime的应用

1. AOP 面向切面编程
来看一下 百度百科 上对「AOP」的解释:
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
画重点,对业务逻辑进行分离,降低耦合度。假设现在有这样一个需求,我们要对应用中所有按钮的点击事件进行上报,统计每个按钮被点击的次数。首先我们要明确,统计功能应该与业务无关,即统计代码不应该与业务代码耦合在一起。因此用上面「AOP」的思想来实现是合适的,而 Runtime 给我们提供了这样一条途径。因为当按钮点击时,会调用 sendAction:to:forEvent: 方法,所以我们可以使用 Method Swizzling 来修改该方法,在其中添加上报的逻辑。来看代码:
// UIButton+Swizzling.m
+ (void)load {

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        RSSwizzleInstanceMethod([self class],
                                @selector(sendAction:to:forEvent:),
                                RSSWReturnType(void),
                                RSSWArguments(SEL action, id target, UIEvent *event),
                                RSSWReplacement({

            NSString *name = NSStringFromClass([self class]);

            NSLog(@"UIButton+Swizzling:%@ 按钮被点击--上报", name);

            RSSWCallOriginal(action, target, event);

        }), RSSwizzleModeAlways, NULL);

    });
}

 

注意:尽管上面的需求也可以用继承一个基类的方式来实现,但是如果此时已经有很多类继承自 UIButton ,则修改起来会很麻烦,其次我们也不能保证后续的所有按钮都继承这个基类。另外上面提到,统计逻辑不应该和业务逻辑耦合,如果为了统计的需求去修改业务代码,也是不可取的(除非迫不得已)。因此上面利用 Method Swizzling 的方式更为合适,也更为简洁。

2. 字典转模型

我们可以用 KVC 来实现字典转模型,方法是调用 setValuesForKeysWithDictionary: 。但这种方法要求 Model 的属性和 NSDictionarykey 一一对应,否则就会报错。这里可以用 Runtime 配合 KVC ,来实现更灵活的字典转模型。下面为 NSObject 添加一个分类,添加一个初始化方法,来看代码:

3. 进行归解档

「归档」是将对象序列化存入沙盒文件的过程,会调用 encodeWithCoder: 来序列化。「解档」是将沙盒文件中的数据反序列化读入内存的过程,会调用 initWithCoder: 来反序列化。通常来说,归解档需要对实例对象的各个属性依次进行归档和解档,十分繁琐且易出错。这里我们参照「字典转模型」的例子,通过获取类的所有属性,实现自动归解档。触发对象归档可以调用 NSKeyedArchiver+ archiveRootObject:toFile: 方法;触发对象解档可以调用 NSKeyedUnarchiver+ unarchiveObjectWithFile: 方法。

4. 逆向开发

在「逆向开发」中,会用到一个叫 class-dump 的工具。这个工具可以将已脱壳的 APP 的所有类的头文件导出,为分析 APP 做准备。这里也是利用 Runtime 的特性,将存储在mach-O文件中的 @interface@protocol 信息提取出来,并生成对应的 .h 文件。

5. 热修复

「热修复」是一种不需要发布新版本,通过动态下发修复文件来修复 Bug 的方式。比如 JSPatch,就是利用 Runtime 强大的动态能力,对出问题的代码段进行替换。


问题[※]:热修复,JSPatch原理

JSPatch 能做到通过 JS 调用和改写 OC 方法最根本的原因是 Objective-C 是动态语言,OC上所有方法的调用/类的生成都通过 Objective-C Runtime 在运行时进行,我们可以通过类名/方法名反射得到相应的类和方法,论上你可以在运行时通过类名/方法名调用到任何 OC 方法,替换任何类的实现以及新增任意类。

所以 JSPatch 的基本原理就是:JS 传递字符串给 OC,OC 通过 Runtime 接口调用和替换 OC 方法。这是最基础的原理,实际实现过程还有很多怪要打,接下来看看具体是怎样实现的。

 

转载于:https://www.cnblogs.com/1-434/p/10497004.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值