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

下载本文档

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

文档简介

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

如何使用runtime动态添加一个类?正确答案:runtime之所以非常强大,很关键的一点就是它能够动态地创建一个全新的类或对象。在runtime.h文件中(江南博哥)可以查到动态创建一个类所涉及的所有函数。

/*创建一个新类或元类*/

objc_allocateClassPair(Classsuperclass,constchar*name,size_textraBytes)

/*注册一个通过objc_allocateClassPair方法创建的类*/

objc_registerClassPair(Classcls)

/*销毁一个类及其元类*/

objc_disposeClassPair(Classcls)

上面3个函数的作用见表。动态创建类所涉及的3个函数函数名作用objc_allocateClassPair函数它的作用是创建一个新类或元类。如果开发者想让这个类成为基类,那么参数superclass指定为nil。参数extraBytes是分配给类和元类对象尾部的索引ivars的字节数,通常指定为0objc_registerClassPair函数当创建完新类后,需要调用这个方法来注册这个类,之后这个类才可以在程序中使用objc_disposeClassPair函数用于销毁一个类及其元类。需要注意的一点是,如果程序运行中还存在类或其子类的实例,那么就不能调用该方法

那么,如何运用上述3个函数动态地添加一个类呢?示例代码如下:

/*创建一个新类myClass*/

ClassmyClass=objc_allocateClassPair([NSObjectclass],"myClass",0);

/*添加ivar*/

//@encode(aType):返回该类型的C字符串

class_addIvar(myClass,"_address",sizeof(NSString*),log2(sizeof(NSString*)),@encode(NSString*));

class_addIvar(myClass,"_age",sizeof(NSUInteger),log2(sizeof(NSUInteger)),@encode(NSUInteger));

/*注册类*/

objc_registerClassPair(myClass);

/*创建实例*/

idobject=[[myClassalloc]init];

/*为ivar设置值*/

[objectsetValue:@"china"forKey:@"address"];

[objectsetValue:@20forKey:@"age"];

NSLog(@"address=%@,age=%@",[objectvalueForKey:@"address"],[objectvalueForKey:@"age"]);

/*当类或者它的子类的实例还存在,则不能调用objc_disposeClassPair方法*/

object=nil;

/*销毁*/

objc_disposeClassPair(myClass);

程序的打印结果如下:

2016-11-0219:32:39.61501[67782:2681508]address=china,age=20

这样就通过运行时动态创建了一个继承自NSObject的myClass类,并为这个新类添加了address和age两个成员变量。特别需要注意的是,添加成员变量的方法class_addIva必须要在objc_allocateClassPair和objc_registerClassPair之间调用才行,这里涉及Objective-C中类的成员变量的偏移量,如果在类注册之后再调用class_addIva方法,那么会破坏原来类成员变量正确的偏移量,导致的后果就是会获取到错误的成员变量。

2.

一二三四范式有什么区别?正确答案:在设计与操作维护数据库时,最关键的问题就是要确保数据正确地分布到数据库的表中。使用正确的数据结构,不仅有助于对数据库进行相应的存取操作,还可以极大地简化应用程序的其他内容(查询、窗体、报表、代码等),正确地进行表的设计成为“数据库规范化”,它的目的就是减少数据库中的数据冗余,从而增加数据的一致性。

规范化是在识别数据库中的数据元素、关系,以及定义所需的表和各表中的项目这些初始工作之后的一个细化的过程。常见的范式有1NF、2NF、3NF、BCNF及4NF。

1NF,第一范式。它指数据库表的每一列都是不可分割的基本数据项,同一列中不能有多个值,即实体中的某个属性不能有多个值或者不能有重复的属性。如果出现重复的属性,那么就可能需要定义一个新的实体,新的实体由重复的属性构成,新实体与原实体之间为一对多关系。第一范式的模式要求属性值不可再分裂成更小部分,即属性项不能是属性组合或由组属性组成。简而言之,第一范式就是无重复的列。例如,由“职工号”“姓名”“电话号码”组成的表(一个人可能有一个办公电话和一个移动电话),这时将其规范化为1NF可以将电话号码分为“办公电话”和“移动电话”两个属性,即职工(职工号、姓名、办公电话、移动电话)。

