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

下载本文档

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

文档简介

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

如何查看设备应用的crash日志?正确答案:对于一般可重现的bug,可以通过再次运行应用找到问题所在。但是对于某些偶尔出现的错误,尤其是内存错误就很难定位了(江南博哥),因为再次运行可能错误不会再次出现,所以需要通过查看crash日志,来定位当时发生错误的位置。

crash日志可以分成两种:一种是应用发布AppStore后,用户的crash日志会上传,开发者可以在iTunesConnect上查看,但前提是用户在手机设置中开启了“诊断与用量”选项,用户同意分享应用崩溃日志等信息给开发者(具体位置位于:设置→隐私→诊断与用量)。

另一种是开发者在开发测试时查看crash日志信息。查看测试过程中的日志也有多种途径,例如运行时可以连接Mac(模拟器或真机调试)在本地目录中查看:~/Library/Logs/CrashReporter/MobileDevice/设备名称/。日志文件扩展名为.crash或.ips,如图所示。

日志文件

还有主要是在Xcode中查看日志,位置位于Xcode中Window导航栏下的Devices选项,打开会有模拟器和真机的设备列表。选中设备,在有日志的情况下会有ViewDeviceLogs选项,如图所示。

Xcode中查看日志

2.

什么是消息推送?和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,如果一致,那么将消息推送到客户端。

3.

如何在Category中增加属性(关联对象)?正确答案:在实际开发中,如果为Category添加一个属性,那么系统将不会为这个属性设置访问器方法,也就是setter和getter方法。这时候可以使用runtime提供的关联对象方法,动态地为该属性实现访问器方法。

开发者可以将关联对象想象成一个Objective-C对象,这个对象通过一个预先设置好的key连接到类的一个实例上。runtime提供了如下方法让一个对象连接到其他对象。

voidobjc_setAssociatedObject(idobject,constvoid*key,

idvalue,objc_AssociationPolicypoliey)

参数object是将要被关联的对象。参数key是一个void指针。参数value是关联对象,它是id类型。参数policy是指定一个内存管理策略来处理关联对象。如果指定的策略OBJC_ASSOCIATION_ASSIGN,那么被关联对象释放时,关联对象不会被释放,而如果指定的OBJC_ASSOCIATION_RETAIN或OBJC_ASSOCIATION_COPY,那么关联对象就会被释放。另外,还有OBJC_ASSOCIATION_RETAIN_NONATOMIC和OBJC_ASSOCIATIO_COPY_NONATOMIC两种策略,当需要在多个线程中处理访问关联对象的多线程代码时,就会变得非常有用。

除此之外,runtime还提供了移除关联对象的方法:

voidobjc_removeAssociatedObjects(idobject)

可以使用这个方法移除所有和参数object关联的对象,或者使用objc_setAssociatedObject函数将key指定的关联对象设置为nil。

下面的示例代码演示了如何为Person的Category添加一个属性。

/*为Person类的Category添加address属性*/

#import"Person.h"

@interfacePerson(Cate)

@property(nonatomic,strong)NSString*address;

@end

/*在.m文件中为address属性实现访问器方法*/

#import"Person+Cate.h"

#import<obje/objc-runtime.h>

@implementationPerson(Cate)

-(id)address{

idvalue=objc_getAssociatedObject(se!f,"address");

returnvalue;

}

-(void)setAddress:(NSString*)address{

objc_setAssociatedObject(self,"address",address,OBJC_ASSOCIATION_RETAIN);

}

@end

/*在控制器中打印属性值*/

Person*aperson=[[Personalloc]init];

aperson.address=@"China";

NSLog(@"address=%@",aperson.address);

程序的打印结果如下:

2016-11-0319:17:40.94901[2756:152639]aaddress=China

结果说明已经成功地在Category中为类添加了一个新的属性,并且能够正常地使用这个属性。事实上,关联对象是使用哈希表实现的,将一个类映射到一张哈希表上,然后根据key找到关联对象,所以严格来说,关联对象和被关联对象没有任何关系,它不是存储在对象的内部。

4.

