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

下载本文档

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

文档简介

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

Objective-C中的id类型指的是什么?id、nil代表什么?正确答案:id表示Objective-C对象的类型在编写代码时(编译期)不确定,视为任意(江南博哥)Object类型(即指向任意继承了NSObject的类的对象或者NSObject对象的指针),直到程序运行起来时才最终确定其类型。

id类似于C/C++中的void*,但id和void*并非完全一样。id是一个指向继承了NSObject的Objective-C对象的指针,注意id是一个指针,虽然省略了*号。id和C语言的void*之间需要通过bridge关键字来显式的桥接转换。具体转换方式示例代码如下:

idnsobj=[[NSObjectalloc]init];

void*p=(__bridgevoid*)nsobj;

idnsobj=(__bridgeid)p;

Objective-C中的nil定义在objc/objc.h中,表示一个指向空的Objctive-C对象的指针。例如weak修饰的弱引用对象在指向的对象释放时会自动将指针置为nil,即空对象指针,来防止指针悬挂。

2.

如何自定义UIViewController之间的转场动画?正确答案:在实际开发中,程序界面常常需要在不同的控制器视图之间切换,常用的跳转方式有push、pop、present和dismiss等。如果使用这些方式,那么系统会提供默认的动画效果,例如当使用-pushViewController:animation:的时候会产生一个新视图从右往左推开旧视图的动画效果。但是有时候希望跳转方式能以其他的动画效果展示,这时就需要自定义转场动画。

在开始自定义转场动画之前,有必要先了解几个简单的概念。

1)fromView和toView,在很多API中常常会有fromView和toView,ffomView表示当前视图,即跳转前的视图,而toView代表跳转后的视图(或称为跳转的目标视图)。

2)presentedViewController和presentingViewController,这也是一组相对的概念,presentedViewController表示被modal出的视图控制器(或称为跳转的目标视图控制器),而presentingViewController代表源视图控制器。

3)UIViewControllerTransitioningDelegate协议用于为跳转动画提供实现了UIViewControllerAnimatedTransitioning协议的对象。

4)UIViewControllerAnimatedTransitioning协议主要用于控制动画的展示时间和动画展示逻辑。

5)UIViewControllerInteractiveTransitioning协议即交互式转场动画代理,这个协议主要用于交互式动画。

6)UIViewControllerContextTransitioning协议即转场动画上下文协议,它的作用在于为动画提供必备的信息。开发者不应该缓存任何关于动画的信息,而是应该从转场动画上下文中获取,这样可以保证总是获取到最新的、正确的信息。

要实现整个转场动画逻辑需要两组必需的元素,其一是代表页面跳转关系的两个控制器对象,其二是动画执行逻辑。以下是实现动画的步骤:

1)为源控制器和目标控制器分别设置一个遵守UIViewControllerAnimatedTransitioning协议的代理对象。当然也可以设置为同一个对象。

2)调用对应的跳转方法。此时系统会自动请求动画代理提供动画逻辑对象。

下面的例子展示了一个自定义的跳转动画效果。

/*设置transitioningDelegate为当前控制器*/

-(void)startCustomControllerTransitionAniamtion1:(UITouch*)touch{

_touchPoint=[touchlocationInView:self.view];

TouchExampleController*VC=[TouchExampleControllernew];

vc.type=3;

vc.transitioningDelegate=self;

[selfpresentViewController:vcanimated:YEScompletion:nil];

}

/*当modal时,系统会自动调用此方法返回一个遵守UIViewControllerAnimatedTransitioning协议的对象*/

-(nullableid<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController*)presentedpresentingController:(UIViewController*)presentingsourceController:(UIViewController*)source{

TransitionAnimator*animator=[TransitionAnimatornew];

/*将单击坐标传过去,以确定动画开始的点*/

animator.touchPoint=_touchPoint;

returnanimator:

}

/*在TransitionAnimator类实现中,实现UIViewControllerAnimatedTransitioning协议*/

@implementationTransitionAnimator

/*设置动画时长*/

-(NSTimeInterval)transitionDuration:(nullableid<UIViewControllerContextTransitioning>)transitionContext{

return1;

}

/*设置动画*/