2NF,第二范式。第二范式是在第一范式的基础上建立起来的,即满足第二范式必须先满足第一范式。第二范式要求数据库表中的每个实例或行必须可以被唯一地区分。为实现区分通常需要为表加上一个列,以存储各个实例的唯一标识。如果关系模式(R)为第一范式,并且R中每一个非主属性完全函数依赖于R的某个候选键,则称R为第二范式模式。(如果A是R的候选键的一个属性,则称A是R的主属性,否则称A是R的非主属性。)例如,在选课关系表(学号、课程号、成绩、学分),关键字为组合关键字(学号、课程号),由于非主属性学分仅依赖于课程号,对关键字(学号、课程号)只是部分依赖,而不是完全依赖,所以此种方式导致数据冗余及更新异常等问题,解决办法是将其分为两个关系模式:学生表(学号、课程号、分数)和课程表(课程号、学分),新关系通过学生表中的外关键字课程号联系,在需要时进行连接。

3NF,第三范式。如果R是第二范式,且每个非主属性都不传递依赖于R的候选键,则称R是第三范式的模式。例如学生表(学号、姓名、课程号、成绩),其中学生姓名无重名,所以该表有两个候选键(学号、课程号)和(姓名、课程号),则存在函数依赖:学号→姓名,(学号、课程号)→成绩,(姓名、课程号)→成绩,唯一的非主属性成绩对候选键不存在部分依赖,也不存在传递依赖,所以属于第三范式。

BCNF。它构建在第三范式的基础上,如果R是第一范式,且每个属性都不传递依赖于R的候选键,那么称R为BCNF的模式。假设仓库管理关系表(仓库号、存储物品号、管理员号、数量),满足一个管理员只在一个仓库工作;一个仓库可以存储多种物品。则存在如下关系:

(仓库号、存储物品号)→(管理员号、数量)

(管理员号、存储物品号)→(仓库号、数量)

所以,(仓库号、存储物品号)和(管理员号、存储物品号)都是仓库管理关系表的候选键,表中的唯一非关键字段为数量,它是符合第三范式的。但是,由于存在如下决定关系:

(仓库号)→(管理员号)

(管理员号)→(仓库号)

即存在关键字段决定关键字段的情况,所以其不符合BCNF范式。把仓库管理关系表分解为两个关系表:仓库管理表(仓库号、管理员号)和仓库表(仓库号、存储物品号、数量),这样的数据库表是符合BCNF范式的,消除了删除异常、插入异常和更新异常。

4NF,第四范式。设R是一个关系模式,D是R上的多值依赖集合。如果D中成立非平凡多值依赖X→Y时,X必是R的超键,那么称R是第四范式的模式。例如,职工表(职工编号、职工孩子姓名、职工选修课程),在这个表中同一个职工也可能会有多个职工孩子姓名,同样,同一个职工也可能会有多个职工选修课程,即这里存在着多值事实,不符合第四范式。如果要符合第四范式,那么只需要将上表分为两个表,使它们只有一个多值事实,例如职工表一(职工编号、职工孩子姓名),职工表二(职工编号、职工选修课程),两个表都只有一个多值事实,所以符合第四范式。

下图为各范式关系图。

各范式关系图

3.

为什么iOS中提供UIView和CALayer这两个平行的层级结构呢?正确答案:iOS中提供UIView和CALayer这两个平行层级结构主要是为了做到职责分离,实现视图的绘制、显示和布局解耦,避免很多重复的代码。在iOS和MacOS两个平台上,事件和用户交互有很多地方并不相同,毕竟基于多点触控的用户界面和基于鼠标键盘有着本质的区别,这就是为什么iOS有UIKit和UIView,但是MacOS有Appkit和NSView的原因。它们功能虽然相似,但是在实现上有着显著的区别。创建两个层次结构就能够在iOS与MacOS之间共享代码,从而使得开发更加便捷。

4.

contentsScale属性有什么作用?正确答案:图层的contentsScale属性属于支持高分辨率屏幕(如Retina屏幕)机制的一部分,它定义了图层content中图像的像素尺寸与视图大小的比例。它也被用来判断在绘制图层时允许为content属性创建的空间大小,以及需要显示的图片的拉伸度。

默认情况下,contentsScale的值是1.0,也就是说图层的绘制系统将会以每个点对应一个像素来绘制图片。如果将其设置为2.0,那么会以每个点对应两个像素来绘制图片,此即所谓的Retina屏幕。

