iOS面试题
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS面试题相关的知识,希望对你有一定的参考价值。
委托(代理)是一种设计模式,它允许类
或结构体
将一些需要它们负责的功能交由(委托)
给其他的类型的实例。
委托模式的实现很简单: 定义协议
来封装
那些需要被委托的函数和方法
,使其遵循者
拥有这些被委托的函数和方法
。
委托模式可以用来响应特定的动作或接收外部数据源提供的数据,而无需要知道外部数据源的所属类型(只要求外部数据源遵循
某协议)。
----------
从委托方法的实现来看,委托方法的实现是在另外一个类中完成,即委托类的内部暴露了,与我们一般坚持的隐藏实现原则相违背。
从委托类的定义可以看出,委托与协议有一定的相似性。都采用protocol关键字来声明,并且其中的方法都有optional和required,都需要对required方法和调用的optional方法进行实现。而不同的是在委托对象所在的类中需要定义一个delegate对象,并且为id类型。
但是delegate与protocol没有关系。Delegate本身应该称为一种设计模式,是把一个类自己需要做的一部分事情,让另一个类(也可以就是自己本身)来完成,而实际做事的类为delegate。而protocol是一种语法,它的主要目标是提供接口给遵守协议的类使用,而这种方式提供了一个很方便的、实现delegate模式的机会。
----------
委托的基本思想是:两个对象协同解决问题。一个对象非常普通,并且打算在广泛的情形中重用。它存储指向另一个对象(即它的委托)的引用,并在关键时刻给委托发消息。消息可能只是通知委托发生了某件事情,给委托提供机会执行额外的处理,或者消息可能要求委托提供一些关键的信息以控制所发生的事情。
~~~extension 扩展
即便无法修改源代码,依然可以通过扩展(Extension)
来扩充已存在类型(译者注: 类,结构体,枚举等)。扩展
可以为已存在的类型添加属性
,方法
,下标脚本
,协议
等成员。
当一个类型已经实现了协议中的所有要求,却没有声明时,可以通过扩展
来补充协议声明。
[email protected] (nonatomic, assign) NSString *title;
nonatomic:非原子性访问,对属性赋值的时候不加锁,多线程并发访问会提高性能。如果不加此属性,则默认是两个访问方法都为原子型事务访问。
atomic是Objc使用的一种线程保护技术,基本上来讲,是防止在写未完成的时候被另外一个线程读取,造成数据错误。而这种机制是耗费系统资源的,所以在iPhone这种小型设备上,如果没有使用多线程间的通讯编程,那么nonatomic是一个非常好的选择。
~~~深拷贝 && 浅拷贝
(1)OC
assign:简单赋值,不更改索引计数,适用于基础数据类型(例如NSInteger,CGFloat)和C数据类型(int,float,double,char等)等简单数据类型,此标记说明设置器直接进行赋值,这也是默认值。在使用垃圾收集的应用程序中,如果你要一个属性使用assign,且这个类符合NSCopying协议,你就要明确指出这个标记,而不是简单的使用默认值,否则的话,你将得到一个编译警告。
retain:始终是浅复制。引用计数每次加一。返回对象是否可变与被复制的对象保持一致。
copy:对于可变对象为深复制,引用计数不改变;对于不可变对象是浅复制,引用计数每次加一。始终返回一个不可变对象。
mutableCopy:始终是深复制,引用计数不改变。始终返回一个可变对象。
----------
不可变对象:值发生改变,其内存首地址随之改变。
可变对象:无论值是否改变,其内存首地址都不随之改变。
引用计数:为了让使用者清楚的知道,该对象有多少个拥有者(即有多少个指针指向同一内存地址)。
----------
类型转换:
1. 不可变对象→可变对象的转换:mutableCopy
2. 可变对象→不可变对象的转换:copy
3. 可变对象→可变对象的转换(不同指针变量指向不同的内存地址):mutableCopy
4. 同类型对象之间的指针复制(不同指针变量指向同一块内存地址):retain / copy
通俗的讲,多个指针同时指向同一块内存区域,那么这些个指针同时拥有对该内存区的所有权。所有权的瓜分过程,这时候就要用到浅拷贝了。
----------
问:什么时候用到深浅拷贝?
答:深拷贝是在要将一个对象从可变(不可变)转为不可变(可变)或者将一个对象内容克隆一份时用到;浅拷贝是在要复制一个对象的指针时用到。
(2)Swift http://blog.csdn.net/hello_hwc/article/details/50102329
使用赋值符号的时候发生的都是copy。值类型或者引用类型在赋值的时候都是Copy,值类型拷贝累实际的内存(value),而引用类型只是拷贝了指针,仍然指向最开始的内存区域。
Class的实例是引用类型,Swift方法类型是引用类型(在Swift中,方法也是一种类型)。其余的都是值类型,像Array,Dictionary本质都是Struct。
----------
对于不可变类型:copy 是浅拷贝,只拷贝指针。mutableCopy 是深拷贝,拷贝了value。
对于可变类型:copy 深拷贝,拷贝了value。mutableCopy 是深拷贝,拷贝了value。
对于不可变集合:对于集合本身,Copy只是拷贝了指针,指针仍然指向最初的Array对象。对于集合本身,MutableCopy拷贝了value。对于集合中存储的对象,不管是copy还是mutableCopy,都是浅拷贝。
对于可变集合:对于集合本身,Copy拷贝了value。对于集合本身,MutableCopy拷贝了value。对于集合中存储的对象,不管是copy还是mutableCopy,都是浅拷贝。
----------
对于深拷贝,必须实现NSCopying
协议,通过copyWithZone()
方法来实现对象的克隆。
~~~NSNotification
如果在一个类中想要执行另一个类中的方法可以使用通知。
NSNotification类可以理解为一个消息对象,其中有三个成员变量。
(1)@property (readonly, copy) NSString *name; 这个成员变量是这个消息对象的唯一标识,用于辨别消息对象。
(2)@property (readonly, retain) id object; 这个成员变量定义一个对象,可以理解为针对某一个对象的消息。
(3)@property (readonly, copy) NSDictionary *userInfo; 这个成员变量是一个字典,可以用其来进行传值。
创建一个通知对象:使用notificationWithName:object: 或者 notificationWithName:object:userInfo:
NSNotification* notification = [NSNotification notificationWithName:kImageNotificationLoadFailed(connection.imageURL)
object:self
userInfo:[NSDictionary dictionaryWithObjectsAndKeys:error,@"error",connection.imageURL,@"imageURL",nil]];
----------
NSNotificationCenter
这个类是一个通知中心,使用单例设计,每个应用程序都会有一个默认的通知中心。用于调度通知的发送和接受。
(1)注册通知:addObserver:selector:name:object: 添加一个观察者,可以为它指定一个方法,名字和对象。接受到通知时,执行方法。
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(aWindowBecameMain:)
name:NSWindowDidBecomeMainNotification object:(id)anObject];
(2)发送通知:postNotificationName:object:或者performSelectorOnMainThread:withObject:waitUntilDone:
[[NSNotificationCenter defaultCenter]
postNotificationName:@"ConverterAdded" object:self];
- (void)postNotificationName:(NSString *)aName object:(id)anObject userInfo:(NSDictionary *)aUserInfo;
(3)移除通知:removeObserver:和removeObserver:name:object:
其中,removeObserver:是删除通知中心保存的调度表一个观察者的所有入口,而removeObserver:name:object:是删除匹配了通知中心保存的调度表中观察者的一个入口。
[[NSNotificationCenter defaultCenter] removeObserver:observer name:nil object:self]; 注意参数notificationObserver为要删除的观察者,一定不能置为nil。
- (void)removeObserver:(id)observer name:(NSString *)aName object:(id)anObject;
----------
观察者收到通知的顺序是没有定义的。通知发出和观察的对象有可能是一样的。通知中心同步转发通知给观察者,就是说postNotification方法直到接收并处理完通知才返回值。ios里的事件广播机制是同步的,默认情况下,广播一个通知,会阻塞后面的代码。
要想异步的发送通知,可以使用NSNotificationQueue。在多线程编程中,通知一般是在一个发出通知的那个线程中转发,但也可能是不在同一个线程中转发通知。
如果发送的通知指定了object对象,那么观察者接收的通知设置的object对象与其一样,才会接收到通知,但是接收通知如果将这个参数设置为了nil,则会接收一切通知。
观察者的SELF函数指针可以有一个参数,参数就是发送的消息对象本身,可以通过这个参数取到消息对象的userInfo,实现传值。
例:
首先,我们在需要接收通知的地方注册观察者,比如: //获取通知中心单例对象 NSNotificationCenter * center = [NSNotificationCenter defaultCenter]; //添加当前类对象为一个观察者,name和object设置为nil,表示接收一切通知 [center addObserver:self selector:@selector(notice:) name:@"123" object:nil]; 之后,在我们需要时发送通知消息: //创建一个消息对象 NSNotification * notice = [NSNotification notificationWithName:@"123" object:nil userInfo:@{@"1":@"123"}]; //发送消息 [[NSNotificationCenter defaultCenter]postNotification:notice]; 我们可以在回调的函数中取到userInfo内容,如下: -(void)notice:(id)sender{ NSLog(@"%@",sender); }
打印结果如下:
~~~weak && strong
weak和strong不同的是:当一个对象不再有strong类型的指针指向它的时候,它会被释放,即使还有weak型指针指向它。
一旦最后一个strong型指针离去 ,这个对象将被释放,所有剩余的weak型指针都将被清除。
strong就相当于retain属性,而weak相当于assign。
默认是strong,只有一种情况需要使用weak,就是为了避免retain cycles(就是父类中含有子类{父类retain了子类},子类中又调用了父类{子类又retain了父类},这样都无法内存无法release。
IBOutlet可以为weak,Delegate一般为weak。一般来说,类“内部”的属性设置为strong,类“外部”的属性设置为weak。说到底是一个归属权的问题。
~~~IBOutlet && weak
From a practical perspective, in iOS and OS X outlets should be defined as declared properties. Outlets should generally be weak, except for those from File’s Owner to top-level objects in a nib file (or, in iOS, a storyboard scene) which should be strong. Outlets that you create should will therefore typically be weak by default, because:
Outlets that you create to, for example, subviews of a view controller’s view or a window controller’s window, are arbitrary references between objects that do not imply ownership.
The strong outlets are frequently specified by framework classes (for example, UIViewController’s view outlet, or NSWindowController’s window outlet).
如果IBOutlet对象是nib/sb scene的拥有者(File’s owner)所持有的对象,那么很显然拥有者必须“拥有”对象的指针,因此属性应设置为strong。而其他的IBOutlet对象的属性需要设置为weak,因为拥有者并不需要“拥有”他们的指针。举例来说,UIViewController的view属性是strong,因为controller要直接拥有view。而添加到view上的subviews,作为IBOutlet只需要设置为weak就可以了,因为他们不是controller直接拥有的。直接拥有subviews的是controller的view,ARC会帮助管理内存。
Outlets should be changed to strong
when the outlet should be considered to own the referenced object:
- As indicated previously, this is often the case with File’s Owner—top level objects in a nib file are frequently considered to be owned by the File’s Owner.
- You may in some situations need an object from a nib file to exist outside of its original container. For example, you might have an outlet for a view that can be temporarily removed from its initial view hierarchy and must therefore be maintained independently.
第二种就是controller需要直接控制某一个subview并且将subview添加到其他的view tree上去。
通过加载xib得到的用户界面,在加载时就已经是view hierarchy的一部分了,后者中的指向都是strong的,因此IBOut指向的对象不该再被hold一次,从而不需要在viewDidUnload方法里再将IBOut指向的对象置为nil。
~~~ARC(Automatic Reference Counting)
简单的说,就是代码中自动加入了retain/release,原先需要手动添加的用来处理内存管理的引用计数的代码可以自动地由编译器完成了。
该机制在iOS5/MacOSX10.7开始导入,利用Xcode4.2以后可以使用该特性。不用ARC的话就会看到很多retain。
~~~#import && #include
一般来说,导入objective c的头文件时用#import,包含c/c++头文件时用#include。如果不是c/c++,尽量用#import。
#import 确定一个文件只能被导入一次,这使你在递归包含中不会出现问题。#import比起#include的好处就是不会引起交叉编译。
~~~#import && @class
import会包含这个类的所有信息,包括实体变量和方法(.h文件中),而@class只是告诉编译器,其后面声明的名称是类的名称,至于这些类是如何定义的,后面会再告诉你。
@class一般用于头文件中需要声明该类的某个实例变量的时候用到,在.m文件中还是需要使用#import。
在头文件中, 一般只需要知道被引用的类的名称就可以了。 不需要知道其内部的实体变量和方法,所以在头文件中一般使用@class来声明这个名称是类的名称。 而在实现类里面,因为会用到这个引用类的内部的实体变量和方法,所以需要使用#import来包含这个被引用类的头文件。
在编译效率方面考虑,如果你有100个头文件都#import了同一个头文件,或者这些文件是依次引用的,如A–>B, B–>C, C–>D这样的引用关系。当最开始的那个头文件有变化的话,后面所有引用它的类都需要重新编译,如果你的类有很多的话,这将耗费大量的时间。而是用@class则不会。
如果有循环依赖关系,如:A–>B, B–>A这样的相互依赖关系,如果使用#import来相互包含,那么就会出现编译错误,如果使用@class在两个类的头文件中相互声明,则不会有编译错误出现。
----------
能在实现文件中#import,就不在头文件中#import。
能在头文件中@class+实现文件中#import,就不在头文件中#import。
UITableView通过重用单元格来达到节省内存的目的:为了做到显示和数据分离,IOS tableView的实现并且不是为每个数据项创建一个tableCell。而是只创建屏幕可显示最大个数的cell,然后重复使用这些cell,对cell做单独的显示配置。通过为每个单元格指定一个重用标识符(reuseIdentifier),即指定了单元格的种类,以及当单元格滚出屏幕时,允许恢复单元格以便重用。达到既不影响显示效果,又能充分节约内存的目的。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = [NSString stringWithFormat:@"Cell"]; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; } //config the cell return cell; }
----------
更新reusableTableCells表:
1. reloadData,这种情况比较特殊。一般是部分数据发生变化,需要重新刷新cell显示的内容时调用。在cellForRowAtIndexPath调用中,所有cell都是重用的。我估计reloadData调用后,把visiableCells中所有cell移入reusableTableCells,visiableCells清空。cellForRowAtIndexPath调用后,再把reuse的cell从reusableTableCells取出来,放入到visiableCells。
2. reloadRowsAtIndex,刷新指定的IndexPath。如果调用时reusableTableCells为空,那么cellForRowAtIndexPath调用后,是新创建cell,新的cell加入到visiableCells。老的cell移出visiableCells,加入到reusableTableCells。于是,之后的刷新就有cell做reuse了。
----------
为什么明明已经创建够用的cell了,在上下拖过多次后,有的时候上下拖的时候只要拖的快了,还是会再创建新的cell?
答:Cell加载的过程是,先寻找可重用的Cell,取出并且加入到显示队列。然后再更新可重用Cell列表。所以,拖动太快时很有可能可重用Cell列表还没有更新,并且可重用Cell的个数已经为0了。这时候因为找不到reuse Cell,就会进行创建。
//定义ClassA以及其methodA @interface ClassA : NSObject { } -(void)methodA; @end //定义ClassB以及其methodB @interface ClassB : NSObject { } -(void)methodB; @end //定义ClassC以及其需要的methodA,methodB @interface ClassC : NSObject { ClassA *a; ClassB *b; } -(id)initWithA:(ClassA *)A b:(ClassB *)B; -(void)methodA; -(void)methodB; @end //注意在ClassC的实现 @implementation ClassC -(id)initWithA:(ClassA *)A b:(ClassB *)B{ a=[[ClassA alloc] initWithClassA: A];//[A copy]; b=[[ClassB alloc] initWithClassB: B];//[B copy]; } -(void)methodA{ [a methodA]; } -(void)methodB{ [b methodB]; }
~~~plist
http://blog.csdn.net/totogo2010/article/details/7634185
Property List,属性列表文件,它是一种用来存储串行化后的对象的文件。属性列表文件的扩展名为.plist ,因此通常被称为plist文件。文件是xml格式的。Plist文件通常用于储存用户设置,也可以用于存储捆绑的信息。
读取数据:
- (void)viewDidLoad { [super viewDidLoad]; //读取plist NSString *plistPath = [[NSBundle mainBundle] pathForResource:@"plistdemo" ofType:@"plist"]; NSMutableDictionary *data = [[NSMutableDictionary alloc] initWithContentsOfFile:plistPath]; NSLog(@"%@", data);//直接打印数据。 }
~~~category && 继承 && extension
Category
1、类包含了很多个方法实现,而这些方法需要不同团队的成员来实现。把相关的方法分组到多个单独的文件中,对于大型而复杂的类,这有助于提高可维护性,并简化单个源文件的管理。
2、当你在使用基础类库中的类时,你不想继承这些类而只想添加一些方法。
----------
Category能实现上面的需求,当然也有使用Category需要注意的问题:
1、Category可以访问原始类的实例变量,但不能添加实例变量,如果想添加变量,那就通过继承创建子类来实现。
2、Category可以重载原始类的方法,但是不大推荐这么做,因为这样会覆盖掉原始类的方法。如果确实要重载,那就通过继承创建子类来实现。所以新扩展的方法与原方法同名时,不能使用category。如果Category中覆盖的那个方法已经在这个类的其它Category定义过了,则之前定义的方法将没有机会被程序调用。
3、和普通接口有所区别的是,在Category的实现文件中的实例方法只要你不去调用它,你可以不用实现所有声明的所有方法。
用于给class及其subclass添加新的方法。
有自己单独的 .h 和 .m 文件。
用于添加新方法,而不能添加新属性(property)。
Extension:
Extension常被称为是匿名的Category。
Extension可以给原始类添加新方法,以及新属性。
用于给类添加新方法,但只作用于原始类,不作用于subclass。
只能对有implementation源代码的类写Extension,对于没有implementation源代码的类,比如framework class,是不可以的。
//MyClass.h #import <Foundation/Foundation.h> @interface MyClass : NSObject -(void) myPrint; @end
//MyClass.m #import "MyClass.h" @implementation MyClass -(void) myPrint{ NSLog(@"myPrint 调用了"); } @end
有了上面的MyClass后,我们要在不增加子类,不修改MyClass类的情况下增加一个HelloWorld的方法,只需添加两个文件MyClass+HelloWorld.h 和 MyClass+HelloWorld.m。
在声明文件和实现文件中用"()"把Category的名称括起来。"原类名+Category"的这是约定的文件命名方式。
//MyClass+HelloWorld.h #import "MyClass.h" @interface MyClass (HelloWorld) -(void)HelloWorld; @end
//MyClass+HelloWorld.m #import "MyClass+HelloWorld.h" @implementation MyClass (HelloWorld) -(void)HelloWorld{ NSLog(@"你好 伦敦奥运!"); } @end
在main中调用:
MyClass *myclass = [[[MyClass alloc]init]autorelease];
[myclass HelloWorld];
[myclass myPrint];
~~~Bundle
Bundle简单地讲,就是一个内部结构按照标准规则组织的特殊目录。
iOS的应用都是通过bundle进行封装的,对应的bundle类型是Application类型,平时我们通过XCode编译出来的Target(即我们开发的应用),其实就是一个Application类型bundle,即一个文件夹。但是Finder会把这个bundle当做一个文件显示给我们,其实是因为这个bundle自身也是一个package,而Mac系统会把所有的package当做一个文件来对待,显示给用户,从而防止用户误操作导致程序文件损坏或丢失。
模态窗口只是视图控制器显示的一种方式(在iOS中并没有专门的模态窗口类),模态窗口不依赖于控制器容器(例如前两种视图切换一个依赖于UITabBarController,另一个依赖于UINavigationController),通常用于显示独立的内容,在模态窗口显示的时其他视图的内容无法进行操作。
模态窗口使用起来比较容易,一般的视图控制器只要调用- (void)presentViewController:(UIViewController *)viewControllerToPresent animated: (BOOL)flag completion:(void (^)(void))completion NS_AVAILABLE_IOS(5_0);方法那么参数中的视图控制器就会以模态窗口的形式展现,同时调用- (void)dismissViewControllerAnimated: (BOOL)flag completion: (void (^)(void))completion NS_AVAILABLE_IOS(5_0);方法就会关闭模态窗口。
这种方式一般出现在需要使用者完成某件事情,如输入密码、增加资料等操作后,才能(回到跳转前的控制器)继续。例如系统的WIFI连接输入密码提示。默认动画是从下至上。
~~~designated && convenience
http://www.codingexplorer.com/designated-initializers-convenience-initializers-swift/
designated init 中必须初始化所有非空属性,默认都是这个。convenience init 可以给一些属性指定默认值,最后必须调用designated init,只在特殊情况使用。
~~~手写代码画界面 && xib && storyboard
http://www.cocoachina.com/industry/20140102/7640.html
手写代码画界面:
便于版本管理,容易检查追踪改动以及进行代码合并。
便于代码重用。如果你的目的是写一些可以高度重用的控件提供给其他开发者使用,那毫无疑问最好的选择就是使用代码来完成UIView的子类。
缺点:开发速度慢,代码量大,较难维护和定位,不能直观看到显示效果。
xib:
文件内容过于复杂,可读性很差,即使只是简单打开没有编辑也有可能造成变化而导致合并和提交的苦难。
xib中的设置往往并非最终设置,在代码中你将有机会覆盖你在xib文件中进行的UI设计。在不同的地方对同一个属性进行设置,维护起来会十分困难。
xib没有逻辑判断,也很难在运行时进行配置。因此,如果选择xib,那么要尽量将xib的工作和代码的工作隔离开来:能够使用xib完成的内容就统一使用xib来做,而不要说三个Label其中两个在xib设置了字体而另一个却在代码中完成。尽量仅保持必要的、较少的IBOutlet和IBAction会是一个好方法。
storyboard:
可以把StoryBoard看做是一组viewController对应的xib,以及它们之间的转换方式的集合。在StoryBoard中不仅可以看到每个ViewController的布局样式,也可以明确地知道各个ViewController之间的转换关系。相对于单个的xib,其代码需求更少,也由于集合了各个xib,使得对于界面的理解和修改的速度也得到了更大提升。
在Xcode5之后,StoryBoard已经成为新建项目的默认配置,这也代表了Apple对开发者的建议和未来的方向。WWDC2013的各个Sample Code中也基本都使用了StoryBoard来进行演示。可以预见到,之后Apple必定会在这方面进行继续强化,而反之纯代码或者单个xib的方式很可能不会再得到增强。
现在StoryBoard面临的最大问题就是多人协作。一种可行的做法是将项目的不同部分分解成若干个StoryBoard,并安排开发人员对自己的部分进行负责,这样就不会有冲突的问题。
StoryBoard的另外的挑战来源于ViewController的重用和自定义的view的处理。对于前者,在正确封装接口以及良好设计的基础上,其实StoryBoard的VC重用与代码的VC重用是没有本质区别的,在StoryBoard中添加封装良好需要重用的Scene即可解决。而对于后者,因为StoryBoard中已经不允许有单个view的存在,因此很多时候我们还是需要借助于单个的xib来自定义UI。
相对于单个xib来说,StoryBoard文件往往更大,加载速度也相应变慢,不过可以随着设备的发展而忽略这个差别。
~~~frame && bounds && center
frame:
-(CGRect)frame{
return CGRectMake(self.frame.origin.x,self.frame.origin.y,self.frame.size.width,self.frame.size.height);
}
该view在父view坐标系统中的位置和大小。原点为在父视图中的坐标。参照系是父亲的坐标系统。
bounds:
-(CGRect)bounds{
return CGRectMake(0,0,self.frame.size.width,self.frame.size.height);
}
该view在本地坐标系统中的位置和大小。原点为(0, 0)。参照系是自己的坐标系统。
center:
该view的中心点在父view坐标系统中的位置和大小。参照系是父亲的坐标系统。
~~~frame && bounds && center
每个 UIView 内部都有一个 CALayer 在背后提供内容的绘制和显示,并且 UIView 的尺寸样式都由内部的 Layer 所提供。两者都有树状层级结构,layer 内部有 SubLayers,View 内部有 SubViews.但是 Layer 比 View 多了个AnchorPoint。
在 View显示的时候,UIView 做为 Layer 的 CALayerDelegate,View 的显示内容由内部的 CALayer 的 display。
CALayer 是默认修改属性支持隐式动画的,在给 UIView 的 Layer 做动画的时候,View 作为 Layer 的代理,Layer 通过 actionForLayer:forKey:向 View请求相应的 action(动画行为)。
layer 内部维护着三个 layer tree,分别是 presentLayer Tree(动画树),modeLayer Tree(模型树), Render Tree (渲染树),在做 iOS动画的时候,我们修改动画的属性,在动画的其实是 Layer 的 presentLayer的属性值,而最终展示在界面上的其实是提供 View的modelLayer。
两者最明显的区别是 View可以接受并处理事件,而 Layer 不可以。
~~~UIButton的父类是什么?UILabel呢?
UIButton的父类为UIControl:
UIControl是UIView的子类,当然也是UIResponder的子类。UIControl是诸如UIButton、UISwitch、UITextField等控件的父类,它本身也包含了一些属性和方法,但是不能直接使用UIControl类,它只是定义了子类都需要使用的方法。
UIControl把复杂的触摸事件封装成了简单的易于使用的控件事件。例如通过UIControl对象处理后,按下按钮的事件就被封装成一个控件事件,而不用去判断触摸屏幕的整个操作过程。例如按钮的单击事件UIControlEventTouchUpInside。
UILabel的父类为UIView。
~~~
在一个对象的方法里面:self.name= “object”;和 name =”object” 有什么不同吗?
答:
self.name =”object”:会调用对象的setName()方法;
name = “object”:会直接把object赋值给当前对象的name属性。
~~~runtime
http://www.cocoachina.com/ios/20141008/9844.html
Objective-C 的 Runtime 是一个运行时库(Runtime Library),它是一个主要使用 C 和汇编写的库,为 C 添加了面向对象的能力并创造了 Objective-C。这就是说它在类信息(Class information) 中被加载,完成所有的方法分发,方法转发,等等。Objective-C runtime 创建了所有需要的结构体,让 Objective-C 的面相对象编程变为可能。
20.NSArray实例化时,array与init的区别
中级题目
1. 什么是arc?(arc是为了解决什么问题诞生的?)
2. 请解释以下keywords的区别: assign vs weak, __block vs __weak
3. __block在arc和非arc下含义一样吗?
4. 使用atomic一定是线程安全的吗?
5. 描述一个你遇到过的retain cycle例子。(别撒谎,你肯定遇到过)
6. +(void)load; +(void)initialize;有什么用处?
7. 为什么其他语言里叫函数调用, objective c里则是给对象发消息(或者谈下对runtime的理解)
8. 什么是method swizzling?
10. 如何高性能的给UIImageView加个圆角?(不准说layer.cornerRadius!)
11. 使用drawRect有什么影响?(这个可深可浅,你至少得用过。。)
12. ASIHttpRequest或者SDWebImage里面给UIImageView加载图片的逻辑是什么样的?(把UIImageView放到UITableViewCell里面问更赞)
13. 麻烦你设计个简单的图片内存缓存器(移除策略是一定要说的)
14. 讲讲你用Instrument优化动画性能的经历吧(别问我什么是Instrument)
16. viewWillLayoutSubView你总是知道的。。
17. GCD里面有哪几种Queue?你自己建立过串行queue吗?背后的线程模型是什么样的?
18. 用过coredata或者sqlite吗?读写是分线程的吗?遇到过死锁没?咋解决的?
19. http的post和get啥区别?(区别挺多的,麻烦多说点)
21.哪些类不适合使用单例模式?即使他们在周期中只会出现一次。
23.简单介绍一下KVC和KVO,他们都可以应用在哪些场景?
25.发送10个网络请求,然后再接收到所有回应之后执行后续操作,如何实现?
26.实现一个第三方控件,可以在任何时候出现在APP界面最上层
27.不同版本的APP,数据库结构变化了,如何处理?
28.内存中的栈和堆的区别是什么?那些数据在栈上,哪些在堆上?
29.block中的weak self,是任何时候都需要加的么?
30.GCD的queue,main queue中执行的代码,一定是在main thread么?
31.NSOperationQueue有哪些使用方式
32.NSThread中的Runloop的作用,如何使用?
33..h文件中的变量,外部可以直接访问么?(注意是变量,不是property)
34.讲述一下runtime的概念,message send如果寻找不到相应的对象,会如何进行后续处理 ?
35.利用runtime实现一个对象的拷贝
内存管理
动画技术
绘图技术
Cocoa的一些设计模式( MVC、单例等)
Objective-C的一些语言特性(KVO、Notification、Category等)
其他 对新技术的了解、平常怎么提高iOS开发等
1.通用编程技能,例如:一道小算法,数据结构的实现方式,网络,多线程。
2.开发语言,例如:语言特性,重新实现语言提供的功能,是否深入研究过这门语言的某部分。
3.开发平台,例如:该平台的内部消息,内存,线程等机制。
4.工具,例如:调试技巧,是否熟练使用,代码管理工具,项目管理工具,效率工具。
5.行业视角,例如:用什么,知道什么。
6.其他能力,例如:网络上解决问题的能力,是否有持续学习的意识。
有经验的,1-6都会问到,刚毕业的,只要重点面1和6
!-->
以上是关于iOS面试题的主要内容,如果未能解决你的问题,请参考以下文章