-(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext{

/*从上下文中获取必需信息*/

UIView*fromV=[transitionContextViewForKey:

UITransitionContextFromViewKey];

UIView*toV=[transitionContextviewForKey:UITransitionContextToViewKey];

UIView*containerView=[transitionContextcontainerView];

/*将视图添加到containerView中*/

[containerViewaddSubview:fromV];

[containerViewaddSubview:toV];

CGFloatscreenWidth=[UIScreenmainScreen].bounds.size.width;

CGFloatscreenHeight=[UIScreenmainScreen].bounds.size.height;

CGPointtempPoint=[containerViewconvertPoint:self.touchPointfromView:[transitionContextviewForKey:UITransitionContextFromViewKey]];

CGRectrect=CGRectMake(tempPoint.x-1,tempPoint.y-1,2,2);

/*使用UIBerizerPath绘制动画的开始路径和结束路径*/

UIBezierPath*startPath=[UIBezierPathbezierPathWithOvalInRect:reet];

UIBezierPath*endPath=[UIBezierPathbezierPathWithArcCenter:containerView.centerradius:sqrt(screenHeight*screenHeight+screenWidth*screenWidth)startAngle:0endAngle:M_PI*2clockwise:YES];

/*设置maskLayer*/

CAShapeLayer*maskLayer=[CAShapeLayerlayer];

maskLayer.path=endPath.CGPath;

toV.layer.mask=maskLayer;

/*设置动画*/

CABasicAnimation*animation=[CABasicAnimationanimationWithKeyPath:@"path"];

animation.delegate=self;

animation.fromValue=(id)startPath.CGPath;

animation.toValue=(id)endPath.CGPath;

animation.duration=[selftransitionDuration:transitionContext];

[maskLayeraddAnimation:animationforKey:@"custom"];

callback=^{

toV.layer.mask=nil;

/*动画结束后一定要调用此方法完成过渡动画*/

[transitionContextcompleteTransition:![transitionContexttransitionWasCancelled]];

};

}

-(void)animationDidStop:(CAAnimation*)animfinished:(BOOL)flag{

callback();

}

动画效果是从单击坐标点开始,以圆圈的形式逐渐展开,如图所示。

转场动画

3.

什么是目标-动作机制?正确答案:目标-动作(target-action)机制是一种设计模式,用于一个对象在某个事件发生时向另一个对象发送消息。消息中要包含一个selector,用于确定要触发的方法,该方法即该机制中的动作。还要包含一个target(目标),表示消息的接收者,例如一个控件,或者更为常见的是它的单元,以插座变量的形式保有其动作消息的目标。

目标-动作机制符合软件开发中“高内聚,低耦合”的设计目标,降低模块间的耦合度,同时增强了模块内部的聚合度。目标一动作机制主要用于MVC设计模式中V到C的通信,通过该机制,充当V的组件只要在UI事件发生时通知C即可,之后的逻辑处理全交由C去进一步处理。

典型的例子是UIButton的单击事件和方法的绑定,当按钮事件发生时,会触发接收者对于该事件所绑定的方法。这里的UIButton按钮即MVC中的V组件,通过目标一动作机制绑定的selector方法即属于C中的后续逻辑处理部分。

[buttonaddTarget:selfaction:@selector(click:)forControlEvents:UIControlEventTouchUpInside];

4.

什么是键值编码KVC?键路径是什么?什么是键值观察KVC?正确答案:1.键值编码KVC

键值编码(KVC)是一种在NSKeyValueCoding非正式协议下使用字符串标志间接访问对象属性的一种机制,也就是访问对象变量的一种特殊的捷径。如果一个对象符合键值编码的约定,那么它的属性就可以通过一个准确的、唯一的字符串(键路径字符串)参数进行访问,类似于将所有对象看作Dictionary,键路径为key(实际为keypath),属性值即value,通过键路径访问属性值。键值编码的间接访问方式其实是传统实例变量的存取方法访问的一种替代,也就是另外一种可以访问对象变量的方法。其中,注意键值编码可以暴力访问对象的任何变量,无论是否是pirvate私有类型的变量。

开发者通常是通过存取方法来访问对象的属性的,getter方法返回属性的值,setter方法设置属性的值。对于实例对象,可以直接通过存取方法或者变量名来访问对象的属性,但是随着属性数量的增加和对象变量的嵌套深度增大,访问代码会随之增多。相比之下,通过键值编码就可以简洁而稳定地对所有属性进行访问。

键值编码是Cocoa框架中很基础的一个概念,像KVO、Cocoa绑定、CoreData等都是基于KVC的。

2.键路径

键路径就是键值编码中某个属性的key,一个由连续键名组成的字符串,键名即属性名,键名之间用点隔开,用于指定一个连接在一起的对象性质序列。键路径使开发者可以独立于模型实现的方式指定相关对象的性质。通过键路径,可以指定对象图中的一个任意深度的路径,使其指向相关对象的某个特定的属性。

3.键值观察KVO

键值观察(KVO),是基于键值编码实现的一种观察者机制,提供了观察某一属性变化的监听方法,用来简化代码,优化逻辑和组织。

这里提供一个最基本的KVO和KVC的使用示例。假设有一个专业类Major,Major有一个专业名(majorName)私有属性;一个学生类Student,Student有一个姓名(name)私有属性,同时还有一个专业(Major)对象变量,这样就出现了简单的Major和Student的对象嵌套。Major和Student都继承自NSObject,都符合键值编码约定,可以定义一个Student变量对其进行键值编码和键值观察。示例代码如下:

/*1.专业类模型:Major.h*/

@interfaceMajor:NSObject{

@private

NSString*majorName;//私有实例变量专业名称

}

@end

/*2.学生类模型:Student.h*/

@interfaceStudent:NSObject{

@private

NSString*name;//私有实例变量姓名

}

@property(nonatomic,strong)Major*major;//学生专业

/*3.kvc和kvo之间基本使用方法*/

#import"Student.h"

#import"Major.h"

@interfaceViewController()

@property(nonatomic,strong)Student*student;

@end

@implementationViewController

(void)viewDidLoad{

[superviewDidLoad];

/*初始化学生Student对象*/

_student=[[Studentalloc]init];

/*初始化学生的专业Major对象*/

_student.major=[[Majoralloc]init];

/*1.set:通过KVC设置Student对象的值(可以强行访问private变量)*/

[_studentsetValue:@"Sam"forKey:@"name"];

[_studentsetValue:@"ComputerScience"forKeyPath:@"major.majorName"];

/*2.get:通过KVC读取Student对象的值(可以强行访问private变量)*/

NSLog(@"%@",[_studentvalueForKey:@"name"]);

NSLog(@"%@",[_studentvalueForKeyPath:@"major.majorName"]);

/*

**3.kvo:添加当前控制器为键路径major.majorName的一个观察者,

**如果major.majorNalne的值改变,那么会通知当前控制器从而

**自动调用下面的observeValueForKeyPath方法,这里传递旧值和新值

*/

[_studentaddObserver:selfforKeyPath:@"major.majorName"options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNewcontext:nil];

/*3s后改变major.majorName的值*/

dispatch_after(dispatch_time(DISPATCH_TIMENOW,(int64_t)(3*NSEC_PER_SEC)),dispatch_get_main_queue(),^{

[_studentsetValue:@"SoftwareEngineer"forKeyPath:@"major.majorName"];

});

}

/*监听keyPath值的变化*/

-(void)observeValueForKeyPath:(NSString*)keyPathofObject:(id)objectchange:(NSDictionary<NSKeyValueChangeKey,id>*)changecontext:(void*)context{

if([keyPathisEqual:@"major.majorName"]){

/*获取变化前后的值并打印*/

NSString*oldValue=[changeobjectForKey:NSKeyVatueChangeOldKey];

NSString*newValue=[changeobjectForKey:NSKeyValueChangeNewKey];

NSLog(@"major.majorNamevaluechanged:oldValue:%@newValue:%@",oldValue,newValue);

}

else{

[superobserveValueForKeyPath:keyPathofObject:objectchange:changecontext:context];

}

}

/*移除观察者释放资源,防止资源泄漏*/

-(void)dealloc{

[_StudentremoveObserver:selfforKeyPath:@"major.majorName"];

}

@end

可以看到major.majorName就是一个由两个键连接起来的基本键路径,相当于用它可以直接访问属性的属性;使用addObserver方法将当前控制器注册为Student对象的观察者,当Student中键路径major.majorName下的值发生改变时会通知当前控制器,触发observeValueForKeyPath监听方法。

5.

objective-C是如何实现内存管理的?正确答案:Objective-C的内存管理本质上是通过引用计数实现的,每次RunLoop都会检查对象的引用计数,如果引用计数为0,那么说明该对象已经没有再被使用了,此时可以对其进行释放了。其中,引用计数可以大体分为3种:MRC、ARC和内存池。

那么引用计数是如何操作的呢?其实不论哪种引用计数方式,它们本质上都是在合适的时机将对象的引用计数加1或者减1。

所以对于引用计数可以总结如下:

1)使对象引用计数加1的常见操作有alloc、copy、retain。