在开发中,有时会直接为图层的content设置图片,这时可以设置contentsScale为合适的值,以防止图片在Retina屏幕上显示不正确(像素化或模糊),代码如下:

layer.contentsScale=[UIScreenmainScreen].scale;

5.

分别写一个setter方法用于完成非ARC下的@property(nonatomic,retain)NSString*name和@property(nonatomic,copy)NSString*name。正确答案:第一种情况,retain是指针变量name对新赋值对象的强引用,相当于ARC下的strong,因此对name指针变量set新值时要先将新赋值对象的引用计数加1,然后将指针变量指向新赋值对象,类似于“浅拷贝”。

首先在实现文件中合成属性变量:@synthesiszename;,然后两种情况下自定义setter方法如下,自定义了setter方法后编译器就不会再在编译期重复合成setter方法了。

/*retain*/

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

if(name!=newName){

[newNameretain];

//新对象引用计数加1

[namerelease];

//将指针变量原来的对象释放掉

name=newName;

//指针变量指向新对象

}

}

第二种情况,copy指当对指针变量name赋值新对象时,是将新对象完全copy一份,将copy好的对象复制给指针变量,即指针指向的是临时copy出来的对象,而不是新赋值的那个对象,因此新赋值对象不需要引用计数加1,因为指针变量并没有指向持有它,类似于“深拷贝”。

/*copy*/

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

if(name!=newName){

idtemp=[newNamecopy];

//将新对象原样克隆一份

[namerelease];

//将指针变量原来的对象释放掉

name=temp;

//指针变量指向新对象的克隆体

}

}

6.

如何使用NSURLSession进行网络请求?正确答案:在2013年的WWDC上,苹果公司推出了NSURLConnection的替代方案:NSURLSession。和NSURLConnection一样,NSURLSession指的也不仅是同名类NSURLSession,它还包括一系列相关联的类。NSURLSession包括了与之前相同的组件:NSURLRequest与NSURLCache,但是将NSURLConnection替换成了NSURLSession、NSURLSessionConfiguration及NSURLSessionTask的3个子类:NSURLSessionDataTask、NSURLSessionUploadTask、NSURLSessionDownloadTask。

与NSURLConnection相比,NSURLSession最直接的改进就是可以配置每个session的缓存、协议、cookie,以及证书策略(CredentialPolicy),甚至跨进程共享这些信息。这将允许程序和网络基础框架之间相互独立,不会发生干扰。每个NSURLSession对象都由一个NSURLSessionConfiguration对象进行初始化,后者指定了刚才提到的那些策略以及一些用来增强移动设备上性能的新选项。

NSURLSessionTask负责处理数据的加载以及文件的数据在客户端与服务器之间的上传和下载。它是一个抽象类,一般使用其子类:NSURLSessionDataTask、NSURLSessionUploadTask、NSURLSessionDownloadTask。这3个子类封装了现代程序3个最基本的网络任务:获取数据(如JSON或者XML),上传文件和下载文件。

NSURLSession相关类的关系如图所示。

NSURLSession相关类的关系

如何使用NSURLSession像NSURLConnection那样发送一个请求呢?基本步骤如下:

1)创建NSURLSessionConfiguration对象对NSURLSession进行配置。

2)创建NSURLSession对象。

3)利用上一步创建好的NSURLSession对象创建NSURLSessionTask的子类对象。

4)执行请求任务。

下面的示例展示了NSURLSession的基本用法,代码如下:

-(void)sessionGet{

/*创建NSURL对象*/

NSURL*url=[NSURLURLWithString:IMAGEURL];

/*创建请求对象,默认为GET请求*/

NSURLRequest*request=[NSURLRequestrequestWithURL:url];

/*创建配置*/

NSURLSessionConfiguration*config=[NSURLSessionConfigurationdefaultSessionConfigtwation];

/*创建NSURLSession对象*/

NSURLSession*session=[NSURLSessionsessionWithConfiguration:config];

/*创建任务*/

NSURLSessionDataTask*task=[sessiondataTaskWithRequest:requestcompletionHandler:^(NSData*_Nullabledata,NSURLResponse*_Nullableresponse,NSError*_Nullableerror){

if(error){

return;

}

/*解析返回的数据*/

_image=[UIImageimageWithData:data];

/*显示图片,注意!此时是异步线程需要在主线程中显示图片*/

[selfchangeBg];

}];

[taskresume];

}

7.