Objective-C中的可变和不可变类型是什么?正确答案:Objective-C中的mutable和immutable类型对应于动态空间分配和静态空间分配。最常见的例子是数组和字典。例如NSArray和NSMutableArray,前者为静态数组(不可变数组),初始化后长度固定,不可以再动态添加新元素改变数组长度;后者为动态数组(可变数组),可以使用addObject方法动态添加或者使用removeObject方法删除元素,动态申请新的空间或释放不需要的空间,伸缩数组长度。

/*不可变数组,初始化后不可添加或删除数组元素*/

NSArray*array=[[NSArrayalloc]initWithObjects:@1,@2,@3,nil];

/*可变数组,初始化后可以继续添加或删除数组元素*/

NSMutableArray*mulArray=[[NSMutableArrayalloc]initWithObjects:@4,@5,@6,nil];

[mulArrayaddObject:@7];

[mulArrayremoveObjectAtIndex:0];

[mulArrayremoveObject:@5];

5.

如何使用NSUserDefault偏好设置保存数据?正确答案:NSUserDefault是iOS系统提供的一个单例类,通过它可以将用户的偏好设置保存到应用包的plist文件中,同时也能读取保存的数据。通过类方法+standardUserDefaults可以获得NSUserDefault单例。

NSUserDefaults*userDefault=[NSUserDefaultsstandardUserDefaults];

NSUserDefault单例以key-value的形式存储数据,key是名称,value是相应的数据。使用-setObject:forKey:和-objectForKey:来存/取数据。下面的示例演示了一般的存取操作。

-(void)userDefaultSave{

NSUserDefaults*defaults=[NSUserDefaultsstandardUserDefaults];

[defaultssetObject:@"jack"forKey:@"name"];

[defaultssetInteger:10forKey:@"age"];

/*下面的代码可以省略,这里只是作为演示*/

if([defaultssynchronize]){

NSLog(@"保存成功");

}

}

-(void)userDefaultGet{

NSUserDefaults*defaults=[NSUserDefaultsstandardUserDefaults];

NSString*name=[defaultsobjectForKey:@"name"];

NSIntegerage=[defaultsintegerForKey:@"age"];

NSLog(@"name=%@,age=%zd",name,age);

}

需要注意的是,默认情况下NSUserDefaults只能存储基本对象类型(如NSData、NSString等类型)和基本数据类型,直接保存自定义对象将会抛出类似如下的异常。

Terminatingappduetouncaughtexception'NSInvalidArgumentException',reason:'Attempttoinsertnon-propertylistobject<UIImage:0x608000092d40>,{400,250}forkeyimg'

此时需要自定义类遵守NSCoding协议并实现协议方法进行编码与反编码,再通过NSKevedUnarchiver类将自定义对象转为NSData对象,之后才能使用NSUserDefaults进行存储。简单来说,对于自定义类对象,必须要通过一些方式将其转化为基本类型,即遵循NSCoding协议。下面的示例展示了如何保存UIImage对象。

-(void)userDefaultSaveImage{

UIImage*img=[UIImageimageNamed:@"1"];

NSUserDefaults*defaults=[NSUserDefaultsstandardUserDefaults];

/*直接保存将产生错误:[defaultssetObject:imgforKey:@"img"];*/

/*转为NSData对象*/

NSData*data=UIImageJPEGRepresentation(img,1);

[defaultssetObject:dataforKey:@"img"];

}

-(void)userDefaultGetImage{

NSUserDefaults*defaults=[NSUserDefaultsstandardUserDefaults];

/*转为NSData对象*/

NSData*data=[defaultsobjectForKey:@"img"];

/*转为UIImage对象*/

UIImage*img=[UIImageimageWithData:data];

self.view.layer.contents=(id)img.CGImage;

}

6.

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

字体效果

7.

什么是谓词?正确答案:谓词(NSPredicate)是Objective-C中针对数据集合的一种逻辑筛选条件,它类似于数据库中SQL语句对数据筛选的限制约束条件。Objective-C中的谓词经常用来从数组(Array)、集合(Set)等数据集合中筛选数据元素,谓词约束条件封装在NSPredicate对象中,可通过类函数predicateWithFormat和逻辑约束语句进行初始化。

