iOS程序员面试分类模拟30_第1页
iOS程序员面试分类模拟30_第2页
iOS程序员面试分类模拟30_第3页
iOS程序员面试分类模拟30_第4页
iOS程序员面试分类模拟30_第5页
已阅读5页,还剩16页未读 继续免费阅读

付费下载

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

iOS程序员面试分类模拟30简答题1.

虚拟地址、逻辑地址、线性地址、物理地址有什么区别?正确答案:虚拟地址指由程序产生的由段选择符和段内偏移地址组成的地址。这两部分组成的地址并不能直接访问物理内(江南博哥)存,而是要通过分段地址的变换处理后才会对应到相应的物理内存地址。

逻辑地址指由程序产生的段内偏移地址。有时直接把逻辑地址当成虚拟地址,两者并没有明确的界限。

线性地址指虚拟地址到物理地址变换之间的中间层,是处理器可寻址的内存空间(称为线性地址空间)中的地址。程序代码会产生逻辑地址,或者说是段中的偏移地址,加上相应段基址就生成了一个线性地址。如果启用了分页机制,那么线性地址可以经过变换产生物理地址。若是没有采用分页机制,那么线性地址就是物理地址。物理地址指现在CPU外部地址总线上的寻址物理内存的地址信号,是地址变换的最终结果。

虚拟地址到物理地址的转化方法是与体系结构相关的,一般有分段与分页两种方式。以x86CPU为例,分段、分页都是支持的。内存管理单元负责从虚拟地址到物理地址的转化。逻辑地址是段标识+段内偏移量的形式,MMU通过查询段表,可以把逻辑地址转化为线性地址。如果CPU没有开启分页功能,那么线性地址就是物理地址;如果CPU开启了分页功能,MMU还需要查询页表来将线性地址转化为物理地址:逻辑地址(段表)→线性地址(页表)→物理地址。

映射是一种多对一的关系,即不同的逻辑地址可以映射到同一个线性地址上;不同的线性地址也可以映射到同一个物理地址上。而且,同一个线性地址在发生换页以后,也可能被重新装载到另外一个物理地址上,所以这种多对一的映射关系也会随时间发生变化。

2.

Foundation对象与CoreFoundation对象有什么区别?正确答案:Foundation对象是Objective-C对象,使用Objective-C语言实现;而CoreFoundation对象是C对象,使用C语言实现。两者之间可以通过__bridge、__bridge_transfer、__bridge_retained等关键字转换(桥接)。

Foundation对象和CoreFoundation对象更重要的区别是ARC下的内存管理问题。在非ARC下两者都需要开发者手动管理内存,没有区别。但在ARC下,系统只会自动管理Foundation对象的释放,而不支持对CoreFoundation对象的管理。因此,在ARC下两者进行转换后,必须要确定转换后的对象是由开发者手动管理,还是由ARC系统继续管理,否则可能导致内存泄漏问题。

下面以NSString对象(Foundation对象)和CFStringRef对象(CoreFoundation对象)为例,介绍两者的转换和内存管理权移交问题。

1)在非ARC下,NSString对象和CFStringRef对象可以直接进行强制转换,都是手动管理内存,无须关心内存管理权的移交问题。

2)在ARC下,NSString对象和CFStringRef对象在相互转换时,需要选择使用__bridge、__bridge_transfer和__bridge_retained来确定对象的管理权转移问题,三者的作用语义分别如下:

①__bridge关键词最常用,它的含义是不改变对象的管理权所有者,本来由ARC管理的Foundation对象,转换成CoreFoundation对象后依然由ARC管理;本来由开发者手动管理的CoreFoundation对象转换成Foundation对象后继续由开发者手动管理。示例代码如下:

/*ARC管理的Foundation对象*/

NSString*s1=@"string";

/*转换后依然由ARC管理释放*/

CFStringRefcfstring=(__bridgeCFStringRef)s1;

/*开发者手动管理的CoreFoundation对象*/

CFStringRefs2=CFStringCreateWithCString(NULL,"string",kCFStringEncodingASCII);

/*转换后仍然需要开发者手动管理释放*/

NSString*fstring=(__bridgeNSString*)s2;

②__bfidge_transfer用在将CoreFoundation对象转换成Foundation对象时,用于进行内存管理权的移交,即本来需由开发者手动管理释放的CoreFoundation对象在转换成Foundation对象后,交由ARC来管理对象的释放,开发者不用再关心对象的释放问题,因为不会发生内存泄漏。示例代码如下:

/*开发者手动管理的CoreFoundation对象*/

CFStringRefs2=CFStringCreateWithCString(NULL,"string",kCFStringEncodingASCII);

/*转换后改由ARC管理对象的释放,不用担心内存泄漏*/

NSString*fstring=(__bridge__transferNSString*)s2;

//NSString*fstring=(NSString*)CFBridgingRelease(s2);

//另一种等效写法

③__bridge_retained用在将Foundation对象转换成CoreFoundation对象时,进行ARC内存管理权的剥夺,即本来由ARC管理的Foundation对象在转换成CoreFoundation对象后,ARC不再继续管理该对象,需要开发者自己进行手动释放该对象,否则会发生内存泄漏。示例代码如下:

/*ARC管理的Foundation对象*/

NSString*s1=@"string";

/*转换后ARC不再继续管理,需要手动释放*/

CFStringRefcfstring=(__bridge__retainedCFStringRef)s1;

//CFStringRefcfstring=(CFStringRef)CFBridgingRetain(s1);

//另一种等效写法

3.

delete与truncate命令有哪些区别?正确答案:相同点:都可以用来删除一个表中的数据。

不同点:

1)truncate是一个数据定义语言(DataDefinitionLanguage,DDL),它会被隐式地提交,一旦执行后将不能回滚。delete执行的过程是每次从表中删除一行数据,同时将删除的操作以日志的形式进行保存,以便将来进行回滚操作。

2)用delete操作后,被删除的数据占用的存储空间还在,还可以恢复。而用truncate操作删除数据,被删除的数据会立即释放所占有的存储空间,被删除的数据是不能被恢复的。

3)truncate的执行速度比delete快。

4.

Nil、nil、NULL、NSNull有什么区别?正确答案:iOS中表示“空”的几种关键词的用法分别如下:NULL比较常用,一般指的是指向基本数据类型以及C类型的空指针,例如:int*var=NULL;nil也很常用,指的是指向Objective-C对象的空指针,例如:NSObject*obj=nil;Nil专门用于对Objective-C中的类赋空值,例如:Classclass=Nil;NSNull用于作为数组等集合类中的空元素,例如:NSArray*array=[NSArrayarrayWithObjects:[[NSObjectalloc]init],[NSNullnull],[[NSObjectalloc]init],[[NSObjectalloc]init],nil};

5.

Objective-C等同性中的字符串相等如何判断?正确答案:有如下代码:

NSString*firstUserName=@"nick";

NSString*secondUserName=@"nick";

if(firstUserName=secondUserName){

NSLog(@"areEqual");

}

else{

NSLog(@"areNotEqual");

}

上述程序会输出“areEqual”。

看上去好像答案很明显,其实不然。用“==”比较两个指针相当于检查它们是否指向同一个对象(指针存储的是对象的地址)。两个指针只有指向同一个对象(两个指针存储相同的对象地址)时这两个指针才相等。两个指针指向的两个对象的值相等,但不是同一个对象,那么这两个指针还是不相等,所以这么分析上面的代码应该输出“areNotEqual”。

在上面的代码片中,firstUserName和secondUserName是指向两个不同对象的指针,但为什么会输出“areEqual”呢?是因为iOS编译器对string对象的引用做了优化,也就是对字符串相同的对象做了复用而不是重新分配空间,所以上面代码虽然是分别定义的两个指针,但实际还是指向了同一个字符串对象,结果输出“areEqual”。

6.

什么是消息推送?和NotificatiOil有什么区别?正确答案:消息推送指在App关闭时(不在前台运行时),仍然向用户发送App的内部消息。消息推送通知和Objective-C中的Notification通知机制不同,推送的消息是给用户看的,也就是可见的,而通知机制是Objective-C语言中对象间通信的一种机制,基于观察者模式,目的是触发内部事件,减小类之间的耦合度,对用户是不可见的。

推送消息的可见形式主要有以下几种:

1)锁屏界面的横幅推送消息。

2)顶部通知栏的横幅推送消息。

3)应用图标上的代表消息数量的红色数字。

4)菜单页面弹出框提示。

5)播放声音提示。

下图所示是iPhone中QQ推送的设置界面。

QQ推送的设置界面

下图所示为iPhone中消息推送的形式。

iPhone中消息推送的形式