Objective-C对象可以被copy的条件是什么?正确答案:Objective-C对象可以被复制的条件是要遵守NSCopying和NSMutableCoping协议,默认是不遵守的。可以向遵守NSCopying协议的类的实例发送copy消息,也可以向遵守NSMutableCopying协议的类的实例发送mutableCopy消息,如果没有遵守协议而发送copy或mutableCopy消息,那么就会出错。两个协议分别对应不可变复制(copy)和可变复制(mutableCopy),而协议内部的实现方式决定是深拷贝还是浅拷贝。

例如,典型的系统类NSAnay是同时遵守了NSCopying和NSMutableCopying协议的,因此既可以发送copy消息,也可以发送mutableCopy消息。此外,iOS中同时遵守NSCopying协议和NSMutableCopying协议的类还有:NSString、NSValue、NSDictionary和NSSet及其子类。

如果想自定义深拷贝或浅拷贝方法,那么就要先遵守对应的协议,并重新实现对应的协议方法。

NSCopying的协议方法为copyWithZone:

-(id)copyWithZone:(nullableNSZone*)zone{

//深拷贝或浅拷贝实现

}

NSMutableCopying的协议方法为mutableCopyWithZone:

-(id)mutableCopyWithZone:(nullableNSZone*)zone{

//深拷贝或浅拷贝实现

}

8.

Cocoa中有虚基类的概念吗?正确答案:Cocoa中没有虚基类的概念。虚基类是C++语言中为了解决多重继承二义性问题的,而Objective-C中只有单继承。

要实现类似C++语言中的多继承,可以通过protocal来简单实现,因为一个类可以实现多个协议,类似于Java中一个类可以实现多个接口。

9.

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

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

10.

iOS中是如何使用自定义字体的?正确答案:字体是软件开发中个性化的一个重要元素,系统自带了很多丰富的字体,但有时候并不能满足个性化的需求,这时候可以向工程中添加自定义的系统字体,然后就可以像使用系统字体一样使用。字体文件最常用的为ttf等格式。

导入自定义字体过程很简单:添加资源包到工程→在info.plist文件中注册字体→在工程BundleResource中复制字体资源包→代码检测查询加入的字体并使用。

1.添加资源包

addFile添加字体资源包或者直接将字体包拖到工程资源文件夹下(见图1)。

图1

字体文件拖入工程目录

2.info.plist文件中注册字体,添加字体资源包

在工程的info.plist属性列表中添加Fontsprovidedbyapplication数组属性,并在其下添加要加入的自定义字体项(见图2和图3)。注意,这里在plist文件中写的是文件的全称,包括文件扩展名,文件的名字是可以随便改的,但建议用本来的字体族名,例如这里是:KristenITC,字体族名是不会变的,之后具体代码中使用的时候是用的字体族名而不是自定义的文件名。本来的字体族名可以右键查看字体文件的详细信息,里面的全称是本来的字体族名,而名称是自定义的。字体注册后将资源包复制到BundleResource即可(见图3)。

图2

配置info.plist

图3

添加注册自定义字体项

图4

字体包复制到BundleResource

3.检测是否成功加入字体

在具体使用之前,可以先通过UIFont类提供的方法打印出系统所有的字体列表,并找到刚添加的字体验证是否添加成功,还可以具体看到资源包有哪些具体的字体样式,如该字体族的斜体、粗体、粗斜体等。打印字体族列表的代码如下:

/*检查自定义字体族是否成功加入*/

/*取出系统安装了的所有字体族名*/

NSArray*familyNames=[UIFont=familyNames];

NSLog(@"系统所有字体族名:%@",familyNames);

/*打印字体族的所有子字体名(每种字体族可能对应多个子样式字体,例如每种字体族可能有粗体、斜体、粗斜体等等样式)*/

for(NSString*familyNameinfamilyNames){

/*字体族的所有子字体名*/

NSArray*detailedNames=[UIFontfontNamesForFamilyName:familyName];

NSLog(@"\n字体族%@的所有了字体名:%@",familyName,detailedNames);

}

这里可以从字体组列表中找到刚添加的字体族KristenlTC,结果如图5所示。

图5

已有字体列表

还可以看到字体族KristenITC下的具体字体样式,这里只有一种也是默认的一种:KristenlTC-Regular(见图6)。

图6

字体名

4.使用字体