这里以用谓词从对象数组筛选对象为例展示谓词的基本使用方法,谓词对象使用基本的逻辑约束格式化字符串来初始化。假设有一个简单的表(见表)结构有name和age两个字段:一个简单的表nameageAmy9Lily10Sam12Eric18

谓词筛选用法的示例代码如下:

/*其中,Person是一个简单的数据模型,它有name和age两个属性*/

/*数据源*/

NSArray*objectArray=[[NSArrayalloc]initWithObjects:

[PersonpersonWithName:@"Amy"age:9],

[PersonpersonWithName:@"Lily"age:10],

[PersonpersonWithName:@"Sam"age:12],

[PersonpersonWithName:@"Eric"age:18],

nil];

/*谓词逻辑约束对象*/

NSPredicate*predicate=[NSPredicatepredicateWithFormat:@"age>10"];

/*筛选数据(结果为Sam,Eric)*/

NSArray*newArray=[objectArrayfilteredArrayUsingPredicate:predicate];

上面的逻辑约束格式化字符串@"age>10"指筛选对象中属性age大于10的所有对象,格式化字符串可以被看作三部分:左手表达式、逻辑符号和右手表达式。其中,左手表达式是一个对象的属性键值(键路径);逻辑符号是一个基本的逻辑运算符;右手表达式是约束范围。

逻辑运算符还有很多,它们与SQL语句中的语法基本相对应,除了最基本的逻辑运算符:>、==、<=、&&等之外,还有逻辑词IN、CONTAINS、like等。它们的使用情况如下:

/*1.IN:名字为Sam或者Eric的对象*/

NSPredicate*predicate=[NSPredicatepredicateWithFormat:@"nameIN{'Sam','Eric'}"];

1*2.&&:年龄大于10,并且年龄小于20的对象(结果为Sam,Eric)*/

NSPredicate*predicate=[NSPredicatepredicateWithFormat:@"age>10&&age<20"];

/*3.CONTAINS:名字里有小写字母a的对象(结果为Sam)*/

NSPredicate*predicate=[NSPredicatepredicateWithFormat:@"nameCONTAINS'a'"];

/*4.like:正则匹配,?表示一个个占位符,*表示任意匹配*/

/*名字第三个字母为m的对象(结果为Sam)*/

NSPredicate*predicate=[NSPredicatepredicateWithFormat:@"namelike'??m'"];

/*名字里有小写字母a的对象(结果为Sam)*/

NSPredicate*predicate=[NSPredicatepredicateWithFormat:@"namelike'*a*'"];

8.

什么是“懒加载”?正确答案:“懒加载”(Lazyloading)也被叫作“延迟加载”,它的核心思想是把对象的实例化尽量延迟,直到真正用到的时候才将其实例化,这样做的好处是可以减轻大量对象在实例化时对资源的消耗,而不是在程序初始化的时候就预先将对象实例化。另外,“懒加载”可以将对象的实例化代码从初始化方法中独立出来,从而提高代码的可读性,以便于代码能够更好地被组织。

最典型的一个应用“懒加载”的例子是在对象的getter方法中实例化对象的时候。例如getter方法被重写,使得在第一次调用getter方法时才实例化对象并将实例化的对象返回。判断是否是第一次调用getter方法可以通过判断对象是否为空来实现。“懒加载”的getter方法的实现模板如下:

/*getter*/

-(NSObject*)object{

if(!_bject){

_object=[[NSObjectalloc]init];

}

return_object;

}

这种实现方法的缺点是使得getter方法产生副作用,也就是破坏了getter方法的纯洁性。因为按照约定和习惯,getter方法就是作为接口简单地将需要的实例对象返回给外部,这里对getter方法的第一次调用添加了懒加载模式,在使用者不知情的情况下会有潜在的隐患。

9.

CGAffineTransform和CATransform3D分别有什么作用?正确答案:CGAffineTransform被称为“仿射变换”,它被定义在CoreGraphics框架中,主要用于在二维平面对视图进行旋转、缩放和平移。事实上,CGAffineTransform是一个可以和二维空间向量(如CGPoint)做乘法的3×2的矩阵。CGAffineTransform中“仿射”的意思是无论使用什么值的变换矩阵,图层中平行的两条线在变换之后仍然会保持平行。