iOS开发中有两种类型的消息推送:本地消息推送(LocalNotification)和远程消息推送(RemoteNotification)。

1)本地消息推送:本地消息推送很简单,不需要联网,不需要服务器,由客户端应用直接发出推送消息,一般通过定时器在指定的时间进行消息推送。

2)远程消息推送:远程消息推送过程略为复杂,需要客户端从苹果公司的APNS(ApplePushNotificationSerwices)服务器注册获得当前用户的设备令牌并发送给应用的服务器,然后应用的服务器才可以通过APNS服务器间接地向客户端发送推送消息,期间难免会有延迟。

远程消息推送的具体流程如图所示,开发中要和服务器合作共同完成。

远程消息推送流程图

①App客户端向APNS服务器发送设备的UDID和BundleIdentifier。

②APNS服务器对传过来的信息加密生成一个deviceToken,并返回给客户端。

③客户端将当前用户的deviceToken发送给自己应用的服务器。

④自己应用的服务器将得到的deviceToken保存,需要的时候利用deviceToken向APNS服务器发送推送消息。

⑤APNS服务器接收到自己应用的服务器的推送消息时,验证传过来的deviceToken,如果一致,那么将消息推送到客户端。

7.

iOS中点与像素有什么关系?正确答案:1)点是iOS中标准的坐标体系。它就是iOS中的虚拟像素,也被称为逻辑像素。在标准设备中,一个点就是一个像素,但是在Ratina屏幕上,一个点等于2×2个像素。iOS用点作为屏幕的坐标测算体系就是为了在Retina设备和普通设备上能有一致的视觉效果。

2)像素是图片分辨率的尺寸单位。物理像素坐标并不会用于屏幕布局,但是仍然和图片有相对关系。UIImage是一个屏幕分辨率解决方法,它是用点来度量大小的。但是,一些底层的CGImage类型的图片会使用像素,所以必须清楚在Retina设备和普通设备上,点和像素代表着不同的大小。

8.

objective-C的优缺点有哪些?正确答案:Objective-C的优缺点见表。Objective-C的优缺点优点缺点1)Objective-C是C语言的超集,在C语言的基础上衍生了很多新的语言特性,封装得很完善而且方便使用,大大降低了编程复杂度,因此开发中使用起来会感觉方便高效1)不支持命名空间(都是通过加一些像NS或者UI这样的命名前缀来达到用命名空间防止命名冲突的作用,但这样会使变量的命名更长)2)Category(类别)的使用,可以快速扩展类的方法,同时使扩展的功能模块之间互不影响2)不支持运算符重载3)Posing(扮演)特性,[ParentClassposeAs:[ChildrenClassclass]];该语言特性使得父类无须定义和初始化子类对象,即可通过父类扮演子类进行操作3)不支持多重继承(C++语言通过virtual关键字防止二义性的出现,实现多重继承)4)动态语言特性,动态类型、动态绑定和动态加载等,将类型确定、方法调用和资源加载等任务推迟到运行时,大大提高了编程灵活度4)使用动态运行时类型,所有的方法都是通过消息传递机制方法调用,有其动态的优势,同时也使很多编译时的优化方法无法使用降低了性能,例如:内联方法等5)指针:Objective-C保留了C语言强大的指针特性

6)Objective-C与C/C++可在.mm文件中进行混合编程,灵活度更高

9.

load和initialize的区别是什么?正确答案:相同点:load和initialize都是为了给应用运行前提供运行环境,并且每个类中不管是父类还是子类中最多只会调用一次。

不同点:load方法在某个类文件被引用时调用,子类会自动调用父类的load方法;initialize是在类或者其子类的第一个方法被调用前调用。所以,如果类没有被引用进项目,那么就不会有load调用,即使类文件被引用进来,但是没有使用,那么initialize也不会被调用。

10.

Objective-C中的属性和实例变量有哪些区别?正确答案:从下面的代码中可以很明显地看出它们的区别:

@interfaceTest:NSObject{

/*实例变量*/

@private

NSString*major;

@public

intage;

}

/*属性变量*/

@property(nonatomic,copy)NSString*name;

@end

Objective-C中的属性主要是对传统实例变量的封装,类对象有一个属性列表用来存放类的所有属性。属性和实例变量的区别主要有以下几个方面的内容:

1)实例变量的存放采用硬编码,编译后写死,根据距起始地址的偏移量来访问变量,不可再插入新变量,而属性可以在运行时动态添加删除。