确定字体加入系统之后就可以像自带的系统字体一样直接使用了(字体效果如图7所示)。

/*设置label的字体和大小f这里直接使用字体族名也是可以的,有默认的子字体样式,也可以根据需求具体到子字体例如这里的:KristenITC-Regular)*/

[_labelsetFont:[UIFontfontWithName:@"KristenITC"size:35.0]];

图7

字体效果

11.

block有哪几种定义的方式?正确答案:在Objective-C中,block定义包含了block的类型声明和实现,基本形式如下:

返回值类型(^block名称)(参数类型)=^(参数类型和参数名){};

其中,返回值类型和参数可以是空。如果有参数,那么在定义block的时候,必须要标明参数的类型和参数名。所以,block大致有3种细分的定义方式。

1)没有返回值,没有参数的定义方式。

void(^myBlock)()=^{

//代码

};

2)有返回值,有参数的定义方式。

int(^myBlock)(int)=^(inta){

returna;

};

3)有返回值,没有参数的定义方式。

int(^myBlock)()=^{

return100;

};

当然,block也有属于自己的类型,就像在Objective-C中,字符串对象属于NSString类型一样。block类型的格式就是:

返回值类型(^)(参数类型)

也就是说,上面第一种定义方式的block类型就是void(^)(),myBlock不是变量名,而是这种block类型的别名。在Objective-C中,可以使用typedef关键字定义block类型,也可以直接使用inline提示符来自动生成block格式。示例代码如下:

/*使用typedef关键字定义block类型*/

typedefvoid(^myBloek)();

myBlockblock=^{

};

/*使用inline提示符来自动生成block格式*/

<#retumType#>(^<#blockName#>)(<#parameterTypes#>)=^(<#parameters#>){

<#statements#>

};

12.

什么是内存泄漏?什么是安全释放?正确答案:内存泄漏指动态分配内存的对象在使用完后没有被系统回收内存,导致该对象始终占用内存,又无法通过代码访问,属于内存管理出错,如果出现大量内存泄漏,那么会导致系统内存不足的问题。由于内存泄漏的检测是程序开发中不可避免的问题,所以对于程序员而言,一方面要深入理解内存管理原则,养成良好的编程习惯以减少内存泄漏情况的发生;另一方面可以通过各种方法,例如使用Xcode提供的检测调试工具Instruments,检测可能导致内存泄漏的代码,并及时进行优化。

安全释放指释放掉不再使用的对象的同时不会造成内存泄漏或指针悬挂问题的操作。为了保证安全释放,在对象dealloc后要将其指针置为nil。另外,要严格遵守内存管理原则,保证对象的引用计数正确,同时要注意避免引用循环的出现。

13.

一个tableView是否可以关联两个不同的datasource数据源?正确答案:多个数据源是完全可以的,问题的重点是如何关联,因为将数据源(Model)和tableView视图的对接工作是程序员完成的,所以数据源的多少没有根本影响。处理上可以分开依次对接,也可以通过数据的集合操作先将数据整理合并成一个数据源,然后对接。

例如,一个表格中的每个cell显示的是一个人的基本信息,为了简单这里假设只有一个头像和I一个姓名。假设有两个数据源,一个数据源是头像的url数组;另一个是姓名的字符串数组,对接时完全可以分开在cell数据回调中对接,也可以将两个数组合并然后对接。

合并数据用到的数据模型:

@interfaceModel:NSObject

@property(nonatomic,copy)NSString*name;

//姓名

@property(nonatomic,copy)NSString*url;

//图片

@end

数据源缓冲器:

/*数据源*/

@property(nonatomic,strong)NSArray*nmne_datasource;

@property(nonatomic,strong)NSArray*url_datasource;

@property(nonatomic,strong)NSMutableArray*datasource;

处理多数据源:

/*请求数据*/

-(void)request{

/*姓名数据源*/

_name_datasource=@[@"张三",@"李四",@"小明",@"小李"];

_url_datasource=@[@"male",@"male",@"male",@"male"];

/*合并数据源*/

for(inti;i<_name_datasource.count;i++){

Model*model=[[Modelalloc]init];

=_name_datasource[i];

model.url=_url_datasource[i];

[_datasourceaddObject:model];

}

}

数据对接:

/*cell数据回调*/