2)使对象引用计数减1的常见操作有release、autorealease。

自动释放池是一个统一来释放一组对象的容器,在向对象发送autorelease消息时,对象并没有立即释放,而是将对象加入到最新的自动释放池(即将该对象的引用交给自动释放池,之后统一调用release),自动释放池会在程序执行到作用域结束的位置时进行drain释放操作,这个时候会对池中的每一个对象都发送release消息来释放所有对象。这样其实就实现了这些对象的延迟释放。

自动释放池释放的时机指自动释放池内的所有对象是在什么时候释放的,这里要提到程序的运行周期RunLoop。对于每一个新的RunLoop,系统都会隐式地创建一个autoreleasepool,RunLoop结束时自动释放池便会进行对象释放操作。autorelease和release的区别主要是引用计数减1的时机不同,autorelease是在对象的使用真正结束的时候才做引用计数减1,而不是收到消息立马释放。

retain、release和autorelease的内部实现代码如下:

-(id)retain{

/*对象引用计数加1*/

NSIncrementExtraRefCount(self);

returnself;

}

-(void)release{

/*对象引用计数减1,之后如果引用计数为0,那么释放*/

if(NSDecrementExtraRefCountWasZero(self)){

NSDeallocateObject(self);

}

}

-(id)autorelease{

/*添加对象到自动释放池*/

[NSAutoreteasePooladdObject:self];

returnself;

}