CoreGraphics框架提供了一系列函数来创建CGAffineTransform实例,主要有以下几种:

/*平移*/

CGAffineTransformMakeTranslation(CGFloattx,CGFloatty)

/*缩放*/

GAffineTransformMakeScale(CGFloatsx,CGFloatsy)

/*旋转*/

CGAffineTransformMakeRotation(CGFloatangle)

UIView中的transform属性或者CALayer中的affineTransform属性都是CGAffineTransform类型,可以使用它们对视图或者图层进行仿射变换。此外,CoreGraphics框架还提供了一些可以进行混合变换的函数,能够在一个变化的基础上做更深层次的变换,甚至可以将两个已经存在的变换矩阵进行合并。如果要做多次变换的操作,那么这些函数就会非常有用。下面用例子来演示一个包含缩放、旋转和移动的变换,示例代码如下:

-(void)viewDidLoad{

[superviewDidLoad];

_searchImageView=[[UIImageViewalloc]initWithFrame:CGRectMake(([UIScreenmainSereen].bounds.size:width-200)*0.5,([UIScreenmainScreen].bounds.size:height-200)*0.5,200,200)];

_searchImageView.image=[UIImageimageNamed:@"马"];

[self.viewaddSubview:_searchImageView];

}

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

/*创建一个空的CGAffineTransform*/

CGAffineTransformtransform=CGAffineTransformIdentity;

/*缩放*/

transform=CGAffineTransformScale(transform,0.5,0.5);

/*旋转*/

transform=CGAffineTransformRotate(transform,M_PI_4);

/*平移*/

transform=CGAffineTransformTranslate(transform,100,0);

_searchImageView.transform=transform;

}

CATransform3D是CoreAnimation结构体。和CGAffineTransform一样,它也是一个矩阵。CATransform3D主要用来做更复杂的关于CALayer的3D操作。CoreAnimation提供了一系列方法来创建和组合CATransform3D类型的矩阵,这些函数和CGAffineTransform类似,只是多了一个z参数,并且旋转函数除了angle之外还多出了x、y、z3个参数,这些参数分别决定了每个坐标轴方向上的旋转。主要函数如下:

/*3D平移*/

CATransform3DMakeTranslation(CGFloattx,CGFloatty,CGFloattz)

/*3D旋转*/

CATransform3DMakeRotation(CGFloatangle,CGFloatx,CGFloaty,CGFloatz)

/*3D缩放*/

CATransform3DMakeScale(CGFloatsx,CGFloatsy,CGFloatsz)

此外可以通过CATransform3D中的m34元素来控制3D变换的透视效果。m34元素主要用于按比例缩放X和Y的值来计算远离视角的距离。m34的默认值是0,可以通过设置其为-1/d来应用透视效果,d代表了视角和屏幕之间的距离,d的值越小,透视效果越强,一般设置为500~1000。示例代码如下:

-(void)viewDidLoad{

[superviewDidLoad];

_searchImageView=[UIImageViewalloc]initWithFrame:CGRectMake(([UIScreenmainScreen].bounds.size.width-200)*0.5,([UIScreenmainScreen].bounds.size.height-200)*0.5,200,200)];

_searchImageView.image=[UIImageimageNamed:@"马"];

[self.viewaddSubview:_searchImageView];

/*创建一个空的CATransform3D*/

CATransform3Dtransform=CATransform3DIdentity;

/*设置m34元素,增强透视效果*/

transform.m34=-1.0/500.0;

/*3D旋转变换*/

transform=CATransform3DRotate(transform,M_PI_4,0,1,0):

_searchImageView.layer:transform=transform;

}

10.

如何实现autorealeasepool?正确答案:autorealeasepool(自动释放池)其实并没有其自身的结构,它是基于多个AutoreleasePoolPage(一个C++类)以双向链表组合起来的结构,其基本操作都是简单封装了AutoreleasePoolPage的操作方法。例如,可以通过push操作添加对象,或者通过pop操作弹出对象,以及通过release操作释放销毁对象,对应的3个封装后的操作函数为:objc_autoreleasepoolPush、objc_autoreleasepoolPop和objc_autorelease。自动释放池将用完的对象集中起来,统一释放,起到延迟释放对象的作用。