-(UITableViewCell*)tableView:(UITableView*)tableViewcellForRowAtIndexPath:(NSIndexPath*)indexPath{

staticNSString*identifier=@"identifier";

/*自制cell组件*/

AccountCell*cell=[[AccountCellalloc]initWithStyle:UITableViewCellStyleDefaultreuseIdentifier:identifier];

/**多数据源分开对接:**/

/*头像*/

[cell.avatarsetImage:[UIImageimageNamed:_url_datasource[indexPath.row]];

/*姓名*/

.text=_name_datasource[indexPath.row];

/*或者:*/

/**数据源合并后对接**/

/*取出对应数据模型*/

Model*model=_datasource[indexPath.row];

/*头像*/

[cell.avatarsetImage:[UIImageimageNamed:model.url]];

/*姓名*/

.text=;

returncell;

}

14.

Objective-C中的NSInteger类型和C语言中的int类型有什么区别?正确答案:在Objective-C中,数据类型可以分为基本数据类型、对象类型和id类型。基本数据类型有int类型、float类型、double类型、char类型、布尔类型等。事实上,Objective-C中的NSInteger也是基本数据类型之一。在苹果的API实现中,NSInterger是一个对int类型和long类型的封装,它会识别当前操作系统的位数,自动返回最大的类型。定义NSInteger类型的代码如下:

#if__LP64__||(TARGET_OS_EMBEDDED&&!TARGET_OS_IPHONE)||TARGET_OS_WIN32||NS_BUILD_32_LIKE_64

typedeflongNSInteger;

typedefunsignedlongNSUInteger;

#else

typedefintNSInteger;

typedefunsignedintNSUInteger;

#endif

其中,#if、#else、typedef等都属于预处理语言。上面这段代码的意思是,当系统是32位系统时,NSInteger类型等价于int类型,即32位,但当系统是64位时,NSInteger类型等价于long类型,即64位。

可以看出,NSInteger是long或者int的别名,对应的NSUInteger是unsignedlong或者unsignedint的别名。区别在于,NSInteger会根据系统是32位机还是64位机来动态确定自身是整型还是长整型,从而可以很好地兼容两种机器。因此,当开发者不知道操作系统是什么类型的时候,通常应该使用NSInteger,这也是苹果公司推荐使用的基本数据类型之一。

另外,NSInteger是Objective-C基本数据类型,不是NSNumber的子类,也不是NSObject的子类。

15.

如何检测内存泄漏?正确答案:检测内存泄漏主要有以下3种方法:

1.静态分析

通过静态分析可以初步地了解到代码的一些不规范的地方或者是存在内存泄漏的地方,这是对内存泄漏的第一步检测。当然有一些警告是不需要关心的,可以略过。

2.通过instruments来检查内存泄漏

这个方法能粗略地定位在哪里发生了内存泄漏。方法是完成一个循环操作,如果内存增长为0,那么就证明程序在该次循环操作中不存在内存泄漏;如果内存增长不为0,那么就证明有可能存在内存泄漏。当然具体问题需要具体分析。

3.代码测试内存泄漏

在做这项工作之前,要注意在dealloc的方法中是否已经释放了该对象所拥有的所有对象。观察对象的生成和销毁是否配对。准确地说,就是init(创建对象的方法)和dealloc是否会被成对触发(简单来说就是有一次创建对象就有一次dealloc该对象)。

16.

隐式动画的实现原理是什么?如何禁用图层的隐式动画?正确答案:在iOS程序开发中,有时仅仅改变了图层或者视图的一个动画属性(例如backgroundColor),就会发现属性是平滑地过渡到目标值的,属性值的改变产生了一个动画效果,而实际上开发者并没有显式地使用动画,这就是隐式动画的作用。

隐式动画指当更改视图的非根图层的可动画属性时,CoreAnimation自动决定如何并且何时去做动画。动画的执行时间取决于当前事务(CATransaction),而动画类型取决于图层行为。

CATransaction类是用来包含一系列属性动画集合的机制,任何用指定事务去改变可动画属性都不会立即发生变化,而是当事务提交的时候开始用一个动画过渡到新值。CATransaction类没有属性或者实例方法,也不能用+alloc和-init方法创建,但是可以用+begin和+commit方法分别来入栈和出栈,这类似于UIView的+beginAnimations:context:和+commitAnimations方法。其次,还可以用+setAnimationDuration方法设置当前事务的动画时间,或者+animationDuration方法来获取值。

改变属性是CALayer自动应用的动画被称为“行为”。当CALayer的属性被修改的时候,它会调用actionForKey:方法,传递属性的名称。具体的实现原理分为以下几步:

1)图层会检测其是否有代理,并且代理是否实现了CALayerDelegate协议指定的-actionForLayer:forKey方法。如果有,那么直接调用并返回结果。