6.

执行下面的代码会发生什么后果?

Ball*ball=[[[[Ballalloc]init]autorelease]autorelease];正确答案:上面的代码会导致程序崩溃,因为连续两次调用autorelease方法后,对象被加入到自动释放池两次。当对象被移除时,自动释放池将其释放了不止一次,其中第二次释放必定导致崩溃。

7.

什么是“应用瘦身”?正确答案:“应用瘦身”(Appthinning)是美国苹果公司自iOS9发布的新特性,它能对AppleStore和操作系统进行优化,它根据用户的具体设备型号,在保证应用特性完整的前提下,尽可能地压缩和减少应用程序安装包的体积,也就是尽可能减少应用程序对用户设备内存的占用,从而减小用户下载应用程序的负担。Appthinning的实现主要有以下3种方法:Slicing、Bitcode和On-DemandResources。以下将对这3种方法进行介绍。

1.Slicing

在开发者将完整的应用安装包发布到AppleStore之后,AppleStore会根据下载用户的目标设备型号创建相应的应用变体(variantsoftheappbundle)。这些变体只包含可执行的结构和资源等必要部分,而不需要让用户下载开发者提供的完整安装包。下图展示了从开发者使用Xcode开发完整应用并发布到AppleStore后被用户下载到不同设备上的流程。

应用发布下载流程图

2.Bitcode

Bitcode是iOS中开发者的一个可选项,如果工程中开启了Bitcode,那么苹果会对开发者编译后的应用二进制文件进行二次优化,将其转换成一种中间代码(Bitcode),在AppleStore上进行编译和链接。Bitcode属于官方的一种新的优化技术,由于很多第三方库不支持Bitcode,所以很多时候不得不关闭Bitcode以保证程序的正常运行。

3.On-DemandResources

它是一种“按需供给”的资源加载方式,用户下载应用程序时不需要下载应用程序完整的资源,而是在用户使用过程中到了某个阶段需要用到某些资源(图片资源和声音资源等)时,才从后台的服务器下载同步,这种方式类似于资源的延迟加载,可以减轻本地内存的负担。这种方式在游戏等对资源使用量大的应用程序中效果最明显。另外,这些在后台延迟下载的资源在内存紧张时可以自动删除,从而极大地提高内存的利用率。