自动释放池存储于内存中的栈上,释放池之间遵循“先进后出”原则。例如下面代码所示的释放池嵌套。

/*释放池1*/

@autoreleasepool{

People*person1=[[[Personalloc]init]autoretease];

/*释放池2*/

@autoreleasepool{

People*person2=[[[Personalloc]init]autorelease];

}

People*person3=[[[Personalloc]init]autorelease];

}

代码中释放池1和释放池2在内存中的结构如图所示,释放池1先入栈,后出栈;释放池2后入栈,先出栈。person2对象在释放池2中,会被先释放;person1和person3在释放池1中,会后被释放。

释放池内存结构

自动释放池背后具体的实现机制比较复杂和巧妙,具体细节感兴趣的读者可以阅读下面这篇博文中的讨论:/2014/10/15/behind-autorelease/

11.

Git和SVN有什么异同?正确答案:Git和SVN都是用来对工程进行版本控制的,它们可以监控工程代码或资源等文件的更改变化,保证正确的内容提交或者撤销恢复到之前的工程版本,有利于实现高效的团队合作。

Git和SVN的主要不同之处在于它们的架构原理。简单地说,Git是分布式的,而SVN是集中式的。当使用Git时,每个开发者要清楚本地仓库和远程仓库的概念,由于Git是分布式的,所以每个开发者建的本地仓库都保存了整个工程的完整备份,而且与SVN不同的是,使用Git可以先在本地提交,在需要的时候再提交到远程仓库从而推送给其他开发者看到,提交的过程即是同步的过程。也就是说,Git允许本地仓库脱离远程服务器仓库进行本地工程的版本控制,而使用SVN每次提交时,都要和中心服务器仓库进行同步。

由于两者架构的不同,所以Git和SVN中的分支(Branch)概念也是不同的。分支在SVN中是一个完整的目录,包含所有的实际文件,和中心仓库是保持同步的,如果某个团队成员创建新的分支,那么会同步到所有的成员版本中,所有人都会受影响,即牵一发而动全身。而在Git下成员创建分支在合并前是不会影响任何人的,创建分支后可以在本地脱机进行任何操作,测试无误之后再提交合并到主分支,然后其他成员才能拉取看到。由此可见Git的优势是很明显的。

另外,Git是把工程的内容按照元数据的方式存储,而SVN是把工程内容按照文件的方式存储。Git工程目录往往比SVN目录要大,因为Git目录中包含远程仓库所有的数据,如版本记录、标签和分支等。

Git的整体结构示意图如图所示。

Git的整体结构示意图

12.

如何理解MVVM设计模式?正确答案:随着业务规模的不断扩大,业务逻辑也越来越复杂,这使得Controller中的任务越来越繁重,传统的MVC架构已经很难满足低耦合、高内聚的设计要求。在这样的背景下,MVVM(Model-View-ViewModel)诞生了。MVVM是由微软公司提出的一种新的设计架构,它基于MVC架构,其特点是在View和Model之间多加了一层ViewModel来实现数据的绑定(data-binding),从而很好地解决了MVC中Controller过于臃肿的问题。下图展示了MVVM设计模式的结构。

MVVM设计模式结构

MVVM中的ViewModel有以下几个特点:

1)ViewModel是有状态的。ViewModel有自己的属性,还会持有Model对象。

2)ViewModel与UI控件的无关性。ViewModel并不关心UI控件的相关逻辑,只关心自己的数据处理逻辑。

3)易于单元测试。以往的Controller过于复杂,无法进行单元测试,而ViewModel测试起来简单很多。

4)ViewModel可以抽离出来做转换器给其他项目使用,从而最大程度上实现了代码的复用。