2)如果图层没有代理,或者代理没有实现-actionForLayer:forKey:方法,那么图层会接着检查包含属性名称对应行为映射的actions字典。

3)如果actions字典没有包含对应的属性,那么图层接着在它的style字典搜索属性名。

4)最后,如果在style字典中也没有找到对应的行为,那么图层将会直接调用定义每个属性的标准行为的-defaultActionForKey:方法。

于是就可以解释UIKit是如何禁用隐式动画的:每个UIView都是其根图层的代理,并且实现了-actionForLayer:forKey:方法,只不过返回nil,也就是不执行任何动画。

除了在-actionForLayer:forKey:方法的实现中返回nil外,还可以通过CATransaction的方法+setDisableActions来打开或者关闭属性的隐式动画。使用+setDisableActions临时取消隐式动画的代码如下:

-(void)viewDidLoad{

[superviewDidLoad];

_colorLayer=[CALayerlayer];

_colorLayer.bounds=CGRectMake(0,0,200,200);

_colorLayer.position=self.view.center;

_colorLayer.baekgroundeolor=[UIColorredColor].CGColor;

_colorLayer.delegate=self;

[self.view.layeraddSublayer:_colorLayer];

}

-(void)touchesBegan:(NSSet<UITouch*>*)toucheswithEvent:(UIEvent*)event{

[CATransactionbegin];

//关闭隐式动画

CATransaction.disableActions=YES;

CATransaction.animationDuration=5;

_colorLayer.backgroundColor=randomColor.CGColor;

[CATransactioncommit];

}

17.

GCD多线程编程中什么时候会创建新线程?正确答案:对于是否会开启新线程的情景主要有如下几种情况:串行队列中提交异步任务、串行队列中提交同步任务、并发队列中提交异步任务、并发队列中提交同步任务。

(其中主队列是典型的串行队列,全局队列是典型的并发队列)

/*创建一个串行队列*/

dispatch_queue_tserialQueue=dispatch_queue_create("serial.queue".DISPATCH_QUEUE_SERIAL);

/*创建一个并发队列*/

dispatch_queue_tconcurrentQueue=dispatch_queue_create("concurrent.queue",DISPATCH_QUEUE_CONCURRENT);

1)串行队列中提交同步任务:不会开启新线程,直接在当前线程同步地串行执行这些任务。

/*1串行队列添加同步任务:没有开肩新线程,全部在主线程串行执行*/

dispatch_sync(serialQueue,^{

NSLog(@"SERIAL_SYN_A%@",[NSThreadcurrentThread]);

});

dispatch_sync(serialQueue,^{

NSLog(@"SERIAL_SYN_B%@",[NSThreadcurrentThread]);

});

dispatch_sync(serialQueue,^{

NSLog(@"SERIAL_SYN_C%@",[NSThreadcurrentThread]);

});

2)串行队列中提交异步任务:会开启一个新线程,在新子线程异步地串行执行这些任务。

/*2串行队列添加异步任务:开启了一个新子线程并共用,串行执行*/

dispatch_async(serialQueue,^{

NSLog(@"SERIAL_ASYN_A%@",[NSThreadcurrentThread]);

});

dispatch_async(serialQueue,^{

NSLog(@"SERfAL_ASYN_B%@",[NSThreadcurrentThread]);

});

dispatch_async(serialQueue,^{

NSLog(@"SERIAL_ASYN_C%@",[NSThreadcurrentThread]);

});

3)并发队列中提交同步任务:不会开启新线程,效果和“串行队列中提交同步任务”一样,直接在当前线程同步地串行执行这些任务。

/*3并发队列添加同步任务:没有开启新线程,全部在主线程串行执行*/

dispatch_sync(concurrentQueue,^{

NSLog(@"CONCURRENT_SYN_A%@",[NSThreadcurrentThread]);

});

dispatch_sync(concurrentQueue,^{

NSLog(@"CONCURRENT_SYN_B%@",(NSThreadcurrentThread]);

});