2)实例变量可以通过@prirate、@public和@protected等修饰词来定义变量的作用域,限制变量的访问权限,而属性不可以。从设计角度,属性主要是用来和外部类进行访问交互的,实例变量主要用于类内部使用。

3)属性可以通过三类属性特质分别来帮助内存管理、多线程管理和读写控制,可以让编译器自动合成存取方法,而不用重复为每一个实例变量手写存取方法造成代码臃肿。

11.

tintColor的作用是什么?正确答案:tintColor是iOS7以后在UIView类中新增的一个属性。tintColot具有传递性,一般用来改变应用的主色调,默认为nil,表示用父视图默认的颜色进行着色。

12.

id,NSObject,id<NSObject>的区别是什么?正确答案:NSObject修饰的对象肯定是NSObject对象,但id修饰的对象虽然肯定是Objective-C对象,但不一定是NSObject对象,id类型是一种运行时的动态类型,编译器不会对其进行类型检查。id<NSObject>修饰的对象也是一种动态类型对象,对象的类型在运行时才确定,但编译器知道它一定是遵守NSObject协议的。例如:id<NSObject>常用来定义delegate对象。

idfool;

//是Objective-C对象,但不是NSObject对象

NSObject*foo2;

//NSObject对象

id<NSObject>foo3;

//不是NSObject对象,但是遵守NSObject协议

13.

union和unionall有什么区别?正确答案:union在进行表求并集后会去掉重复的元素,所以会对所产生的结果集进行排序运算,删除重复的记录再返回结果。

而unionall只是简单地将两个结果合并后就返回。因此,如果返回的两个结果集中有重复的数据,那么返回的结果集就会包含重复的数据了。

从上面的对比可以看出,在执行查询操作的时候,unionall要比union快很多。所以,如果可以确认合并的两个结果集中不包含重复的数据,那么最好使用unionall。例如,有两个学生表table1和table2。Table1C1C2112233Table2C1C2334411select*fromTable1unionselect*fromTable2的查询结果为:C1C211223344select*fromTablelunionallselect*fromTable2的查询结果为:C1C2112233334411

14.

如何解决masksToBounds离屏渲染带来的性能损耗?正确答案:在开发中常通过CALayer的cornerRadius属性来设置图层的圆角曲率(如设置圆角图片)。默认情况下,cornerRadius只影响背景颜色而不影响背景图片或子图层。但当将CALayer的masksToBounds设置为YES时,图层的内容就会被截取。代码如下:

imageView.layer.cornerRadius=10

imageView.layer.masksToBounds=YES

由于这样处理的渲染机制是GPU在当前屏幕缓冲区外新开辟的一个渲染缓冲区进行工作,也就是所谓的离屏渲染,所以这会给应用程序带来额外的性能损耗。如果在某一时刻大量地使用这种方式设置圆角,那么就会触发缓冲区频繁合并和上下文之间的频繁切换,这时应用程序就有可能出现掉帧和卡顿。具体可以使用Xcode自带的Instruments(工具)进行检测。

为了防止因离屏渲染而产生性能损耗,可以不使用CALayer的cornerRadius和masksToBounds属性,而将处理图片的权利交于CPU,虽然CPU对图形的处理能力不及GPU,但设置圆角图片的处理难度并不大,且代价远小于上下文切换。以下是实际开发中常用的两种优化方案:

1)使用CALayer提供的shouldRasterize属性。shouldRasterize属性是设置光栅化,可以使离屏渲染的结果缓存到内存中存为位图,当下次使用的时候就可以直接使用内存缓存,这样就节省了一直离屏渲染的性能损耗。为了使用shouldRasterize属性,还需要设置rasterizationScale属性去匹配屏幕,以防止出现Retina屏幕像素化的问题。

imageView.layer.shouldRasterize=YES;

imageView.layer.msterizationScale=[UIScreenmainScreen].scale;

这种方法虽然能在一定程度上优化性能,但是如果layer及sublayers经常改变,那么它就会不停地渲染及设置缓存,在这种情况下还是很耗性能的。

2)通过CoreGraphics绘制出圆角图片。可以使用UIKit中对CoreGraphics有一定封装的应用层类UIBezierPath,对图片进行重新剪切。示例代码如下:

UIImageView*imageView=[[UIImageViewalloc]initWithFrame:CGRectMake(100,100,100,100)];