8.

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

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

9.

按照默认法则,哪些关键字生成的对象需要手动释放?正确答案:起初在MRC(手动引用计数)中开发者要自己手动管理内存,基本原则是:谁创建,谁释放,谁引用,谁管理。其中,创建主要始于关键词new、alloc和copy的使用,创建并持有开始引用计数为1,如果引用要通过发送retain消息增加引用计数,通过发送release消息减少引用计数,那么引用计数变为0后对象会被系统清理释放。现在有了ARC(自动引用计数)后编译器会自动管理引用计数,开发者不再需也不可以手动管理引用计数。

使用new、alloc、copy关键字生成的对象和retain了的对象需要手动释放。被设置为autorelease的对象不需要手动释放,会直接进入自动释放池。

10.

如何理解HTTP协议?正确答案:超文本传输协议(HyperTextTransferProtocol,HTTP)是互联网上应用最为广泛的一种网络协议。简单来说,HTTP是客户端和服务器端之间请求和应答的标准,它可以使浏览器(或其他客户端)更加高效,使网络传输减少。它不仅保证计算机正确快速地传输超文本文档,还确定传输文档中的哪一部分,以及哪部分内容优先显示(如文本先于图形)等。

一次HTTP操作称为一个事务,其工作过程可分为4步:

1)客户端与服务器需要建立连接。例如,单击某个超链接后,浏览器和服务器将建立通信连接。

2)建立连接后,客户端发送一个请求给服务器,请求方式的格式为:统一资源标识符(URL)、协议版本号,后边是MIME信息包括请求修饰符、客户端信息和可能的内容。

3)服务器接到请求后,给予相应的响应信息,其格式为一个状态行,包括信息的协议版本号、一个成功或错误的代码,后边是MIME信息包括服务器信息、实体信息和可能的内容。

4)客户端接收服务器所返回的信启、通过浏览器显示在用户的显示屏上,然后客户机与服务器断开连接。

如果在以上过程中的某一步出现错误,那么产生错误的信息将返回到客户端,由显示屏输出。对于用户来说,这些过程是由HTTP自己完成的,用户只要用鼠标单击,等待信息显示就可以了。

通常HTTP消息包括客户端向服务器的请求消息和服务器向客户端的响应消息。这两种类型的消息由一个起始行,一个或者多个头域,一个指示头域结束的空行和可选的消息体组成。HTTP的头域包括通用头、请求头、响应头和实体头4个部分。每个头域由一个域名、冒号(:)和域值组成。域名是大小写无关的,域值前可以添加任何数量的空格符,头域可以被扩展为多行,在每行开始处,使用至少一个空格或制表符。

一个完整的由客户端发给服务器的HTTP请求包含以下两部分内容。

1)请求头:包含了对客户端的环境描述、客户端请求信息等。

GET/minion.pngHTTP/1.1

//包含了请求方法、请求资源路径、HTTP协议版本

Host:86:32812

//客户端想访问的服务器主机地址

User-Agent:Mozilla/5.0

//客户端的类型,客户端的软件环境

Accept:text/html

//客户端所能接收的数据类型

Accept-Language:zh-cn

//客户端的语言环境

Accept-Encoding:gzip

//客户端支持的数据压缩格式

2)请求体:客户端发给服务器的具体数据,例如文件数据(POST请求才会有)。

同样,一个完整的HTTP响应中应该包含以下两部分内容。

1)响应头:包含了对服务器的描述、对返回数据的描述。

HTTP/1.1200OK

//包含了HTTP协议版本、状态码、状态英文名称

Server:Apache-Coyote/1.1

//服务器的类型

Content-Type:image/jpeg

//返回数据的类型

Content-Length:56811

//返回数据的长度

Date:Mon,23Jun201412:54:52GMT

//响应的时间

2)响应体:服务器返回给客户端的具体数据,例如文件数据。

常见的响应状态码如图所示。

常见的响应状态码

此外,在iOS开发中发送HTTP请求有以下几种方案:

1)NSURLConnection。iOS7之前,通常使用NSURLConnection及关联类,这是iOS开发中最经典的网络请求方案。