dispatch_sync(concurrentQueue,^{

NSLog(@"CONCURRENT_SYN_C%@",[NSThreadcurrentThread]);

});

4)并发队列中提交异步任务:会开启多个子线程,在子线程异步地并发执行这些任务。

/*4并发队列添加异步任务:开启多个子线程,并发执行多个任务*/

dispatch_async(concurrentQueue,^{

NSLog(@"CONCURRENT_ASYN_A%@",[NSThreadcurrentThread]);

});

dispatch_async(concurrentQueue,^{

NSLog(@"CONCURRENT_ASYN_B%@",[NSThreadcurrentThread]);

});

dispatch_async(concurrentQueue,^{

NSLog(@"CONCURRENT_ASYN_C%@",[NSThreadcurrentThread]);

});

下图展示了上面例子中线程的执行顺序。

线程执行顺序

总结:

只有异步提交任务时才会开启新线程,异步提交到串行队列会开启一个新线程,异步提交到并发队列可能会开启多个线程。

同步提交任务无论提交到并发队列还是串行队列,都不会开启新线程,都会直接在当前线程依次同步执行。

注意,如果当前线程是主线程,那么不可在当前线程提交同步任务,否则会造成线程死锁而报错。

18.

Objective-C有私有方法吗?有私有变量吗?正确答案:Objective-C是否有私有方法和私有变量关键要看私有的定义。私有主要指通过类的封装性,将不希望让外界看到的方法或属性隐藏在类内部,只有该类可以在内部访问,外部不可见不可访问。

表面上看,Objective-C中是可以实现私有的变量和方法的,即将它们隐藏不暴露在头文件中,不可以显式地直接访问,但是Objective-C中这种私有并不是绝对的私有,例如即使将变量和方法隐藏在.m实现文件中,开发者仍然可以利用KVC或runtime运行时机制强行访问没有暴露在头文件的变量或方法。

Objective-C中实现变量和方法“私有”的方式主要有两种。

1)在类的头文件中声明私有变量。

#import<Foundation/Foundation.h>

@interfaceTest:NSObject{

/*头文件中定义私有变量,默认为@protected*/

@private

NSString*major;

}

@end

2)在.m实现文件头部的类扩展区域定义私有属性或方法,其中方法可不用声明,直接在实现文件中实现即可,只要不在头文件声明的方法都对外不可见。

#import"Test.h"

@interfaceTest(){

/*类扩展区域定义私有变量,默认就是@private*/

intage;

}

/*类扩展区域定义私有属性*/

@property(nonatomic,copy)NSString*name;

/*类扩展区域定义私有实例方法(可省略声明,类方法的作用主要就是提供对外接口的,所以一般不会定义为私有)*/

-(void)test;

@end

@implementationTest

/*私有实例方法*/

-(void)test{

NSLog(@"这是个私有实例方法!");

}

@end

利用KVC和runtime暴力访问私有属性和变量

KVC访问变量不受私有权限的限制,访问代码如下:

#import"Test.h"

#import<objc/runtime.h>

...

Test*test=[[Testalloc]ink];

/*1.KVO暴力访问私有属性和私有变量*/

[testsetValue:@"albert"forKeyPath:@"name"];

NSString*pname=[testvalueForKey:@"name"];

[testsetValue:@"mathmetics"forKeyPath:@"major"];

NSString*pmajor=[testvalueForKey:@"major"];

NSLog(@"pname:%@pmajor:%@",pname,pmajor);

程序的运行结果为:

2017-04-0211:58:56.255723CommandLine[99973:3239125]pname:albertpmajor:mathmetics

运行时可以使用class_copyIvarList函数获取类对象的Ivar变量列表,然后可使用object_getIvar和object_setIvar运行时函数对变量进行暴力访问。

#import"Test.h"

#import<objc/runtime.h>

...

Test*test=[[Testalloc]init];

/*2.运行时暴力访问私有属性和私有变量(ARC)*/

/*获取实例变量列表*/

unsignedintcount=0;

Ivar*members=class_copyIvarList([Testclass],&count);

/*打印所有的变量名及其类型*/

for(inti=0;i<count;i++){

constchar*memberName=ivar_getName(members[i]);

constchar*memberType=ivar_getTypeEncoding(members[i]);

NSLog(@"name:%stype:%s",memberName,memberType);

}

/*访问私有属性和变量*/

Ivarv

温馨提示

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

评论

0/150

提交评论