MVVM设计模式的目的是帮助MVC设计模式中的(Sontroiler瘦身,将数据加工的任务从Controller中解放了出来,使得Controller只需要专注于业务分配的工作,让MVVM中的ViewModel负责Model与View之间的通信,并完成通信间的额外操作,如数据转换、字符拼接等操作。因此,ViewModel经常作为转换器使用,从而提高了代码的复用性。ViewModel还能帮助Controller完成复杂的网络请求逻辑,从而大大降低了Controller的复杂度。这里需要强调的是,ViewModel具有独立性,它并不关心UI的业务逻辑,也不持有任何UI对象,只关心自己的数据处理逻辑是否正确。很多初学者不清楚ViewModel的用法,往往会错误地将UI对象当作ViewModel的属性或者将UI对象的操作放入ViewModel的方法中,这些做法都是不正确的,没有正真理解MVVM的含义。尽管MVVM带来了很多好处,降低了代码的耦合度和复杂度,但它往往要写更多的代码来实现一个功能,同时还增加了工程的规模,使得工程中的目录比以前要稍多一些,不易查找文件。MVVM并不是iOS开发中的“银弹”,没有那种方法能完全解决软件开发中的问题,但相对于MVC来说,MVVM无疑是一个更好的选择。

13.

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的子类。

14.

什么是SEL?正确答案:SEL又称选择器,表示的是一个方法的selector的指针。在很多方法名中都可以看到,例如UIControl.h中事件的监听方法:

-(void)addTarget:(nullableid)targetaction:(SEL)actionforControlEvents:(UIControlEvents)controlEvents;

其中,参数action就是SEL类型。SEL的定义如下:

typedefstructobjc_selector*SEL;

方法的selector用于表示运行时方法的名字。Objective-C在编译时,会根据每个方法的名字、参数列表,生成一个唯一的整型标识,这个标识就是SEL。正因为其具有唯一性,所以在Objective-C的同一个类中,不能存在两个同名的方法,即使参数类型不同也不行。

开发者可以在运行时添加新的selector,也可以在运行时获取已存在的selector。可以通过下面3种方法来获取SEL。

/*在运行时注册一个方法,返回一个SEL指针*/

SELsel_registerName(constchar*str)

/*编译器提供的方法*/

@selector(selector)

/*通过字符串获取SEL*/

NSSelectorFromString(NSString*aSelectorName);

事实上,工程中所有的SEL会组成一个Set集合,Set的特点就是具有唯一性,因此SEL也是唯一的。如果想要查找某个方法,那么只需要找到这个方法所对应的SEL就可以了,SEL实际上就是根据方法名Hash转换的一个字符串。

15.

相对于objective-C而言,Swift有什么新特性?正确答案:Swift是一门新型语言,它借鉴了Haskell、Ruby、Python、C#等语言特性,看上去偏脚本化。Swift仍然支持已有的Cocoa和CocoaTouch框架。

Swift的主要新特性如下:

1)安全:有严格的类型检查。

2)强大:有高度优化的LLVM编译器。

3)新型:Swift借鉴多种语言特性,表达更简单精确。

Swift与Objective-C和C/C++的基本对比见表。C/C++、objective-C和Swift三者基本对比

C/C++Objective-CSwift库引入#include<stdio.h>#import<Foundation/Foundation.h>importFoundation头文件#include“Person.h”#import“Person.h”无常量定义#defineSPEED1.0#defineSPEED1.0letSPEED=1.0成员变量声明intage;intage;varage:Int类方法声明staticvoidspeak();+(void)speak();classfuncspeak(){...}实例方法声明intspeak();-(int)speak();funcspeak(){...}动态内存申请Person*person=malloc(sizeof(Person));Person*person=newPerson;Person*person=[Personalloc];Varperson=Person()类方法调用Person::speak();[Personspeak];Person.speak()实例方法调用Person->speak();[personspeak]Person.speak()字符串“String”@“String”“String”

1.从基本的ViewController代码窥探Objective-C和Swift的区别

(1)Swift

/*ViewController.swift*|

importUIKit

classViewController:UIViewController{

@IBOutletweakvarlabel1:UILabel!

@IBActionfuncbutton1(sender:AnyObject){

label1.text="HelloiOS!!!"

}

overridefuncviewDidLoad(){

super.viewDidLoad()

//Doanyadditionalsetupafterloadingtheview,typicallyfromanib.

}

overridefuncdidReceiveMemoryWaming(){

super.didReceiveMemoryWarning()

//Disposeofanyresourcesthatcanberecreated.

}

(2)Objective-C

/*ViewController.h*/

