slide页 第三方登陆注册 极光推送、苹果远程推送流程 地图 二维码扫描 地图线路规划 日历 支付bmob+支付宝 音频下载 环信 百度地图、线路规划 ShareSDK,MJRefresh KVC&KVO 响应式编程 Swift小项目 Apple Pay 运行时机制Runtime Runloop MVVM MVCS设计模式 HTTP原理 Framework封装。
一、OC概述
1、OC语言的基本特点
OC 语言是 C 语言的一个超集,只是在 C 的基础之上加上了面向对象 (oop) 的特性;
OC 与 Java 语言相同都是单继承,这一点与 C++ 语言不同(多重继承);
OC 不支持命名空间机制,取而代之的是在类名之前添加前缀,以此来区分。
OC作为一门面向对象的语言,自然具有面向对象的语言特性,如:封装、多态、继承。它具有静态语言的特性,又有动态语言的效率。
Objective-C 具有相当多的动态特性,表现在三个方面:动态类型、动态绑定、动态加载。之所以叫做动态,是因为必须到运行时才会做一些事情。
1、 动态类型
:即运行时再决定对象的类型。这类动态类型在日常应用中非常常见。简单说就是id类型。实际上静态类型因为其固定性和可预知性而使用的非常广泛,静态类型是强类型,而动态类型属于弱类型。运行时决定接受者。
2、 动态绑定
:基于动态类型,在某个实例对象被确定后,其类型就被确定了。该对象的属性和响应的消息也被完全确定,这就是动态绑定。
3、 动态加载
:根据需求加载所需要的资源,这点很容易理解,对于iOS开发来说,基本就是根据不同的急性左适配。最经典的例子就是在Retina 设备上加载@2x的图片,而在老一些的普通设备上加载原图。随着Retina iPad的推出,和之后可能的Retina Mac的出现,这个特性相信会被越来越多的使用。让程序在运行时添加代码块以及其他资源。用户可以根据需要加载一些可执行代码和资源,而不是在启动时就加载所有组件。可执行代码中可以含有和程序运行时整合的新类。
2、我们说的oc是动态运行时语言是什么意思?
答案:多态。 主要是将数据类型的确定由编译时,推迟到了运行时。
这个问题其实浅涉及到两个概念,运行时和多态。
简单来说,运行时机制使我们直到运行时才去决定一个对象的类别,以及调用该类别对象指定方法。
多态:不同对象以自己的方式响应相同的消息的能力叫做多态。意思就是假设生物类(life)都用有一个相同的方法-eat;
那人类属于生物,猪也属于生物,都继承了life后,实现各自的eat,但是调用是我们只需调用各自的eat方法。
也就是不同的对象以自己的方式响应了相同的消息(响应了eat这个选择器)。
因此也可以说,运行时机制是多态的基础
3、关于多态性
答案:多态,子类指针可以赋值给父类。
这个题目其实可以出到一切面向对象语言中,
因此关于多态,继承和封装基本最好都有个自我意识的理解,也并非一定要把书上资料上写的能背出来。最重要的是转化成自我理解。
4、OC语言的优缺点
OC语言优点:
1) Cateogies
2) Posing
3) 动态识别
4) 指标计算
5)弹性讯息传递
6) 不是一个过度复杂的 C 衍生语言
7) Objective-C 与 C++ 可混合编程
缺点:
1) 不支援命名空间
2) 不支持运算符重载
3)不支持多重继承
4)使用动态运行时类型,所有的方法都是函数调用,所以很多编译时优化方法都用不到。(如内联函数等),性能低劣。
二、property与属性修饰符
5、 @property后面可以有哪些属性修饰符?作用?
@property是一个属性访问声明,扩号内支持以下几个属性:
1,getter=getterName,setter=setterName,设置setter与getter的方法名
2,readwrite,readonly,设置可供访问级别
3,assign,setter方法直接赋值,不进行任何retain操作,为了解决原类型与环循引用问题
4,retain,setter方法对参数进行release旧值再retain新值,所有实现都是这个顺序(CC上有相关资料)
5,copy,setter方法进行Copy操作,与retain处理流程一样,先旧值release,再Copy出新的对象,retainCount为1。这是为了减少对上下文的依赖而引入的机制。
copy是在你不希望a和b共享一块内存时会使用到。a和b各自有自己的内存。
6,nonatomic,非原子性访问,不加同步,多线程并发访问会提高性能。注意,如果不加此属性,则默认是两个访问方法都为原子型事务访问。锁被加到所属对象实例级(我是这么理解的...)。
atomic 和 nonatomic 用来决定编译器生成的 getter 和 setter 是否为原子操作。在多线程环境下,原子操作是必要的,否则有可能引起错误的结果。
6、浅拷贝和深拷贝的区别?
答案:浅层拷贝:只复制指向对象的指针,而不复制引用对象本身。
深层拷贝:复制引用对象本身。
意思就是说我有个A对象,复制一份后得到A_copy对象后,对于浅复制来说,A和A_copy指向的是同一个内存资源,复制的只不过是是一个指针,对象本身资源
还是只有一份,那如果我们对A_copy执行了修改操作,那么发现A引用的对象同样被修改,这其实违背了我们复制拷贝的一个思想。深复制就好理解了,内存中存在了
两份独立对象本身。
浅拷贝好比你和你的影子,你完蛋,你的影子也完蛋。
深拷贝好比你和你的克隆人,你完蛋,你的克隆人还活着。
7、定义属性时,什么时候用assign、retain、copy以及它们的之间的区别
答:(1)assign:普通赋值,一般常用于基本数据类型,常见委托设计模式,以此来防止循环引用。(我们称之为弱引用,weak)
(2)retain:保留计数,获得到了对象的所有权。引用计数在原有基础上加1。
(3)copy:一般认为,是在内存中重新开辟了一个新的内存空间,用来
存储新的对象,和原来的对象是两个不同的地址,引用计数分别1。但是当copy对象为不可变对象时,那么copy的作用相当于retain。因为,这样可以节约内存空间。
8、原子(atomic)跟非原子(non-atomic)属性有什么区别?
1. atomic提供多线程安全。是防止在写未完成的时候被另外一个线程读取,造成数据错误
2. non-atomic:在自己管理内存的环境中,解析的访问器保留并自动释放返回的值,如果指定了 nonatomic ,那么访问器只是简单地返回这个值。
9、为什么 delegate(代理)属性都是 assign 而不是 retain的?
答:防止循环引用,以至对象无法得到正确的释放。
三、类别 扩展 代理 通知 协议
10、 类别的作用?继承和类别在实现中有何区别?
答案:category 可以在不获悉,不改变原来代码的情况下往里面添加新的方法,只能添加,不能删除修改。并且如果类别和原来类中的方法产生名称冲突,则类别将覆盖原来的方法,因为类别具有更高的优先级。
类别主要有3个作用:
(1)将类的实现分散到多个不同文件或多个不同框架中。
(2)创建对私有方法的前向引用。
(3)向对象添加非正式协议。
继承可以增加,修改或者删除方法,并且可以增加属性。
11、类别和类扩展的区别。
答案:category和extensions的不同在于 后者可以添加属性。另外后者添加的方法是必须要实现的。
extensions可以认为是一个私有的Category。
12、延展是什么?作用是什么?
答:延展(extension):在自己类的实现文件中添加类目来声明私有方法。
延展(Extension)“匿名”的类目,延展定义类的私有方法,一般是在类的实现文件@implementation之上定义如:Person() 和类目的区别在于括号里面是空的,没有名字的,暂时性的存在,只能本类调用方法,子类也无法调用父类的延展
另外在本类的实现文件@implementation之上也可以定义私有方法,私有的静态变量
延展的特点就是定义私有方法
13、代理的作用?
答案:代理的目的是改变或传递控制链。允许一个类在某些特定时刻通知到其他类,而不需要获取到那些类的指针。可以减少框架复杂度。
另外一点,代理可以理解为java中的回调监听机制的一种类似。
14、通知和协议的不同之处?
答案:协议有控制链(has-a)的关系,通知没有。
首先我一开始也不太明白,什么叫控制链(专业术语了~)。但是简单分析下通知和代理的行为模式,我们大致可以有自己的理解
简单来说,通知的话,它可以一对多,一条消息可以发送给多个消息接受者。
代理按我们的理解,到不是直接说不能一对多,比如我们知道的明星经济代理人,很多时候一个经济人负责好几个明星的事务。
只是对于不同明星间,代理的事物对象都是不一样的,一一对应,不可能说明天要处理A明星要一个发布会,代理人发出处理发布会的消息后,别称B的
发布会了。但是通知就不一样,他只关心发出通知,而不关心多少接收到感兴趣要处理。
因此控制链(has-a从英语单词大致可以看出,单一拥有和可控制的对应关系。
15、 oc中的协议和java中的接口概念有何不同?
答案:OC中的代理有2层含义,官方定义为 formal和informalprotocol。前者和Java接口一样。informalprotocol中的方法属于设计模式考虑范畴,不是必须实现的,但是如果有实现,就会改变类的属性。
其实关于正式协议,类别和非正式协议我很早前学习的时候大致看过,也写在了学习教程里
“非正式协议概念其实就是类别的另一种表达方式“这里有一些你可能希望实现的方法,你可以使用他们更好的完成工作”。
这个意思是,这些是可选的。比如我门要一个更好的方法,我们就会申明一个这样的类别去实现。然后你在后期可以直接使用这些更好的方法。
这么看,总觉得类别这玩意儿有点像协议的可选协议。
现在来看,其实protocal已经开始对两者都统一和规范起来操作,因为资料中说“非正式协议使用interface修饰“,
现在我们看到协议中两个修饰词:“必须实现(@requied)”和“可选实现(@optional)”。
16、Object-c的类可以多重继承么?可以实现多个接口么?Category是什么?重写一个类的方式用继承好还是分类好?为什么?
答: Object-c的类不可以多重继承;可以实现多个接口,通过实现多个接口可以完成C++的多重继承;Category是类别,一般情况用分类好,用Category去重写类的方法,仅对本Category有效,不会影响到其他类与原有类的关系。没有,cocoa 中所有的类都是NSObject 的子类,多继承在这里是用protocol 委托代理来实现的? ,ood的多态特性在obj-c中通过委托来实现。
四、static
17、static 关键字的作用:
(1)函数体内 static 变量的作用范围为该函数体,不同于 auto 变量,该变量的内存只被分配一次, 因此其值在下次调用时仍维持上次的值;
(2)在模块内的 static 全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问;
(3)在模块内的 static 函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明它的模块内;
(4)在类中的 static 成员变量属于整个类所拥有,对类的所有对象只有一份拷贝;
(5)在类中的 static 成员函数属于整个类所拥有,这个函数不接收 this 指针,因而只能访问类的static 成员变量。
18、static有什么用途?(请至少说明两种)
1.限制变量的作用域
2.设置变量的存储域
3. 引用与指针有什么区别?
1) 引用必须被初始化,指针不必。
2) 引用初始化以后不能被改变,指针可以改变所指的对象。
3) 不存在指向空值的引用,但是存在指向空值的指针。
4)描述实时系统的基本特性在特定时间内完成特定的任务,实时性与可靠性
19、解释以下关键字,static、self、super用实例说明
答static:静态全局变量,持久性作用、存储区域在静态区域,它的生命周期和应用进行绑定。程序结束时,由系统自动回收。
self:当前消息的接收者。super:向父类发送消息。
20、如何引用一个已经定义过的全局变量?
答:extern可以用引用头文件的方式,也可以用extern关键字,如果用引用头文件方式来引用某个在头文件中声明的全局变理,假定你将那个变写错了,那么在编译期间会报错,如果你用extern方式引用时,假定你犯了同样的错误,那么在编译期间不会报错,而在连接期间报错
9.全局变量可不可以定义在可被多个.C文件包含的头文件中?为什么?
答:可以,在不同的C文件中以static形式来声明同名全局变量。
可以在不同的C文件中声明同名的全局变量,前提是其中只能有一个C文件中对此变量初值,此时连接不会出错
五、内存管理
21、简述内存管理基本原则
答:(1)如果使用alloc、copy(mutableCopy)或者retian一个对象时,你就有义务,向它发送一条release或者autorelease消息。其他方法创建的对象,不需要由你来管理内存。
(2)向一个对象发送一条autorelease消息,这个对象并不会立即销毁,而是将这个对象放入了自动释放池,待池子释放时,它会向池中每一个对象发送一条release消息,以此来释放对象。
(3)向一个对象发送release消息,并不意味着这个对象被销毁了,而是当这个对象的引用计数为0时,系统才会调用dealloc方法,释放该对象和对象本身它所拥有的实例。
22、Objective-C如何对内存管理的,说说你的看法和解决方法?
Objective-C的内存管理主要有三种方式ARC(自动内存计数)、手动内存计数、内存池。
1. (Garbage Collection)自动内存计数:这种方式和java类似,在你的程序的执行过程中。始终有一个高人在背后准确地帮你收拾垃圾,你不用考虑它什么时候开始工作,怎样工作。你只需要明白,我申请了一段内存空间,当我不再使用从而这段内存成为垃圾的时候,我就彻底的把它忘记掉,反正那个高人会帮我收拾垃圾。遗憾的是,那个高人需要消耗一定的资源,在携带设备里面,资源是紧俏商品所以iPhone不支持这个功能。所以“Garbage Collection”不是本入门指南的范围,对“Garbage Collection”内部机制感兴趣的同学可以参考一些其他的资料,不过说老实话“Garbage Collection”不大适合适初学者研究。
解决: 通过alloc– initial方式创建的, 创建后引用计数+1, 此后每retain一次引用计数+1,那么在程序中做相应次数的release就好了.
2. (Reference Counted)手动内存计数:就是说,从一段内存被申请之后,就存在一个变量用于保存这段内存被使用的次数,我们暂时把它称为计数器,当计数器变为0的时候,那么就是释放这段内存的时候。比如说,当在程序A里面一段内存被成功申请完成之后,那么这个计数器就从0变成1(我们把这个过程叫做alloc),然后程序B也需要使用这个内存,那么计数器就从1变成了2(我们把这个过程叫做retain)。紧接着程序A不再需要这段内存了,那么程序A就把这个计数器减1(我们把这个过程叫做release);程序B也不再需要这段内存的时候,那么也把计数器减1(这个过程还是release)。当系统(也就是Foundation)发现这个计数器变成了0,那么就会调用内存回收程序把这段内存回收(我们把这个过程叫做dealloc)。顺便提一句,如果没有Foundation,那么维护计数器,释放内存等等工作需要你手工来完成。
解决:一般是由类的静态方法创建的, 函数名中不会出现alloc或init字样, 如[NSString string]和[NSArray arrayWithObject:], 创建后引用计数+0, 在函数出栈后释放, 即相当于一个栈上的局部变量. 当然也可以通过retain延长对象的生存期.
3. (NSAutoRealeasePool)内存池:可以通过创建和释放内存池控制内存申请和回收的时机.
解决:是由autorelease加入系统内存池, 内存池是可以嵌套的, 每个内存池都需要有一个创建释放对, 就像main函数中写的一样. 使用也很简单, 比如[[[NSString alloc]initialWithFormat:@”Hey you!”] autorelease], 即将一个NSString对象加入到最内层的系统内存池, 当我们释放这个内存池时, 其中的对象都会被释放.
23、内存管理的几条原则时什么?按照默认法则.那些关键字生成的对象
需要手动释放?在和property结合的时候怎样有效的避免内存泄露?
谁申请,谁释放
遵循Cocoa Touch的使用原则;
内存管理主要要避免“过早释放”和“内存泄漏”,对于“过早释放”需要注意@property设置特性时,一定要用对特性关键字,对于“内存泄漏”,一定要申请了要负责释放,要细心。
关键字alloc 或new 生成的对象需要手动释放;
设置正确的property属性,对于retain需要在合适的地方释放
24、内存管理的几条原则是什么?按照默认法则,哪些关键字生成的对象需要手动释放?哪些情况下不需要手动释放,会直接进入自动释放池?
当使用new、alloc或copy方法创建一个对象时,该对象引用计数器为1。如果不需要使用该对象,可以向其发送release或autorelease消息,在其使用完毕时被销毁。
如果通过其他方法获取一个对象,则可以假设这个对象引用计数为1,并且被设置为autorelease,不需要对该对象进行清理,如果确实需要retain这个对象,则需要使用完毕后release。
如果retain了某个对象,需要release或autorelease该对象,保持retain方法和release方法使用次数相等。
使用new、alloc、copy关键字生成的对象和retain了的对象需要手动释放。设置为autorelease的对象不需要手动释放,会直接进入自动释放池。
25、OC的内存管理
如果您通过分配和初始化(比如[[MyClass alloc] init])的方式来创建对象,您就拥
有这个对象,需要负责该对象的释放。这个规则在使用NSObject的便利方法new 时也同样适用。
如果您拷贝一个对象,您也拥有拷贝得到的对象,需要负责该对象的释放。
如果您保持一个对象,您就部分拥有这个对象,需要在不再使用时释放该对象。
反过来,
如果您从其它对象那里接收到一个对象,则您不拥有该对象,也不应该释放它(这个规则有少数
的例外,在参考文档中有显式的说明)。
26、自动释放池是什么,如何工作
当您向一个对象发送一个autorelease消息时,Cocoa就会将该对象的一个引用放入到最新的自动释放池。它仍然是个正当的对象,因此自动释放池定义的作用域内的其它对象可以向它发送消息。当程序执行到作用域结束的位置时,自动释放池就会被释放,池中的所有对象也就被释放。
1. ojc-c 是通过一种"referring counting"(引用计数)的方式来管理内存的, 对象在开始分配内存(alloc)的时候引用计数为一,以后每当碰到有copy,retain的时候引用计数都会加一, 每当碰到release和autorelease的时候引用计数就会减一,如果此对象的计数变为了0, 就会被系统销毁.
2.NSAutoreleasePool 就是用来做引用计数的管理工作的,这个东西一般不用你管的.
3.autorelease和release没什么区别,只是引用计数减一的时机不同而已,autorelease会在对象的使用真正结束的时候才做引用计数减一。
27、 如果我们不创建内存池,是否有内存池提供给我们?
答案:界面线程维护着自己的内存池,用户自己创建的数据线程,则需要创建该线程的内存池。
28、什么时候需要在程序中创建内存池?
答案:用户自己创建的数据线程,则需要创建该线程的内存池。
29、自动释放池是什么,如何工作
答:自动释放池是 NSAutorelease 类的一个实例,当向一个对象发送autorelease 消息时,该对象会自动入池,待池销毁时,将会向池中所有对象发送一条 release 消息,释放对象。[pool release]; [pool drain]表示的是池本身。
不会销毁,而是池子中的临时对象都被发送 release,从而将对象销毁。
30、当我们将一个对象加入自动释放池时,该对象何时被销毁
答:我们在 application kit 应用程序中,自动释放池中的临时对象被销毁的时间时,一个事件循环结束后。注意自动释放池没有被释放,而是被排空了,向池发送了 drain 消息。
31、OC的垃圾回收机制?
答案: OC2.0有Garbage collection,但是iOS平台不提供。
一般我们了解的objective-c对于内存管理都是手动操作的,但是也有自动释放池。
但是差了大部分资料,貌似不要和arc机制搞混就好了。
六、内存管理 --- 堆和栈
32、堆和栈的区别 :
管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak。
申请大小:
栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在 WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。
堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。
碎片问题:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出
分配方式:堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。
分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的。
33、堆和栈的区别
答:(1)栈区(stack)由编译器自动分配释放 ,存放方法(函数)的参数值,局部变量的值等。先进后出。
(2)堆区(heap)一般由程序员分配释放, 若程序员不释放,程序结束时由OS回收 。
(3)全局区(静态区)(static),全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后有系统释放。
(4)文字常量区—常量字符串就是放在这里的。程序结束后由系统释放。(5)程序代码区—存放函数体的二进制代码。
34、Heap与stack的差别
Heap是堆,stack是栈。
Stack的空间由操作系统自动分配/释放,Heap上的空间手动分配/释放。
Stack空间有限,Heap是很大的自由存储区C中的malloc函数分配的内存空间即在堆上,C++中对应的是new操作符。程序在编译期对变量和函数分配内存都在栈上进行,且程序运行过程中函数调用时参数的传递也在栈上进行
35、堆和栈上的指针
指针所指向的这块内存是在哪里分配的,在堆上称为堆上的指针,在栈上为栈上的指针.
在堆上的指针,可以保存在全局数据结构中,供不同函数使用访问同一块内存.
在栈上的指针,在函数退出后,该内存即不可访问.
36、全局变量和局部变量在内存中是否有区别?如果有,是什么区别?
全局变量储存在静态数据库,局部变量在堆栈
1.什么是平衡二叉树?
左右子树都是平衡二叉树且左右子树的深度差值的绝对值不大于1
37、堆栈溢出一般是由什么原因导致的?
没有回收垃圾资源
1.什么函数不能声明为虚函数?
七、block 委托
38、什么是block
对于闭包(block),有很多定义,其中闭包就是能够读取其它函数内部变量的函数,这个定义即接近本质又较好理解。对于刚接触Block的同学,会觉得有些绕,因为我们习惯写这样的程序main(){ funA();}funA(){funB();} funB(){…..}; 就是函数main调用函数A,函数A调用函数B… 函数们依次顺序执行,但现实中不全是这样的,例如项目经理M,手下有3个程序员A、B、C,当他给程序员A安排实现功能F1时,他并不等着A完成之后,再去安排B去实现F2,而是安排给A功能F1,B功能F2,C功能F3,然后可能去写技术文档,而当A遇到问题时,他会来找项目经理M,当B做完时,会通知M,这就是一个异步执行的例子。在这种情形下,Block便可大显身手,因为在项目经理M,给A安排工作时,同时会告诉A若果遇到困难,如何能找到他报告问题(例如打他手机号),这就是项目经理M给A的一个回调接口,要回掉的操作,比如接到电话,百度查询后,返回网页内容给A,这就是一个Block,在M交待工作时,已经定义好,并且取得了F1的任务号(局部变量),却是在当A遇到问题时,才调用执行,跨函数在项目经理M查询百度,获得结果后回调该block。
39、block 实现原理
Objective-C是对C语言的扩展,block的实现是基于指针和函数指针。
从计算语言的发展,最早的goto,高级语言的指针,到面向对象语言的block,从机器的思维,一步步接近人的思维,以方便开发人员更为高效、直接的描述出现实的逻辑(需求)。
下面是两篇很好的介绍block实现的博文
iOS中block实现的探究
谈Objective-C Block的实现
block的使用
使用实例
cocoaTouch框架下动画效果的Block的调用
使用typed声明block
typedef void(^didFinishBlock)(NSObject *ob);
这就声明了一个didFinishBlock类型的block,
然后便可用
@property (nonatomic,copy)didFinishBlock finishBlock;
声明一个blokc对象,注意对象属性设置为copy,接到block 参数时,便会自动复制一份。
block是一种特殊类型,
使用该关键字声明的局部变量,可以被block所改变,并且其在原函数中的值会被改变。
1 )使用block和使用delegate完成委托模式有什么优点?
首先要了解什么是委托模式,委托模式在iOS中大量应用,其在设计模式中是适配器模式中的对象适配器,Objective-C中使用id类型指向一切对象,使委托模式更为简洁。了解委托模式的细节:
iOS设计模式—-委托模式
使用block实现委托模式,其优点是回调的block代码块定义在委托对象函数内部,使代码更为紧凑;
适配对象不再需要实现具体某个protocol,代码更为简洁。
2 )多线程与block
GCD与Block
使用 dispatch_async 系列方法,可以以指定的方式执行block
GCD编程实例
dispatch_async的完整定义
void dispatch_async(
dispatch_queue_t queue,
dispatch_block_t block);
功能:在指定的队列里提交一个异步执行的block,不阻塞当前线程
通过queue来控制block执行的线程。主线程执行前文定义的 finishBlock对象
dispatch_async(dispatch_get_main_queue(),^(void){finishBlock();});
40、 谈谈对Block 的理解?并写出一个使用Block执行UIVew动画?
答案:Block是可以获取其他函数局部变量的匿名函数,其不但方便开发,并且可以大幅提高应用的执行效率(多核心CPU可直接处理Block指令)
[UIViewtransitionWithView:self.view
duration:0.2
options:UIViewAnimationOptionTransitionFlipFromLeft
animations:^{[[blueViewController view] removeFromSuperview]; [[self view]insertSubview:yellowViewController.view atIndex:0]; }
completion:NULL];
41、 写出上面代码的Block的定义。
typedef void(^animations)(void);
typedef void(^completion) (BOOLfinished);
42、试着使用+ beginAnimations:context:以及上述Block的定义,写出一个可以完成
+ (void)transitionWithView:(UIView *)viewduration:(NSTimeInterval)duration options:(UIViewAnimationOptions)optionsanimations:(void (^)(void))animations completion:(void (^)(BOOLfinished))completion NS_AVAILABLE_IOS(4_0);操作的函数执行部分
43、委托是什么?委托和委托方双方的property声明用什么属性?为什么?
委托:一个对象保存另外一个对象的引用,被引用的对象实现了事先确定的协议,该协议用于将引用对象中的变化通知给被引用对象。
委托和委托方双方的property声明属性都是assign而不是retain
为了避免循环引用造成的内存泄露。
44、循环引用的问题这样理解:
比如在main函数中创建了两个类的对象A和B,现在引用计数都是1。现在让A和B互相引用(A有一个属性是B对象,属性说明是retain;B有一个属性是A对象,属性说明是retain),现在两个对象的引用计数都增加了1,都变成了2。
现在执行[A release]; [B release]; 此时创建对象的main函数已经释放了自己对对象的所有权,但是此时A和B的引用计数都还是1,因为他们互相引用了。
这时你发现A和B将无法释放,因为要想释放A必须先释放B,在B的dealloc方法中再释放A。同理,要想释放B必须先释放A,在A的dealloc方法中再释放B。所以这两个对象将一直存在在内存中而不释放。这就是所谓的循环引用的问题。要想解决这个问题,一般的方法可以将引用的属性设置为assign,而不是retain来处理。
八、线程 进程 多线程
46、线程与进程的区别和联系?
进程和线程都是由操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性。
进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
47、进程与线程
进程(process)是一块包含了某些资源的内存区域。操作系统利用进程把它的工作划分为一些功能单元。
进程中所包含的一个或多个执行单元称为线程(thread)。进程还拥有一个私有的虚拟地址空间,该空间仅能被它所包含的线程访问。
通常在一个进程中可以包含若干个线程,它们可以利用进程所拥有的资源。
在引入线程的操作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位。
由于线程比进程更小,基本上不拥有系统资源,故对它的调度所付出的开销就会小得多,能更高效的提高系统内多个程序间并发执行的程度。
简而言之,一个程序至少有一个进程,一个进程至少有一个线程.一个程序就是一个进程,而一个程序中的多个任务则被称为线程。
线程只能归属于一个进程并且它只能访问该进程所拥有的资源。当操作系统创建一个进程后,该进程会自动申请一个名为主线程或首要线程的线程。应用程序(application)是由一个或多个相互协作的进程组成的。
另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。
进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位.
线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.
一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行.
48、进程和线程的差别
线程是指进程内的一个执行单元,也是进程内的可调度实体.
与进程的区别:
1)调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位
2)并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可并发执行
3)拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属
于进程的资源.
4)系统开销:在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开
销明显大于创建或撤消线程时的开销。
49、网络编程中设计并发服务器,使用多进程与多线程,请问有什么区别?
1)进程:子进程是父进程的复制品。子进程获得父进程数据空间、堆和栈的复制品。
2)线程:相对与进程而言,线程是一个更加接近与执行体的概念,它可以与同进程的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。两者都可以提高程序的并发度,提高程序运行效率和响应时间。程和进程在使用上各有优缺点:线程执行开销小,但不利于资源管理和保护;而进程正相反。同时,线程适合于在SMP机器上运行,而进程则可以跨机器迁移。
50、多线程是什么
多线程是个复杂的概念,按字面意思是同步完成多项任务,提高了资源的使用效率,从硬件、操作系统、应用软件不同的角度去看,多线程被赋予不同的内涵,对于硬件,现在市面上多数的CPU都是多核的,多核的CPU运算多线程更为出色;从操作系统角度,是多任务,现在用的主流操作系统都是多任务的,可以一边听歌、一边写博客;对于应用来说,多线程可以让应用有更快的回应,可以在网络下载时,同时响应用户的触摸操作。在iOS应用中,对多线程最初的理解,就是并发,它的含义是原来先做烧水,再摘菜,再炒菜的工作,会变成烧水的同时去摘菜,最后去炒菜。
51、iOS 中的多线程
iOS中的多线程,是Cocoa框架下的多线程,通过Cocoa的封装,可以让我们更为方便的使用线程,做过C++的同学可能会对线程有更多的理解,比如线程的创立,信号量、共享变量有认识,Cocoa框架下会方便很多,它对线程做了封装,有些封装,可以让我们创建的对象,本身便拥有线程,也就是线程的对象化抽象,从而减少我们的工程,提供程序的健壮性。
GCD是(GrandCentral Dispatch)的缩写 ,从系统级别提供的一个易用地多线程类库,具有运行时的特点,能充分利用多核心硬件。GCD的API接口为C语言的函数,函数参数中多数有Block,关于Block的使用参看这里,为我们提供强大的“接口”,对于GCD的使用参见本文
NSOperation与Queue
NSOperation是一个抽象类,它封装了线程的细节实现,我们可以通过子类化该对象,加上NSQueue来同面向对象的思维,管理多线程程序。具体可参看这里:一个基于NSOperation的多线程网络访问的项目。
NSThread
NSThread是一个控制线程执行的对象,它不如NSOperation抽象,通过它我们可以方便的得到一个线程,并控制它。但NSThread的线程之间的并发控制,是需要我们自己来控制的,可以通过NSCondition实现。
参看 iOS多线程编程之NSThread的使用
其他多线程
在Cocoa的框架下,通知、Timer和异步函数等都有使用多线程,(待补充).
52、在项目什么时候选择使用GCD,什么时候选择NSOperation
项目中使用NSOperation的优点是NSOperation是对线程的高度抽象,在项目中使用它,会使项目的程序结构更好,子类化NSOperation的设计思路,是具有面向对象的优点(复用、封装),使得实现是多线程支持,而接口简单,建议在复杂项目中使用。
项目中使用GCD的优点是GCD本身非常简单、易用,对于不复杂的多线程操作,会节省代码量,而Block参数的使用,会是代码更为易读,建议在简单项目中使用。
53、NSOperation queue?
答案:存放NSOperation的集合类。
操作和操作队列,基本可以看成java中的线程和线程池的概念。用于处理ios多线程开发的问题。网上部分资料提到一点是,虽然是queue,但是却并不是带有队列的概念,放入的操作并非是按照严格的先进现出。这边又有个疑点是,对于队列来说,先进先出的概念是Afunc添加进队列,Bfunc紧跟着也进入队列,Afunc先执行这个是必然的,但是Bfunc是等Afunc完全操作完以后,B才开始启动并且执行,因此队列的概念离乱上有点违背了多线程处理这个概念。但是转念一想其实可以参考银行的取票和叫号系统。
因此对于A比B先排队取票但是B率先执行完操作,我们亦然可以感性认为这还是一个队列。
但是后来看到一票关于这操作队列话题的文章,其中有一句提到
“因为两个操作提交的时间间隔很近,线程池中的线程,谁先启动是不定的。”瞬间觉得这个queue名字有点忽悠人了,还不如pool综合一点,我们知道他可以比较大的用处在于可以帮组多线程编程就好了。
54、Object C中创建线程的方法是什么?如果在主线程中执行代码,方法是什么?如果想延时执行代码、方法又是什么?
线程创建有三种方法:使用NSThread创建、使用GCD的dispatch、使用子类化的NSOperation,然后将其加入NSOperationQueue;在主线程执行代码,方法是performSelectorOnMainThread,如果想延时执行代码可以用performSelector:onThread:withObject:waitUntilDone:
GCD从语言、运行时库、系统扩展等三个方面给让使用者更充分的操作多核设备,同时它基于队列的概念,因为每一个CPU core单位时间(时间片)内只能运行某个队列的某个task,并通过优先级、FIFO等策略进行task的切换运行。
GCD共提供三种队列形式:
1. main,即主线程,iOS的UI绘制、交互响应都要在此线程上执行
2. concurrent,并发线程队列,task的进出队列,都遵循FIFO策略,但队列中的task‘看起来’是并发执行的完成时间却可能是以任意的顺序结束。很容易理解,因为每个task的执行时间长短通常不一样。
3. serial,串行队列,在此队列中的task,one by one的执行,严格遵守FIFO即,我们要执行一个block(task)
55、多线程
多线程编程是防止主线程堵塞,增加运行效率等等的最佳方法。而原始的多线程方法存在很多的毛病,包括线程锁死等。在Cocoa中,Apple提供了NSOperation这个类,提供了一个优秀的多线程编程方法。
本次介绍NSOperation的子集,简易方法的NSInvocationOperation:
一个NSOperationQueue 操作队列,就相当于一个线程管理器,而非一个线程。因为你可以设置这个线程管理器内可以并行运行的的线程数量等等
九、键路径
56、什么是键-值,键路径是什么
模型的性质是通过一个简单的键(通常是个字符串)来指定的。视图和控制器通过键来查找相应的属性值。在一个给定的实体中,同一个属性的所有值具有相同的数据类型。键-值编码技术用于进行这样的查找—它是一种间接访问对象属性的机制。
键路径是一个由用点作分隔符的键组成的字符串,用于指定一个连接在一起的对象性质序列。第一个键的
性质是由先前的性质决定的,接下来每个键的值也是相对于其前面的性质。键路径使您可以以独立于模型
实现的方式指定相关对象的性质。通过键路径,您可以指定对象图中的一个任意深度的路径,使其指向相
关对象的特定属性。
57、什么叫键路径?
答:在一个给定的实体中,同一个属性的所有值具有相同的数据类型。键-值编码技术用于进行这样的查找—它是一种间接访问对象属性的机制。键路径是一个由用点作分隔符的键组成的字符串,用于指定一个连接在一起的对象性质序列。第一个键的性质是由先前的性质决定的,接下来每个键的值也是相
对于其前面的性质。键路径使您可以以独立于模型实现的方式指定相关对象的性质。通过键路径,您可以指定对象图中的一个任意深度的路径,使其指向相关对象的特定属性。
十、保存数据
58、 在iPhone应用中如何保存数据?
答案:有以下几种保存机制:
1.通过web服务,保存在服务器上
2.通过NSCoder固化机制,将对象保存在文件中
3.通过SQlite或CoreData保存在文件数据库中
59、什么是coredata?
答案:coredata是苹果提供一套数据保存框架,其基于SQlite
60、 什么是NSManagedObject模型?
答案:NSManagedObject是NSObject的子类 ,也是coredata的重要组成部分,它是一个通用的类,实现了core data 模型层所需的基本功能,用户可通过子类化NSManagedObject,建立自己的数据模型。
61、什么是NSManagedobjectContext?
答案:NSManagedobjectContext对象负责应用和数据库之间的交互。
62、和coredata一起有哪几种持久化存储机制?
答案:存入到文件、 存入到NSUserDefaults(系统plist文件中)、存入到Sqlite文件数据库
63、iOS 开发中数据持久性,有哪几种
答:文件写入、对象归档、sqlite3 数据库、coredata
64、对象归档的基本概念,以及它的特点是什么?
答:归档为对象的数据持久化提供了一种解决方法,它特点是给归档的对象进行加密,增强了数据的安全性。此外,自定义类的对象归档必须实现 NSCoding协议。
65、什么是沙盒,文档目录结构的作用
答:IOS 应用程序只能在为该改程序创建的文件系统中读取文件,不可以去其它地方访问,此区域被成为沙盒。每个应用程序都有自己的存储空间;应用程序不能翻过自己的围墙去访问别的存储空间的内容;应用程序请求的数据都要通过权限检测,假如不符合条件的话,不会被放行。
Documents 目录:您应该将所有的应用程序数据文件写入到这个目录下。这个目录用于存储用户数据或其它应该定期备份的信息。
AppName.app 目录:这是应用程序的程序包目录,包含应用程序的本身。由于应用程序必须经过签名,所以在运行时不能对这个目录中的内容进行修改,否则可能会使应用程序无法启动。
Library 目录:这个目录下有两个子目录:Caches 和 Preferences
Preferences 目录包含应用程序的偏好设置文件。我们不应该直接创建偏好设置文件,而是应该使用 NSUserDefaults 类来取得和设置应用程序的偏好。Caches 目录用于存放应用程序专用的支持文件,保存应用程序再次启动过程中需要的信息。
tmp 目录:这个目录用于存放临时文件,保存应用程序再次启动过程中不需要的信息。
获取这些目录路径的方法:1,获取家目录路径的函数:
NSString *homeDir = NSHomeDirectory();
2,获取 Documents 目录路径的方法:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *docDir = [paths objectAtIndex:0];
3,获取 Caches 目录路径的方法:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory,NSUserDomainMask, YES);
NSString *cachesDir = [paths objectAtIndex:0];
4,获取 tmp 目录路径的方法:
NSString *tmpDir = NSTemporaryDirectory();5,获取应用程序程序包中资源文件路径的方法:例如获取程序包中一个图片资源(apple.png)路径的方法:
NSString *imagePath = [[NSBundle mainBundle] pathForResource:@”apple”ofType:@”png”];
UIImage *appleImage = [[UIImage alloc] initWithContentsOfFile:imagePath];代码中的 mainBundle 类方法用于返回一个代表应用程序包的对象。
十一、单例设计模式 类工厂方法
66、怎样实现一个单例模式的类,给出思路,不写代码。
首先必须创建一个全局实例,通常存放在一个全局变量中,此全局变量设置为nil
提供工厂方法对该全局实例进行访问,检查该变量是否为nil,如果nil就创建一个新的实例,最后返回全局实例
全局变量的初始化在第一次调用工厂方法时会在+allocWithZone:中进行,所以需要重写该方法,防止通过标准的alloc方式创建新的实例
为了防止通过copy方法得到新的实例,需要实现-copyWithZone方法
只需在此方法中返回本身对象即可,引用计数也不需要进行改变,因为单例模式下的对象是不允许销毁的,所以也就不用保留
因为全局实例不允许释放,所以retain,release,autorelease方法均需重写
67、类工厂方法是什么
类工厂方法的实现是为了向客户提供方便,它们将分配和初始化合在一个步骤中,返回被创建的对象,并进行自动释放处理。这些方法的形式是+ (type)className...(其中 className不包括任何前缀)。工厂方法可能不仅仅为了方便使用。它们不但可以将分配和初始化合在一起,还可以为初始化过程提供对象的分配信息。
类工厂方法的另一个目的是使类(比如NSWorkspace)提供单件实例。虽然init...方法可以确认一个类在每次程序运行过程只存在一个实例,但它需要首先分配一个“生的”实例,然后还必须释放该实例。工厂方法则可以避免为可能没有用的对象盲目分配内存。
68、单件实例是什么
Foundation和Application Kit 框架中的一些类只允许创建单件对象,即这些类在当前进程中的唯一实例。举例来说,NSFileManager和NSWorkspace 类在使用时都是基于进程进行单件对象的实例化。当向这些类请求实例的时候,它们会向您传递单一实例的一个引用,如果该实例还不存在,则首先进行实例的分配和初始化。单件对象充当控制中心的角色,负责指引或协调类的各种服务。如果类在概念上只有一个实例(比如NSWorkspace),就应该产生一个单件实例,而不是多个实例;如果将来某一天可能有多个实例,您可以使用单件实例机制,而不是工厂方法或函数。
69、Singleton(单例模式),也叫单子模式,是一种常用的软件设计模式
在应用这个模式时,单例对象的类必须保证只有一个实例存在。
代码如下:
staticClassA *classA = nil;//静态的该类的实例
+(ClassA *)sharedManager
{
@synchronized(self){
if(!classA) {
classA= [[super allocWithZone:NULL]init];
}
returnclassA;
}
}
+(id)allocWithZone:(NSZone *)zone {
return[[self sharedManager] retain];
}
-(id)copyWithZone:(NSZone *)zone {
returnself;
}
-(id)retain {
returnself;
}
-(NSUIntger)retainCount {
returnNSUIntgerMax;
}
-(oneway void)release {
}
-(id)autorelease {
returnself;
}
-(void)dealloc{
}
十二、KVC 和 KVO 观察者模式
70、什么是 KVC 和 KVO?以及它们之间的关系是什么
答:(1)KVC(键值编码)是一种间接访问对象实例变量的机制,该机制可以 不通过存取方法就可以访问对象的实例变量。非对象类型的变量将被自动封装或 者解封成对象。此外,使用 KVC 能够简化代码。我们需要注意 KVC 有两个较为 明显的缺点,一旦使用 KVC 你的编译器无法检查出错误,即不会对设置的键、 键路径进行错误检查,且执行效率要低于(虽然效率已经很高,你已经感觉不到) 合成存取器方法和自定的 setter 和 getter 方法。因为使用 KVC 键值编码,它必 须先解析字符串,然后在设置或者访问对象的实例变量。
(2)KVO(键值观察)是一种能使得对象获取到其他对象属性变化的通知机制。
(3)实现 KVO 键值观察模式,被观察的对象必须使用 KVC 键值编码来修
改它的实例变量,这样才能被观察者观察到。因此,KVC 是 KVO 的基础或者说 KVO 的实现是建立在 KVC 的基础之上的。
71、 什么是KVO和KVC?
答案:kvc:键 – 值编码是一种间接访问对象的属性使用字符串来标识属性,而不是通过调用存取方法,直接或通过实例变量访问的机制。
很多情况下可以简化程序代码。apple文档其实给了一个很好的例子。
kvo:键值观察机制,他提供了观察某一属性变化的方法,极大的简化了代码。
具体用看到嗯哼用到过的一个地方是对于按钮点击变化状态的的监控。
比如我自定义的一个button
[self addObserver:selfforKeyPath:@"highlighted" options:0 context:nil];
#pragma mark KVO
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)objectchange:(NSDictionary *)change context:(void *)context
{
if ([keyPathisEqualToString:@"highlighted"] ) {
[self setNeedsDisplay];
}
}
对于系统是根据keypath去取的到相应的值发生改变,理论上来说是和kvc机制的道理是一样的。
对于kvc机制如何通过key寻找到value:
“当通过KVC调用对象时,比如:[self valueForKey:@”someKey”]时,程序会自动试图通过几种不同的方式解析这个调用。首先查找对象是否带有 someKey这个方法,如果没找到,会继续查找对象是否带有someKey这个实例变量(iVar),如果还没有找到,程序会继续试图调用 -(id)valueForUndefinedKey:这个方法。如果这个方法还是没有被实现的话,程序会抛出一个NSUndefinedKeyException异常错误。
(cocoachina.com注:Key-Value Coding查找方法的时候,不仅仅会查找someKey这个方法,还会查找getsomeKey这个方法,前面加一个get,或者_someKey以及_getsomeKey这几种形式。同时,查找实例变量的时候也会不仅仅查找someKey这个变量,也会查找_someKey这个变量是否存在。)
设计valueForUndefinedKey:方法的主要目的是当你使用-(id)valueForKey方法从对象中请求值时,对象能够在错误发生前,有最后的机会响应这个请求。这样做有很多好处,下面的两个例子说明了这样做的好处。“
来至cocoa,这个说法应该挺有道理。
因为我们知道button却是存在一个highlighted实例变量.因此为何上面我们只是add一个相关的keypath就行了,
可以按照kvc查找的逻辑理解,就说的过去了。
72、在 objective c 中如何实现 KVO
答:(1)注册观察者(这里我们需要注意,观察者和被观察者不会被保留也不会被释放)
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath
options:(NSKeyValueObservingOptions)options
context:(void *)context;
(2)接收变更通知
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object change:(NSDictionary *)change context:(void *)context;
(3)移除对象的观察者身份
- (void)removeObserver:(NSObject *)observer
forKeyPath:(NSString *)keyPath;
73、代理、通知和 KVO 的区别以及它们的优缺点
答:代理是一种回调机制,且是一对一的关系,而通知是一对多的关系,一个中心对象向所有的观察者提供变更通知(KVO 是被观察者向观察者直接发送通知,这是通知和 KVO 的区别)。
http://blog.youkuaiyun.com/dqjyong/article/details/7685933http://www.apple.com.cn/developer/mac/library/documentation/Cocoa/Conceptual/CocoaFundamentals/CommunicatingWithObjects/chapter_6_section_7.html
十二、MVC设计模式
74、什么是 MVC,如何理解
答:M 表示 model,即数据模型,V 表示 view,即视图,C 表示 viewController,即视图控制器,使用 MVC 的目的是将 M 和 V 的实现代码分离,从而使同一个程序可以使用不同的表现形式。C 存在的目的则是确保 M 和 V 的同步。
75、描述一下iOS SDK中如何实现MVC的开发模式
MVC是模型、试图、控制开发模式,对于iOS SDK,所有的View都是视图层的,它应该独立于模型层,由视图控制层来控制。所有的用户数据都是模型层,它应该独立于视图。所有的ViewController都是控制层,由它负责控制视图,访问模型数据。
76、如何使用Xcode设计通用应用?
答案:使用MVC模式设计应用,其中Model层完成脱离界面,即在Model层,其是可运行在任何设备上,在controller层,根据iPhone与iPad(独有UISplitViewController)的不同特点选择不同的viewController对象。在View层,可根据现实要求,来设计,其中以xib文件设计时,其设置其为universal。
77、MVC是什么?有什么特性?
MVC是一种设计模式,由模型、视图、控制器3部分组成。
模型:保存应用程序数据的类,处理业务逻辑的类
视图:窗口,控件和其他用户能看到的并且能交互的元素
控制器:将模型和试图绑定在一起,确定如何处理用户输入的类
十三、socket http
78、socket连接和http连接的区别
简单说,你浏览的网页(网址以http://开头)都是http协议传输到你的浏览器的, 而http是基于socket之上的。socket是一套完成tcp,udp协议的接口。
HTTP协议:简单对象访问协议,对应于应用层 ,HTTP协议是基于TCP连接的
tcp协议: 对应于传输层
ip协议: 对应于网络层
TCP/IP是传输层协议,主要解决数据如何在网络中传输;而HTTP是应用层协议,主要解决如何包装数据。
Socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,我们才能使用TCP/IP协议。
http连接:http连接就是所谓的短连接,即客户端向服务器端发送一次请求,服务器端响应后连接即会断掉;
socket连接:socket连接就是所谓的长连接,理论上客户端和服务器端一旦建立起连接将不会主动断掉;但是由于各种环境因素可能会是连接断开,比如说:服务器端或客户端主机down了,网络故障,或者两者之间长时间没有数据传输,网络防火墙可能会断开该连接以释放网络资源。所以当一个socket连接中没有数据的传输,那么为了维持连接需要发送心跳消息~~具体心跳消息格式是开发者自己定义的
我们已经知道网络中的进程是通过socket来通信的,那什么是socket呢?socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read–> 关闭close”模式来操作。我的理解就是Socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭),这些函数我们在后面进行介绍。我们在传输数据时,可以只使用(传输层)TCP/IP协议,但是那样的话,如果没有应用层,便无法识别数据内容,如果想要使传输的数据有意义,则必须使用到应用层协议,应用层协议有很多,比如HTTP、FTP、TELNET等,也可以自己定义应用层协议。WEB使用HTTP协议作应用层协议,以封装HTTP文本信息,然后使用TCP/IP做传输层协议将它发到网络上。
1)Socket是一个针对TCP和UDP编程的接口,你可以借助它建立TCP连接等等。而TCP和UDP协议属于传输层 。
而http是个应用层的协议,它实际上也建立在TCP协议之上。
(HTTP是轿车,提供了封装或者显示数据的具体形式;Socket是发动机,提供了网络通信的能力。)
2)Socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,我们才能使用TCP/IP协议。Socket的出现只是使得程序员更方便地使用TCP/IP协议栈而已,是对TCP/IP协议的抽象,从而形成了我们知道的一些最基本的函数接口。
79、利用Socket建立网络连接的步骤
建立Socket连接至少需要一对套接字,其中一个运行于客户端,称为ClientSocket ,另一个运行于服务器端,称为ServerSocket 。
套接字之间的连接过程分为三个步骤:服务器监听,客户端请求,连接确认。
1。服务器监听:服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态,等待客户端的连接请求。
2。客户端请求:指客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。
3。连接确认:当服务器端套接字监听到或者说接收到客户端套接字的连接请求时,就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,双方就正式建立连接。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。
80、http 和 socket 区别
答:http 是客户端用 http 协议进行请求,发送请求时候需要封装 http 请求头,并绑定请求的数据,服务器一般有 web 服务器配合(当然也非绝对)。 http 请求方式为客户端主动发起请求,服务器才能给响应,一次请求完毕后则断开连接,以节省资源。服务器不能主动给客户端响应(除非采取 http 长连接技术)。iphone主要使用类是 NSURLConnection。
scoket 是客户端跟服务器直接使用 socket“套接字”进行连接,并没有规定连接后断开,所以客户端和服务器可以保持连接通道,双方都可以主动发送数据。一般在游戏开发或股票开发这种要求即时性很强并且保持发送数据量比较大的场合使用。主要使用类是 CFSocketRef。
81、网络数据传输一般有几种格式,他们有哪些区别,你又用的哪些框架进行解析的
答:JSON、XML;
JSON:JSON(JavaScript Object Notation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。
1、对象是一个无序的“‘名称/值’对”集合。一个对象以“{”开始,以“}”结束。每个“名称”后跟一个“:”,“‘名称/值’对”之间使用“,”分隔。
2、数组是值(value)的有序集合。一个数组以“[”开始,“]”结束。值之间使用“,”分隔。
XML:可扩展标记语言(extensible Markup Language,简称:XML),是一种标记语言。XML设计用来传送及携带数据信息,不用来表现或展示数据,HTML语言则用来表现数据,此外,XML天生有很好的扩展性,JSON当然也有。不过JSON在 Javascript主场作战,可以存储Javascript 复合对象,有着xml 不可比拟的优势。
82、如何发起一个http同步、异步网络请求
答:(1)同步请求
// 构造 url
NSURL *url = [NSURL URLWithString:@"http://www.taobao.com"];// 创建一个请求对象
NSURLRequest *request = [NSURLRequest requestWithURL:url];NSURLResponse *response;
// 发送同步请求,请求成功后返回数据
NSData *resultData = [NSURLConnection
sendSynchronousRequest:request
returningResponse:&responseNSString *datastring = [[NSString alloc] initWithData:resultData
encoding:NSUTF8StringEncoding];self.resultText.text = datastring;
error:nil];
(2)异步请求
NSURL *url = [NSURL URLWithString:request_url];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// 发送异步请求
[NSURLConnection connectionWithRequest:request
delegate:self];
// 接受加载的数据
- (void)connection:(NSURLConnection
didReceiveData:(NSData *)data {[_data appendData:data];
}
// 数据加载完以后调用
*)connection
- (void)connectionDidFinishLoading:(NSURLConnection*)connection {
NSString *datastring = [[NSString alloc] initWithData:_data
encoding:NSUTF8StringEncoding];self.resultText.text = datastring;
}
十四、指针
83、什么是指针的释放?
具体来说包括两个概念.
1释放该指针指向的内存,只有堆上的内存才需要我们手工释放,栈上不需要.
2将该指针重定向为NULL.
84、数据结构中的指针?
其实就是指向一块内存的地址,通过指针传递,可实现复杂的内存访问.
1.函数指针?
指向一块函数的入口地址.
2.指针作为函数的参数?
比如指向一个复杂数据结构的指针作为函数变量
这种方法避免整个复杂数据类型内存的压栈出栈操作,提高效率.
注意:指针本身不可变,但指针指向的数据结构可以改变.
3.指向指针的指针?
指针指向的变量是一个指针,即具体内容为一个指针的值,是一个地址.
此时指针指向的变量长度也是4位.
85、指针与地址的区别?
1.指针意味着已经有一个指针变量存在,他的值是一个地址,指针变量本身也存放在一个长度为四个字节的地址当中,而地址概念本身并不代表有任何变量存在.
2.指针的值,如果没有限制,通常是可以变化的,也可以指向另外一个地址.
地址表示内存空间的一个位置点,他是用来赋给指针的,地址本身是没有大小概念,指针指向变量的大小,取决于地址后面存放的变量类型.
85、指针与数组名的关系?
其值都是一个地址,但前者是可以移动的,后者是不可变的.
1.怎样防止指针的越界使用问题?
必须让指针指向一个有效的内存地址,
1)防止数组越界
2)防止向一块内存中拷贝过多的内容
3)防止使用空指针
4)防止改变const修改的指针
5)防止改变指向静态存储区的内容
6)防止两次释放一个指针
7)防止使用野指针.
2.指针的类型转换?
指针转换通常是指针类型和void * 类型之前进行强制转换,从而与期望或返回void指针的函数进行正确的交接。
十五、常见iPhone UI笔试题
86、UIViewController生命周期
答:(1)视图控制器的装载:当视图控制器从nib文件而来时,nid中创建一个视图,然后调用viewDidLoad方法,不会调用loadView方法。当没有创建nib文件,创建一个视图控制器时,视图控制器调用会调用loadView方法创建一个空的视图,当你覆盖loadView方法时,需要创建一个视图给视图控制器,或者调用父类的loadView方法,完成视图的创建后,再调用viewDidLoad方法。之后调用viewWillAppear:方法和viewDidAppear方法表示视图已全部渲染完成。
(2)视图控制器的卸载:当设备内存不足时,调用didRecieveMemoryWarning,如果当前的视图控制器可以被安全的释放,那么将调用viewWillUnload:方法和viewDidUnload方法,我们在这两个方法中将视图所拥有的强引用对象释放。当在iOS6中不在调用这个两个方法而是只调用didRecieveMemoryWarning,那么我们就在这个方法中将当前视图控制器中的视图所拥有的对象销毁。
87、ViewController声明周期
ViewController生命周期中有那么多函数,一个重要问题就是什么代码该写在什么地方。
1、init里不要出现创建view的代码。良好的设计,在init里应该只有相关数据的初始化,而且这些数据都是比较关键的数据。init里不要掉self.view,否则会导致viewcontroller创建view。(因为view是lazyinit的)。
2、loadView中只初始化view,一般用于创建比较关键的view如tableViewController的tabView,UINavigationController的navgationBar,不可掉用view的getter(在掉superloadView前),最好也不要初始化一些非关键的view。如果你是从nib文件中创建的viewController在这里一定要首先调用super的loadView方法,但建议不要重载这个方法。
3、viewDidLoad 这时候view已经有了,最适合创建一些附加的view和控件了。有一点需要注意的是,viewDidLoad会调用多次(viewcontroller可能多次载入view,参见图2)。
4、viewWillAppear 这个一般在view被添加到superview之前,切换动画之前调用。在这里可以进行一些显示前的处理。比如键盘弹出,一些特殊的过程动画(比如状态条和navigationbar颜色)。
5、viewDidAppear 一般用于显示后,在切换动画后,如果有需要的操作,可以在这里加入相关代码。
6、viewDidUnload 这时候viewController的view已经是nil了。由于这一般发生在内存警告时,所以在这里你应该将那些不在显示的view释放了。比如你在viewcontroller的view上加了一个label,而且这个label是viewcontroller的属性,那么你要把这个属性设置成nil,以免占用不必要的内存,而这个label在viewDidLoad时会重新创建。
88、什么是响应事件者链
答:当Application Kit在应用程序中构造对象时,会为每个窗口建立响应者链。响应者链中的基本对象是UIWindow对象及其视图层次。在视图层次中级别较低的视图将比级别更高的视图优先获得处理事件或动作消息的机会。NSWindow中保有一个第一响应者的引用,它通常是当前窗口中处于选择状态的视图,窗口通常把响应消息的机会首先给它。对于事件消息,响应者链通常以发生事件的窗
口对应的 NSWindow对象作为结束,虽然其它对象也可以作为下一个响应者被加入到NSWindow对象的后面。
89、简述tableView的重用机制
答:表视图的重用机制是一种节约设备内存的机制,尤其是需要显示大量数据时,显得尤其重要。当我们开始创建表视图的一行时,首先定义一个静态的字符串作为单元格的标识符,然后通过该标识符去检测该表视图中有没有闲置的单元格,如果没有,我们就创建一个新的单元格,并且给它指定一个标识符。如果已有闲置的单元格,则直接从表视图中取出来,给单元重新赋上新的值。初学者容易犯的错误时,当我需要自定义一个单元格时,将自定义实例创建在“括号”外,这样单元格中会出现重叠现象,这是初学者常见的错误。此外,如果当前表视图显示10个单元格,那么11个就可以满足重用的需要。
90、如何适配iOS6
答:iPhone5的分辨率是1136x640,尺寸是568x320,适配iphone5的屏幕首先要添加Default-568h@2x.png作为启动图片,否则上下会有黑边出现。
用宏定义获取设备的尺寸,减少程序中的硬编码。
#define iPhone5 ([UIScreeninstancesRespondToSelector:@selector(currentMode)] ?
CGSizeEqualToSize(CGSizeMake(640,1136), [[UIScreenmainScreen] currentMode].size) : NO)
十六、其他
91、NSRunloop的基本概念
答:Run loops是线程相关的的基础框架的一部分。一个run loop就是一个
事件处理的循环,用来不停的调度工作以及处理输入事件。使用run loop的目的是让你的线程在有工作的时候忙于工作,而没工作的时候处于休眠状态。
92、Dom和Sax解析的优缺点
答:DOM即Document Object Model,中文叫文档对象模型。
SAX即Simple API for XML的简称,中文叫XML简单应用程序接口。
Dom 解析(常见框架是GDataXMLNode)的优点是解析XML变得简单,但是
耗费内存资源,因为它是一次性将 XML 转换成Dom对象。Sax解析XML较为复杂,但是解析的速度快、而且占用内存较小(NSXMLParser)。
93、CALayer的基本概念,它与UIView又是什么关系
答:通常会将 UIView理解为能够在屏幕上直接制图的对象,实际上从技术的角度而言,视图自身并不在窗口上绘图,它只知道如何在CALayer对象上绘图。我们在创建一个UIView实例时,该对象会自动创建一个层(自己创建的这个层称为隐式层)。视图在绘图时,将内容画在自己的层上。当视图在层上完成绘图后,系统会将层拷贝到屏幕上。如果我们创建一个CALayer层的实例,我们称之为显示层,层其实就是一副位图。(层的委托对象是UIView)
94、cocoa touch框架
iPhone OS 应用程序的基础Cocoa Touch框架重用了许多Mac系统的成熟模式,但是它更多地专注于触摸的接口和优化。
UIKit 为您提供了在iPhone OS上实现图形,事件驱动程序的基本工具,其建立在和Mac OS X中一样的Foundation框架上,包括文件处理,网络,字符串操作等。
Cocoa Touch 具有和 iPhone用户接口一致的特殊设计。有了UIKit,您可以使用iPhone OS上的独特的图形接口控件,按钮,以及全屏视图的功能,您还可以使用加速仪和多点触摸手势来控制您的应用。
各色俱全的框架 除了 UIKit外,Cocoa Touch包含了创建世界一流iPhone应用程序需要的所有框架,从三维图形,到专业音效,
甚至提供设备访问 API 以控制摄像头,或通过GPS获知当前位置。
Cocoa Touch 既包含只需要几行代码就可以完成全部任务的强大的Objective-C框架,也在需要时提供基础的C语言API来直接访问系统。这些框架包括:
Core Animation:通过Core Animation,您就可以通过一个基于组合独立图层的简单的编程模型来创建丰富的用户体验。
Core Audio:Core Audio是播放,处理和录制音频的专业技术,能够轻松为您的应用程序添加强大的音频功能。
Core Data:提供了一个面向对象的数据管理解决方案,它易于使用和理解,甚至可处理任何应用或大或小的数据模型。
功能列表:框架分类
下面是 Cocoa Touch中一小部分可用的框架:
音频和视频:Core Audio,OpenAL,Media Library,AV Foundation数据管理 :Core Data,SQLite
图形和动画 :Core Animation,OpenGL ES,Quartz 2D网络:Bonjour,WebKit,BSD Sockets
用户应用:Address Book,Core Location,Map Kit,Store Kit
95、将变量保存到本地,列举两个简单的方法
答:使用 writeToFile方法,将字符串、字典、数组直接保存。将对象保存到
本地,需要实现 NSCoding协议。
96、如何发布我们的应用程序
答:参考真机调试的流程,将程序打包成一个发布的包,最后使用使用application Loader、或者用xCode直接发布。
可参考:http://mobile.51cto.com/iphone-280746.htm
97、setNeedsLayout、setNeedsDisplay
答:调用 setNeedsLayout方法将会调用UIView的layoutSubview,会对视图重新排版;调用setNeedsDisplay方法将会调用UIView的drawRect方法,会对视图重新绘制。(它们都是异步执行的)
98、以下函数主要完成什么事情?
UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]))
答:这个函数被调用的主入口点创建应用程序的对象和应用程序委托和事件循环。注意这个函数虽有返回值,但这个函数在程序没有结束时,永远不会返回。
99、iOS后台可以运行哪些类型的程序,如何自定义一个后台运行的任务
答:audio(音频播放)、location(位置服务)、voip(网络电话),在plist文件中Required backgroud modes属性中添加需要在后台执行的任务。
一般自定义的后台任务只能执行 600s。如果需要自定义一个后台任务超过600s。
if (__count % 500 == 0) {
UIApplication*application = [UIApplication sharedApplication];bgTask=[application
beginBackgroundTaskWithExpirationHandler:^(void){
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;}];
100、找错题
试题1:
voidtest1()
{
char string[10];
char* str1 = "0123456789";
strcpy( string, str1 );
}
试题2:
voidtest2()
{
char string[10], str1[10];
int i;
for(i=0; i<10; i++)
{
str1 = 'a';
}
strcpy( string, str1 );
}
试题3:
voidtest3(char* str1)
{
char string[10];
if( strlen( str1 ) <= 10 )
{
strcpy( string, str1 );
}
}
解答:
试题1字符串str1需要11个字节才能存放下(包括末尾的’\0’),而string只有10个字节的空间,strcpy会导致数组越界;
对试题2,如果面试者指出字符数组str1不能在数组内结束可以给3分;如果面试者指出strcpy(string, str1)调用使得从str1[url=]内存[/url]起复制到string内存起所复制的字节数具有不确定性可以给7分,在此基础上指出库函数strcpy工作方式的给10分;
对试题3,if(strlen(str1) <= 10)应改为if(strlen(str1) < 10),因为strlen的结果未统计’\0’所占用的1个字节。
剖析:
考查对基本功的掌握:
(1)字符串以’\0’结尾;
(2)对数组越界把握的敏感度;
(3)库函数strcpy的工作方式,如果编写一个标准strcpy函数的总分值为10,下面给出几个不同得分的答案:
voidstrcpy( char *strDest, char *strSrc )
{
while( (*strDest++ = * strSrc++) != ‘\0’ );
}
voidstrcpy( char *strDest, const char *strSrc )
//将源字符串加const,表明其为输入参数,加2分
{
while( (*strDest++ = * strSrc++) != ‘\0’ );
}
voidstrcpy(char *strDest, const char *strSrc)
{
//对源地址和目的地址加非0断言,加3分
assert( (strDest != NULL) && (strSrc != NULL) );
while( (*strDest++ = * strSrc++) != ‘\0’ );
}
//为了实现链式操作,将目的地址返回,加3分!
char* strcpy( char *strDest, const char *strSrc )
{
assert( (strDest != NULL) && (strSrc != NULL) );
char *address = strDest;
while( (*strDest++ = * strSrc++) != ‘\0’ );
return address;
}
(4)对strlen的掌握,它没有包括字符串末尾的'\0'。
读者看了不同分值的strcpy版本,应该也可以写出一个10分的strlen函数了,完美的版本为: int strlen( const char*str ) //输入参数const
{
assert( strt != NULL ); //断言字符串地址非0
int len;
while( (*str++) != '\0' )
{
len++;
}
return len;
}
试题4:
voidGetMemory( char *p )
{
p = (char *) malloc( 100 );
}
voidTest( void )
{
char *str = NULL;
GetMemory( str );
strcpy( str, "hello world" );
printf( str );
}
试题5:
char*GetMemory( void )
{
char p[] = "hello world";
return p;
}
voidTest( void )
{
char *str = NULL;
str = GetMemory();
printf( str );
}
试题6:
voidGetMemory( char **p, int num )
{
*p = (char *) malloc( num );
}
voidTest( void )
{
char *str = NULL;
GetMemory( &str, 100 );
strcpy( str, "hello" );
printf( str );
}
试题7:
voidTest( void )
{
char *str = (char *) malloc( 100 );
strcpy( str, "hello" );
free( str );
... //省略的其它语句
}
解答:
试题4传入中GetMemory( char *p )函数的形参为字符串指针,在函数内部修改形参并不能真正的改变传入形参的值,执行完
char*str = NULL;
GetMemory(str );
后的str仍然为NULL;
试题5中
charp[] = "hello world";
returnp;
的p[]数组为函数内的局部自动变量,在函数返回后,内存已经被释放。这是许多程序员常犯的错误,其根源在于不理解变量的生存期。
试题6的GetMemory避免了试题4的问题,传入GetMemory的参数为字符串指针的指针,但是在GetMemory中执行申请内存及赋值语句
*p= (char *) malloc( num );
后未判断内存是否申请成功,应加上:
if( *p == NULL )
{
...//进行申请内存失败处理
}
试题7存在与试题6同样的问题,在执行
char*str = (char *) malloc(100);
后未进行内存是否申请成功的判断;另外,在free(str)后未置str为空,导致可能变成一个“野”指针,应加上:
str= NULL;
试题6的Test函数中也未对malloc的内存进行释放。
剖析:
试题4~7考查面试者对内存操作的理解程度,基本功扎实的面试者一般都能正确的回答其中50~60的错误。但是要完全解答正确,却也绝非易事。
对内存操作的考查主要集中在:
(1)指针的理解;
(2)变量的生存期及作用范围;
(3)良好的动态内存申请和释放习惯。
再看看下面的一段程序有什么错误:
swap(int* p1,int* p2 )
{
int *p;
*p = *p1;
*p1 = *p2;
*p2 = *p;
}
在swap函数中,p是一个“野”指针,有可能指向系统区,导致程序运行的崩溃。在VC++中DEBUG运行时提示错误“AccessViolation”。该程序应该改为:
swap(int* p1,int* p2 )
{
int p;
p = *p1;
*p1 = *p2;
*p2 = p;
}[img=12,12]file:///D/temp/{56068A28-3D3B-4D8B-9F82-AC1C3E9B128C}_arc_d[1].gif[/img]
以上就是花费数天总结下来的面试题,希望对大家有所帮助!
有很多地方还是有很大的改进空间的,希望大家留言指点,谢谢!