2)NSURLSession。iOS7之后,逐渐取代NSURLConnection,现在已经是iOS开发中最常用的网络请求方式。

3)CFNetwork。它是NSURLConnection和NSURLSession的底层C语言实现,很少在开发中使用。

4)其他第三方网络请求框架,如Objective-C中的AFNetworking和Swift中的Alamofire等。这些网络框架也基本上都是对其他3种方案的封装与拓展。

11.

什么是Cocoa和CocoaTouch?正确答案:Cocoa和CocoaTouch分别是OSX平台和。iOS平台的应用开发环境,两个平台的环境都包含Objective-C的运行时环境和Foundation框架。

Cocoa用来开发OSX系统上的应用,它主要包括Foundation框架和AppKit界面开发框架,如图所示。

Cocoa架构图

CocoaTouch用来开发iOS系统上的应用(主要指iPhone和iPad),它主要包括Foundation框架和UIKit界面开发框架,如图所示。

12.

触发器分为事前触发和事后触发,二者有什么区别?语句级触发和行级触发有什么区别?正确答案:事前触发发生在事件发生之前验证一些条件或进行一些准备工作;事后触发发生在事件发生之后,做收尾工作,保证事务的完整性。事前触发可以获得之前和新的字段值。语句级触发器可以在语句执行之前或之后执行,而行级触发在触发器所影响的每一行触发一次。

13.

一般的方法method和Objective-C中的选择器selector有什么区别?正确答案:selector是一个方法的名字,基于动态绑定环境下;method是一个组合体,包含了名字和实现。

可以理解@selector()就是取类方法的编号,它的行为基本可以等同C语言的中函数指针,只不过C语言中,可以把函数名直接赋给一个函数指针,而Objective-C的类不能直接应用函数指针,只能通过一个@selector语法来取,它的结果是一个SEL类型。这个类型本质是类方法的编号(函数地址)。

14.

Objective-C中@class代表什么?正确答案:@class相当于只是在头文件声明一下要用到的类的头文件(前向声明),告诉编译器有这样一个类的定义但暂时不要将类的实现引入,让该类定义的变量能够编译通过,直到运行起来时才去查看类的实现文件。但实际上这样也只能起到在头文件中声明该类实例变量的作用,在.m文件中如果用到类的实现细节(属性和方法),那么还是要通过#import再次引入类的头文件。

使用@class的好处是将头文件的引入延迟了,至少延迟到了.m实现文件中,这也符合“直到真正用到的时候再确定引入”的动态思想,尽量往后拖延,更重要的是这样也可以有效地避免头文件的重复引入甚至循环引用等问题。

15.

什么是Layer层对象?正确答案:Layer层对象是用来展示可见内容的一种数据对象,常在视图中用来渲染视图内容。一般的层对象在界面中可以实现一些复杂的动画或者其他类型的一些复杂特效。

常见的几个其自身具有绘制功能的专用Layer有:CATextLayer、CAShapeLayer、CAGradientLayer,这里给出使用示例。其他还有用于3D图形变换的CATransformLayer,实现滚动视图的CAScrollLayer,专门播放视频的AVPlayerLayer和制作粒子特效的CAEmitterLayer等。它们都是继承自CALayer的,和CALayer一样都来自QuartzCore.framework框架。

1.CATextLayer

这个类是用来实现更加灵活的文字布局和渲染的,它几乎包含了UILabe1的所有特性并在此基础上增加了很多更强大的功能,包括字体、尺寸、前景色和下画线等文字效果,同时CATextLayer的渲染效率明显高于UILabel。

通过CALayer来实现一个UILabel的示例代码如下(效果见图):

-(void)viewDidLoad{

[superviewDidLoad];

/*创建一个字符承载视图*/

UIView*textView=[[UIViewalloc]initWithFrame:CGRectMake(50,50,200,50)];

CATextLayer*text=[CATextLayerlayer];

text.frame=textView.frame;

text.string=@"CAText";

/*文字前景色和背景色*/

text.foregroundColor=[UIColorwhiteColor].CGColor;

text.backgroundColor=[UIColorblackColor].CGColor;

/*文字超出视图边界裁剪*/

text.wrapped=YES;

/*文字字体*/

text.font=(__bridgeCFTypeRef)[UIFontsystemFontOfSize:30].fontName;

/*文字居中*/

text.alignmentMode=kCAAlignmentCenter;

/*适应屏幕Retina分辨率,防止像素化导致模糊*/

text.contentsScale=[[UIScreenmainScreen]scale];

[textView.layeraddSublayer:text];

[self.viewaddSubview:textView];

}