imageView.image=[UIImageimageNamed:@"1"];

/*开启上下文

开始对imageView进行画图*/

UIGraphicsBeginImageContextWithOptions(imageView.bounds.size,NO,1.0);

/*使用贝塞尔曲线画出一个圆形图*/

[[UIBezierPathbezierPathWithRoundedRect:imageView.bounds

cornerRadius:imageView.frame.size.width]addClip];

[imageViewdrawRect:imageView.bounds];

imageView.image=UIGraphicsGetImageFromCurrentImageContext();

/*关闭上下文结束画图*/

UIGraphicsEndImageContext();

[self.viewaddSubview:imageView];

15.

什么是Notification?什么时候用Delegate或Notification?正确答案:Notification(通知)是Cocoa框架中基于观察者模式实现的用于一对多传播消息的一种机制。项目中的对象将它们自己或者其他对象添加到通知的观察者列表里(这个过程又叫通知注册),其中项目中的所有通知都有一个唯一的字符串标识作为通知名唯一确定每个通知,通知源也就是被观察者可以创建通知对象并发送到通知中心,通知中心找出所有注册该通知的对象(观察者),并将从被观察者那里收到通知以消息的方式发送给所有的观察者们。被观察者发送通知是一个同步过程,即发送者在通知中心成功将该发送者之前的消息发送给所有观察者之前不可以再次发送通知。另外,通知触发的代理方法都必须符合某个单一参数签名约定,代理方法的参数是一个通知对象,参数里包含着通知名、被观察者和一个包含其他额外信息的字典。

Delegate和Notification的主要区别在于前者是一对一的消息传递,而后者是一对多的,可以根据这个特点在使用中进行选择。另外在代理模式中,reciever(接收者)可以返回值给sender(发送者),实现一种回调,而观察者模式中观察者不可以返回值给被观察者,因此在需要实现回调时只能选择代理模式。

16.

CocoaTouch的底层技术架构是什么?正确答案:如图所示,CocoaTouch的底层技术架构主要分4层,每层负责的服务见下表。

CocoaTouch架构图CocoaTouch的4层架构及其服务架构层服务CocoaTouch框架层UI组件、触摸处理和事件驱动、系统接口Media媒体层音频视频播放、动画、2D和3D图形CoreService核心服务层、底层特性、文件、网络、位置服务等CoreOS系统层内存管理、底层网络、硬件管理

17.

Objective-C中数据类型的限定词有哪些?正确答案:在Objective-C中,有如下几个限定词。

1.long

如果直接将限定词long放到int声明之前,那么其所声明的整型变量在某些计算上将具有拓展的值域,也就是说该整型变量的储值范围将变大。一个longint声明的例子为:

longintprice;

longint类型的常量值可通过在整型常量末尾添加字母L(不区分大小写)来完成。单数字和L之间不允许使用空格。一个longint类型常量的例子为:

longintprice=123456789L;

另外,要用NSLog打印longint的值,使用字母1作为修饰并放在整型格式符号i、o和x之前。例如“%lx”表示十六进制格式显示值。

同理,也可以将long限定词放到double声明之前,如:

longdoublelongDouble;

longdouble常量可写成其尾部带有字母l或L的浮点常量,例如:1.234e+7L。要显示longdouble的值,需要使用修饰符L。因此,%Lf用浮点计数法显示longdouble的值,%Le用科学计数法显示同样的值,而%Lg将告诉NSLog在%Lf和%Le之间任选一个使用。

2.longlong

结合上面所描述的,longlong限定词同样具有拓展存储数据值的范围和精度的作用。可以用如下形式使用:

longlongintmaxIntStorage;

NSLog字符串不使用单个字母l,而使用两个l来显示longlong的整数,例如“%lli”。

3.short

使用short限定词修饰的变量主要用来存储比较小的整数。主要的好处是在应用开发中能够节约内存空间,在一般情况下,shortint占用的内存空间是常规int变量所占空间的一半。

4.unsigned

顾名思义,unsigned这个限定词当放在int变量之前,就是向编译器声明所修饰的整数变量只用于存储正整数。通过限定了整型变量的使用,让它专门用于存储正整数,可以扩展整型变量的范围。一般unsignedint可简写为uint。例如:

uintunsignedIntNumber;

其等价于:

unsignedintunsignedIntNumber;

18.