#import<UIKit/UIKit.h>

@interfaceViewController:UIViewController

@property(weak,nonatomic)IBOutletUILabel*labell;

-(IBAction)buttonl:(id)sender;

@end

/*ViewController.m*/

#import"ViewController.h"

@interfaceViewController()

@end

@implementationViewController

@synthesizelabel1;

-(void)viewDidLoad{

[superviewDidLoad];

//Doanyadditionalsetupafterloadingtheview,typicallyfromanib.

}

-(void)didReceiveMemoryWaming{

[superdidReceiveMemoryWarning];

//Disposeofanyresourcesthatcanberecreated.

}

-(IBAction)button1:(id)sender{

label1.text=@"HelloiOS!!!";

}

@end

2.SwiR类的定义

整个类文件都定义在一个Swift文件内:

importFoundation

classBall{

/*变量*/

varcenterX:Float

varcenterY:Float

varradius:Float

/*初始化方法*/

init(centerX:Float,centerY:Float,radius:Float){

selfcenterX=centerX

self.centerY=centerY

self.radius=radius

}

/*实例方法*/

funcmove(moveX:Float,_moveY:Float){

self.centerX+=moveX

self.centerY+=moveY

}

/*类方法*/

classfuncaClassMethod(){

print("Iamaclassmethod")

}

}

...

/*创建对象*/

varball1=Ball(centerX:7.0,centerY:5.0,radius:6.0)

/*方法调用*/

ball1.move(moveX:1.0,1.0)

Ball.aClassMethod0

3.Objective-C和Swift语言中流程控制语句的比较

(1)Objective-C

/*条件判断*/

if(a<b){

//Dosomethinghere

}else{

//Doanotherthinghere

}

/*for循环*/

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

//Dosomethinghere

}

/*while循环*/

while(count<10){

//Dosomethinghere

}

/*do-while循环*/

do{

//Dosomcthinghere

}while(count<10);

(2)Swift

/*条件判断*/

ifa<b{

//Dosomethinghere

}else{

//Doanotherthinghere

}

/*for循环*/

forinti=0;i<10;i++{

//Dosomethinghere

}

/*while循环*/

whilecount<10{

//Dosomethinghere

}

/*repeat-while循环*/

repeat{

//Dosomethinghere

}whilecount<10

4.Objective-C和Swift语言中String字符串的对比

(1)Objective-C

NSString*Str=@"string";

NSString*formatStr=[NSStringstringWithFormat:@"%@andfloat%f",Str,3.1415"];

(2)Swift

/*可变字符串*/

varStr="string"

varStr:String="string"

varStr=String("string")

/*不可变字符串*/

letStr="string"

letStr:String="string"

letStr=String("string")

5.Objective-C和Swift语言中Array和MultableArray的对比

(1)Objective-C

/*静态数组*/

NSArray*array=[[NSArrayalloc]initWithObjects:ball1,ball2,nil];

array[0].radius=10;

/*可变数组*/

NSMutableArray*mArray=[[NSMutableArrayalloc]initWithCapacity:2];

[mArrayaddObject:ball1];

[mArrayaddObject:ball2];

Ball*newball=[mArrayobjectAtIndex:1];

[mArrayremoveObjectAtIndex:1];

(2)Swift

/*静态数组*/

letmyArray:Array<Ball>=[ball1,ball2]

letmyArray:[Ball]=[ball1,ball2]

letmyArray=[ball1,ball2]

myArray[0].radius=10

/*可变数组*/

vatmyArray:[Ball]=[]

myArray.append(ball1)

myArray.append(ball2)

varnewBall=myArray[1]

myArray.remove(at:0)

6.Objective-C和Swift语言中UIImageView的使用对比

(1)Objective-C

UIImageView*myImage=[[UIImageViewalloc]initWithImage:[UIImageimageNamed:@"tiger.png"]];

[self.viewaddSubview:myImage];

myImage.center=CGPointM

温馨提示

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

评论

0/150

提交评论