CATextLayer效果

2.CAShapeLayer

这个类是用来专门绘制矢量图形的图形子类,例如可以指定线宽和颜色等利用CGPath绘制图形路径,可以实现图形的3D变换效果,渲染效率比CoreGraphics快很多,而且可以在超出视图边界之外绘制,即不会被边界裁减掉。

这里展示使用CAShapeLayer绘制一个圆形的实例代码如下(效果见图):

-(void)viewDidLoad{

[superviewDidLoad];

/*创建圆形路径*/

UIBezierPath*path=[[UIBezierPathalloc]init];

/*起点要在圆心水平右侧半径长度处*/

[pathmoveToPoint:CGPointMake(200,100)];

/*添加圆形弧路径*/

[pathaddAreWithCenter:CGPointMake(150,100)radius:50startAngle:0endAngle:2*M_PIclockwise:YES];

/*创建图形层*/

CAShapeLayer*layer=[CAShapeLayerlayer];

/*路径线的颜色*/

layer.strokeColor=[UIColorredColor].CGColor;

/*闭合图形填充色,这里设置透明*/

layer.fillColor=[UIColorclearColor].CGColor;

/*线宽*/

layer.lineWidth=10;

/*线的样式:端点、交点*/

layer.lineCap=kCALineCapRound;

layer.lineJoin=kCALineJoinRound;

/*设置图形路径*/

layer.path=path.CGPath;

[self.view.layeraddSublayer:layer];

}

@end

CAShapeLayer绘制效果

3.CAGradientLayer

它是一个硬件加速的高性能绘制图层,主要用来实现多种颜色的平滑渐变效果。这里给出一个3种颜色从正方形左上角到右下角的渐变效果示例代码(效果见图6):

-(void)viewDidLoad{

[superviewDidLoad];

/*创建layer承载视图*/

UIView*containerView=[[UIViewalloc]initWithFrame:CGRectMake(50,50,150,150)];

CAGradientLayer*layer=[CAGradientLayerlayer];

layer.frame=containerView.bounds;

/*依次设置渐变颜色数组*/

layer.colors=@[(__bridgeid)[UIColorgreenColor].CGColor,(__bridgeid)[UIColoryellowColor].CGColor,(__bridgeid)[UIColororangeColor].CGColor];

/*颜色从起点到终点按比例分段位置*/

layer.locations=@[@0,0,@0.3,@0.5];

/*颜色渐变的起点和终点:(0,0)~(1,1)表示左上角到右下角*/

layer.startPoint=CGPointMake(0,0);

layer.endPoint=CGPointMake(1,1);

[containerView.layeraddSublayer:layer];

[self.viewaddSubview:containerView];

}

CAGradientLayer绘制效果

16.

在Objective-C中,常量有哪几种类型?正确答案:在Objective-C中,常量有以下4种类型。

(1)整型常量

整型常量由一个或多个int类型数字的序列组成。值12、0和-10都是合法的整型常量。数字之间不允许插入空格,并且不能使用逗号等特殊符号(例如,1,000就是一个非法的整型常量,它必须写作1000)。

(2)浮点型常量

浮点型常量和整型常量的数据正好相反,浮点型常量可以存储包含小数位数字的值。例如0.01、3.1415926、-0.002等都是合法的浮点型常量。

(3)字符常量

字符类型的常量可以存储单个字符。将字符放入一对单引号中就能得到字符常量。因此,“a”“.”“;”和“100”都是合法的字符常量。需要注意的是,第4个表示字符100,它和数字100并不相同。

(4)符号常量

前面3种常量可统称为直接常量,而符号常量是使用标识符代表一个常量。符号常量在使用之前必须先定义,其一般形式为:

#define标识符常量

示例代码如下:

#definePRICE10

main(){

intnum,total;

num=10;

total=num*PRICE;

printf("total=%d",total);

}