如何使用CAShapeLayer绘制图层?正确答案:CAShapeLayer是一个通过矢量图形来进行绘制的图层子类。开发者通过指定诸如颜色和线宽等属性,用CGPath来定义指定形状的图形,最后CAShapeLayer对象就会自动渲染出来了。与直接使用CoreGraphics在原始的CALayer对象中绘制的方式相比,使用CAShapeLayer有以下优点:

1)渲染速度更快。CAShapeLayer使用了硬件加速的方式绘制图形,绘制速度比用CoreGraphics快很多。

2)内存使用更加高效。普通的CALayer对象需要创建一个寄宿图,而CAShapeLayer不需要,这样就节约了内存。

3)不会被边界图层裁剪掉。一个CAShapeLayer可以在图层边界绘制。CAShapeLayer的图层路径不会像在使用CoreGraphics的普通CALayer一样被裁剪掉。

通常通过指定CAShapeLayer对象的path属性来绘制图层。代码如下:

@property(nullable)CGPathRefpath;

此外还可以通过一些属性来描绘形状的线条,例如lineWidth属性用于设置线宽,lineCap属性用于描绘线条结尾的样子,但是在图层层面只有一次机会设置这些属性。如果想用不同颜色或风格来绘制多个形状,那么就不得不为每个形状准备一个图层。下面示例使用CAShapeLayer渲染一个简单的多边形,由于CAShapeLayer对象的path属性是CGPathRef类型,所以很容易使用UIBezierPath类帮助创建图层路径,这样就不需要人工释放CGPath了。代码如下:

/*创建一个CAShapeLayer对象*/

CAShapeLayer*shaperLayer=[CAShapeLayerlayer];

/*设置线宽*/

shaperLayer.lineWidth=2;

shaperLayer.lineCap=kCALineCapRound;

shaperLayer.lineJoin=kCALineJoinRound;

/*设置描边颜色*/

shaperLayer.strokeColor=[UIColorredColor].CGColor;

/*设置填充颜色*/

shaperLayer.fillColor=[UIColorwhiteColor].CGColor;

/*创建一个UIBezierPath对象*/

UIBezierPath*bezierPath=[UIBezierPathbezierPath];

[bezierPathmoveToPoint:CGPointMake(160,100)];

[bezierPathaddLineToPoint:CGPointMake(100,160)];

[bezierPathaddLineToPoint:CGPointMake(100,220)];

[bezierPathaddLineToPoint:CGPointMake(160,280)];

[bezierPathaddLineToPoint:CGPointMake(220,220);

[bezierPathaddLineToPoint:CGPointMake(220;160)];

[bezierPathclosePath];

/*绘制*/

shaperLayer.path=bezierPath.CGPath;

[self.view.layeraddSublayer:shaperLayer];

代码执行效果如图所示。

CAShapeLayer绘制的路径图

19.

MethodSwizzling的应用场景有哪些?正确答案:MethodSwizzling主要用于在运行时对编译器编译好的方法再次进行编辑,应用场景主要有以下4种。

1.替换类中某两个类方法或实例方法的实现

替换函数:method_exchangeImplementations(method1,method2)。

这里随便定义一个Test类,类中定义两个实例方法和类方法并在.m文件中实现,在运行时将两个实例方法的实现对调,以及将两个类方法的实现对调。注意运行时代码写在类的load方法内,该方法只会在该类第一次加载时调用一次,且编写运行时代码的地方需要引入运行时头文件:

#import<objc/runtime.h>

Test类定义:

/*Test.h*/

#import<Foundation/Foundation.h>

@interfaceTest:NSObject

/*定义两个公有实例方法*/

-(void)instanceMethod1;

-(void)instanceMethod2;

/*定义两个公有类方法*/

+(void)ClassMethod1;

+(void)classMethod2;

@end

/*Test.m*/

#import"Test.h"

#import<objc/runtime.11>

@implementationTest

/*实例方法的原实现*/

-(void)instanceMethod1{

NSLog(@"instanceMethod1...");

}

-(void)instanceMethod2{

NSLog(@"instanceMethod2...");

}

/*类方法的原实现*/

+(void)classMethod1{

NSLog(@"classMethod1...");

}

+(void)classMethod2{

NSLog(@"classMethod2...");

}

@end

/*runtime代码写在类第一次被调用加载的时候(load方法有且只有一次会被调用)*/

+(void)load{

/*1.获取当前类名*/

Classclass=[selfclass];

/*2.获取方法名(选择器)*/

SELselInsMethodl=@selector(instanceMethod1);

SELselInsMethod2=@selector(instanceMethod2);

SELselClassMethod1=@selector(classMethod1);

SELselClassMethod2=@selector(classMethod2);

/*3.根据方法名获取方法对象*/

MethodInsMethod1=class_getInstanceMethod(class,selInsMethod1);

MethodInsMethod2=class_getInstanceMethod(class,selInsMethod2);

MethodClassMethod1=class_getClassMethod(class,selClassMethod1);

MethodClassMethod2=class_getClassMethod(class,selClassMethod2);

/*4.交换实例方法的实现和类方法的实现*/

if(!InsMethod1||!InsMethod2){

NSLog(@"实例方法实现运行时交换失败!");

return;

}

/*交换实例方法的实现*/

method_exchangeImplementations(InsMethod1,InsMethod2);

if(!ClassMethod1||!ClassMethod2){

NSLog(@"类方法实珧运行时交换失败!");

return;

}

/*交换类方法的实现*/

method_exchangeImplementations(ClassMethod1,ClassMethod2);

}

测试代码:

#import"ViewController.h"

#import"Test.h"

@implementationViewController

-(void)viewDidLoad{

[superviewDidLoad];

/*测试类方法调用*/

[TestclassMethod1];

[TestclassMethod2];

Test*test=[[Testalloc]init];

/*测试实例方法调用*/

[testinstanceMethod1];

[testinstanceMethod2];

}

@end

通过下而的输出结果可知,两个实例方法和类方法的实现都被互换了。

2017-03-0617:47:13.684SingleView[41495:1196960]classMethod2...

2017-03-0617:47:13.684SingleView[41495:1196960]classMethod1...

2017-03-0617:47:13.685SingleView[41495:1196960]instanceMethod2...

2017-03-0617:47:13.685SingleView[41495:1196960]instanceMethod1...

2.重新设置类中某个方法的实现

设置函数:method_setImplementation(method,IMP)

理解了上面的例子,现在略微修改其中运行时代码,通过重新设置方法的实现来实现上面同样的效果。修改部分的运行时代码为:

/*获取方法的实现*/

IMPimpInsMethod1=method_getImplementation(InsMethod1);

IMPimpInsMethod2=method_getImplementation(InsMethod2);

IMPimpClassMethod1=method_getImplementation(ClassMethod1);

IMPimpClassMethod2=method_getImplementation(ClassMethod2);

/*重新设置方法的实现*/

/*重新设置instanceMethod1的实现为instanceMethod2的实现*/

method_setImplementation(InsMethod1,impInsMethod2);

/*重新设置instanceMethod2的实现为instanceMethod1的实现*/

method_setImplementation(InsMethod2,impInsMethod1):

/*重新设置classMethod1的实现为classMethod2的实现*/

method_setImplementation(ClassMethod1,impClassMethod2);

/*重新设置classMethod2的实现为classMethod1的实现*/

method_setImplementation(ClassMethod2,impClassMethod1);

运行后打印结果和上面方法实现交换的例子结果相同。

2017-03-0618:27:53.032SingleView[41879:1212691]classMethod2...

2017-03-0618:27:53.032SingleView[41879:1212691]classMethod1...

2017-03-0618:27:53.033SingleView[41879:1212691]instanceMethod2...

2017-03-0618:27:53.033SingleView[41879:1212691]instanceMethod1...

3.替换类中某个方法的实现

替换方法实现函数:class_replaceMethod(Classcls,SELname,IMPimp,constchar*types)。

这种方法只能替换实例方法的实现,而不能替换类方法的实现,修改的代码如下:

/*获取方法编码类型*/

constchar*typeInsMethod1=method_getTypeEncoding(InsMethod1);

constchar*typeInsMethod2=method_getTypeEncoding(InsMethod2);

constchar*typeClassMethod1=method_getTypeEncoding(ClassMethod1);

constchar*typeClassMethod2=method_getTypeEncoding(ClassMethod2);

/*替换InsMethod1的实现为InsMethod2的实现*/

class_replaceMethod(class,selInsMethod1,impInsMethod2,typeInsMethod2);

/*替换InsMethod2的实现为InsMethod1的实现*/

class_replaceMethod(class,selInsMethod2,impInsMethod1,typeInsMethod1);

/*尝试替换类方法的实现*/

class_replaceMethod(class,selCl

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论