以上程序的输出为100。在本例中,#define是一条预处理命令(在后面的预处理程序章节中将进一步介绍),其功能是将标识符定义为其后的常量值。习惯上符号常量的标识符用大写字母,变量标识符用小写字母,以示区别。

17.

NSMutableDictionary中setValue和setObject有什么区别?正确答案:需要了解苹果公司API中关于NSMutableDictionary中setValue:forKey:方法和setObject:forKey:方法的定义。其中,对应setValue:forKey:方法的API是:

@interfaceNSMutableDictionary<KeyType,ObjectType>(NSKeyValueCoding)

/*Send

-setObject:forKey:tothereceiver,unlessthevalueisnil,inwhichcasesend-removeObjectForKey:*/

-(void)setValue:(nullableObjectType)valueforKey:(NSString*)key;

@end

官方的注释说得很清楚,当发送setValue:forKey:消息给一个NSMutableDictionary类型的对象时,一般情况下仍然是调用了setObject:forKey方法,除非,当参数value的值是nil的时候,就会转而调用removeObjectForKey:清除这个键值对。

对应setobject:forKey:方法的API是:

-(void)setObject:(id)anObjectforKey:(id<NSCopying>)aKey;

这里需要注意的是,这个方法中Key是一个遵循了NSCopying协议的id类型对象,并不是NSString类型的字符串,只不过在实践中通常使用NSString而己。示例代码如下:

NSMutableDictionary*dic=[NSMutableDictionarydictionary];

[dicsetObject:@"1"forKey:[NSNumbernumberWithInt:1]];

[dicsetValue:@"2"forKey:[NSNumbernumberWithInt:2]];

NSLog(@"value1=%@value2=%@",dic[[NSNumbernumberWithInt:1]],dic[[NSNumbernumberWithInt:2]]);

程序的输出结果为:

2016-10-1117:12:40:865test[5827:890647]value1=1value2=2

事实上,当试图为setValue:forKey:设置一个非NSString类型的Key时(此处是NSNumber类型),系统会发出“uncompatiblepointertypessending'NSNumber*_Nonnull'toparameteroftype'NSString*_Nonnull'”的警告,但是不会抛出异常,仍然会正常打印出结果。

综上所述,NSMutableDictionary中setValue:forKey:方法和setObject:forKey:方法的主要区别有:

1)setObject:forKey:方法是NSMutableDictionary类特有的,只有NSMutableDictionary类及其子类的实例化对象能够使用,而setValue:forKey:方法是KVC的主要方法,只要遵循了NSCoding协议的对象都能够使用。

2)setObject:forKey:方法中参数value的值不能为nil,否则会抛出异常。而setValue:forKey:中value能够为nil,只是当value为nil的时候,会自动调用removeObject:forKey:方法。

3)setValue:forKey:中key的参数只能够是NSString类型,而setObject:forKey:的key可以是任何类型的对象类型。

18.

Objective-C的类可以多重继承吗?可以实现多个接口吗?重写一个类的方式用继承好还是类别好?为什么?正确答案:Objective-C的类只支持单继承,不可以多重继承。

Objective-C可以利用protocol代理协议实现多个接口,通过实现多个接口完成类似C++的多重继承。在Objective-C中多态特性是通过protocol(协议)或者Category(类别)来实现的。protocol定义的接口方法可以被多个类实现,Category可以在不变动原类的情况下进行方法重写或者扩展。

重写一个类一般情况下使用类别更好,因为用Category去重写类的方法,仅对本Category有效,不会影响到其他类与原有类的关系。

19.

AppID和BundleID有什么不同?正确答案:AppID是一个组合字符串,它包括两个部分,一个是开发团队的ID,另一个是标识应用的BundleID,它们之间是用点隔开的。开发团队的ID是苹果公司提供给开发者的,这个ID可以唯一标识一个开发团队;BundleID是开发者自定义的唯一标识一个应用的。一个团队的ID和不同的BundleID组合可以得到不同的AppID,这个AppID就可以标识该团队的不同的应用,开发者需要通过AppID来使自己的应用可以获取丰富的苹果服务,如iCloud服务等。

BundleID也就是AppID的后半部分,是一个App应用的唯一标识符,

温馨提示

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

评论

0/150

提交评论