目录
- block原理及使用
- 多线程方式
- OC内存管理机制
- 代理,通知的区别及使用场景
- 宏
- const,const,extern,volatile等关键字
1.定义block
typedef void(^animations)(void);
typedef void(^completion)(BOOL finished);
2.做过的项目是否涉及网络访问功能,使用什么对象完成网络功能?
ASIHTTPRequest与NSURLConnection
3.简单介绍下NSURLConnection类
NSURLConnection主要用于网络访问
+ sendSynchronousRequest:returningResponse:error:
是同步访问数据,即当前线程会阻塞,并等待request的返回的response
– initWithRequest:delegate:
使用的是异步加载,当其完成网络访问后,会通过delegate回到主线程,并其委托的对象。
4.多线程是什么
多线程是个复杂的概念,按字面意思是同步完成多项任务,提高了资源的使用效率,从硬件、操作系统、应用软件不同的角度去看,多线程被赋予不同的内涵,对于硬件,现在市面上多数的CPU都是多核的,多核的CPU运算多线程更为出色;
- 从操作系统角度,是多任务,现在用的主流操作系统都是多任务的,可以一边听歌、一边写博客;
- 对于应用来说,多线程可以让应用有更快的回应,可以在网络下载时,同时响应用户的触摸操作。
- 在iOS应用中,对多线程最初的理解,就是并发,它的含义是原来先做烧水,再摘菜,再炒菜的工作,会变成烧水的同时去摘菜,最后去炒菜。
5.iOS 中的多线程
iOS中的多线程,是Cocoa框架下的多线程,通过Cocoa的封装,可以让我们更为方便的使用线程,做过C++的同学可能会对线程有更多的理解,比如线程的创立,信号量、共享变量有认识,Cocoa框架下会方便很多,它对线程做了封装,有些封装,可以让我们创建的对象,本身便拥有线程,也就是线程的对象化抽象,从而减少我们的工程,提供程序的健壮性。
GCD是(Grand Central Dispatch)
从系统级别提供的一个易用地多线程类库,具有运行时的特点,能充分利用多核心硬件。GCD的API接口为C语言的函数,函数参数中多数有Block,关于Block的使用参看这里,为我们提供强大的“接口”。NSOperation与Queue
NSOperation是一个抽象类,它封装了线程的细节实现,我们可以通过子类化该对象,加上NSQueue来同面向对象的思维,管理多线程程序。NSThread
NSThread是一个控制线程执行的对象,它不如NSOperation抽象,通过它我们可以方便的得到一个线程,并控制它。但NSThread的线程之间的并发控制,是需要我们自己来控制的,可以通过NSCondition实现。其他多线程
在Cocoa的框架下,通知、Timer和异步函数等都有使用多线程。
6.在项目什么时候选择使用GCD,什么时候选择NSOperation?
NSOperation
项目中使用NSOperation的优点是NSOperation是对线程的高度抽象,在项目中使用它,会使项目的程序结构更好,子类化NSOperation的设计思路,是具有面向对象的优点(复用、封装),使得实现是多线程支持,而接口简单,建议在复杂项目中使用。GCD
项目中使用GCD的优点是GCD本身非常简单、易用,对于不复杂的多线程操作,会节省代码量,而Block参数的使用,会是代码更为易读,建议在简单项目中使用。
7.什么是block
对于闭包(block),有很多定义,其中闭包就是能够读取其它函数内部变量的函数,这个定义即接近本质又较好理解。
8.block 实现原理
Objective-C是对C语言的扩展,block的实现是基于指针
和函数指针
。
从计算语言的发展,最早的goto,高级语言的指针,到面向对象语言的block,从机器的思维,一步步接近人的思维,以方便开发人员更为高效、直接的描述出现实的逻辑(需求)。
- 使用实例
cocoaTouch框架下动画效果的Block的调用
typedef void(^didFinishBlock)(NSObject *obj); // 这就声明了一个didFinishBlock类型的block
@property(nonatomic,copy)didFinishBlock finishBlock;
1声明一个blokc对象,注意对象属性设置为copy,接到block 参数时,便会自动复制一份。
2__block是一种特殊类型,
使用该关键字声明的局部变量,可以被block所改变,并且其在原函数中的值会被改变。
9.关于block
面试时,面试官会先问一些,是否了解block,是否使用过block,这些问题相当于开场白,往往是下面一系列问题的开始,所以一定要如实根据自己的情况回答。
9.1使用block和使用delegate完成委托模式有什么优点?
首先要了解什么是委托模式,委托模式在iOS中大量应用,其在设计模式中是适配器模式中的对象适配器,Objective-C中使用id类型指向一切对象,使委托模式更为简洁。了解委托模式的细节:
- iOS设计模式—-委托模式
- 使用block实现委托模式,其优点是回调的block代码块定义在委托对象函数内部,使代码更为紧凑;
- 适配对象不再需要实现具体某个protocol,代码更为简洁。
9.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对象
10.谈谈Object-C的内存管理方式及过程?
当你使用new,alloc和copy方法创建一个对象时,该对象的保留计数器值为1.当你不再使用该对象时,你要负责向该对象发送一条release或autorelease消息.这样,该对象将在使用寿命结束时被销毁.
当你通过任何其他方法获得一个对象时,则假设该对象的保留计数器值为1,而且已经被设置为自动释放,你不需要执行任何操作来确保该对象被清理.如果你打算在一段时间内拥有该对象,则需要保留它并确保在操作完成时释放它.
如果你保留了某个对象,你需要(最终)释放或自动释放该对象.必须保持retain方法和release方法的使用次数相等.
11.Object-C有私有方法吗?私有变量呢?
- objective-c – 类里面的方法只有两种, 静态方法和实例方法.
- @private可以用来修饰私有变量
- 在Objective‐C中,所有实例变量默认都是私有的,所有实例方法默认都是公有的
12.Object-C有多继承吗?没有的话用什么代替?cocoa 中所有的类都是NSObject 的子类
- 多继承在这里是用protocol 委托代理 来实现的
- ood的多态特性 在 obj-c 中通过委托来实现.
13.内存管理 Autorelease、retain、copy、assign的set方法和含义?
- 1.你初始化(alloc/init)的对象,你需要释放(release)它。例如:
NSMutableArray aArray = [[NSArray alloc] init];
[aArray release];
- 2.你retain或copy的,你需要释放它。例如:
[aArray retain]
[aArray release];
- 3.被传递(assign)的对象,你需要斟酌的retain和release。例如:
obj2 = [[obj1 someMethod] autorelease];
对象2接收对象1的一个自动释放的值,或传递一个基本数据类型(NSInteger,NSString)时:你或希望将对象2进行retain,以防止它在被使用之前就被自动释放掉。但是在retain后,一定要在适当的时候进行释放。
关于索引计数(Reference Counting)的问题
- retain值 = 索引计数(Reference Counting)
- NSArray对象会retain(retain值加一)任何数组中的对象。当NSArray被卸载(dealloc)的时候,所有数组中的对象会 被 执行一次释放(retain值减一)。不仅仅是NSArray,任何收集类(Collection Classes)都执行类似操作。例如 NSDictionary,甚至UINavigationController。
- Alloc/init建立的对象,索引计数为1。无需将其再次retain。
- [NSArray array]和[NSDate date]等
方法
建立一个索引计数为1的对象,但是也是一个自动释放对象。所以是本地临时对象,那么无所谓了。如果是打算在全Class中使用的变量(iVar),则必须retain它。 - 缺省的类方法返回值都被执行了
自动释放
方法。(*如上中的NSArray) - 在类中的卸载方法
dealloc
中,release所有未被平衡的NS对象。(所有未被autorelease,而retain值为1的
)
14.C和obj-c 如何混用
- 1.obj-c的编译器处理后缀为m的文件时,可以识别obj-c和c的代码,处理mm文件可以识别obj-c,c,c++代码,但cpp文件必须只能用c/c++代码,而且cpp文件include的头文件中,也不能出现obj-c的代码,因为cpp只是cpp
- 2.在mm文件中混用cpp直接使用即可,所以obj-c混cpp不是问题
- 3.在cpp中混用obj-c其实就是使用obj-c编写的模块是我们想要的。
如果模块以类实现,那么要按照cpp class的标准写类的定义,头文件中不能出现obj-c的东西,包括#import cocoa的。实现文件中,即类的实现代码中可以使用obj-c的东西,可以import,只是后缀是mm。
如果模块以函数实现,那么头文件要按c的格式声明函数,实现文件中,c++函数内部可以用obj-c,但后缀还是mm或m。
总结:只要cpp文件和cpp include的文件中不包含obj-c的东西就可以用了,cpp混用obj-c的关键是使用接口,而不能直接使用 实现代 码,实际上cpp混用的是obj-c编译后的o文件,这个东西其实是无差别的,所以可以用。obj-c的编译器支持cpp
15.Objective-C堆和栈的区别?
管理方式:
对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak。
申请大小:栈:
在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在 WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因 此,能从栈获得的空间较小。堆:
堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。碎片问题:
对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出分配方式:
堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。分配效率:
栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的。
16.什么时候用delegate,什么时候用Notification?
-
delegate
针对one-to-one关系,用于sender接受到reciever的某个功能反馈值。 -
notification
针对one-to-one/many/none,reciver,用于通知多个object某个事件。
17.用预处理指令#define声明一个常数,用以表明1年中有多少秒(忽略闰年问题)
#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL
我在这想看到几件事情:
-
#define
语法的基本知识(例如:不能以分号结束,括号的使用,等等) - 懂得预处理器将为你计算常数表达式的值,因此,直接写出你是如何计算一年中有多少秒而不是计算出实际的值,是更清晰而没有代价的。
- 意识到这个表达式将使一个16位机的整型数溢出-因此要用到长整型符号
L
,告诉编译器这个常数是的长整型数。 - 如果你在你的表达式中用到
UL(表示无符号长整型)
,那么你有了一个好的起点。记住,第一印象很重要。
18.写一个”标准"宏MIN ,这个宏输入两个参数并返回较小的一个。
#define MIN(A,B) (A) <= (B) ? (A) : (B))
这个测试是为下面的目的而设的:
- 标识
#define
在宏中应用的基本知识。这是很重要的,因为直到嵌入(inline)操作符变为标准C的一部分,宏是方便产生嵌入代码的唯一方
法, - 对于嵌入式系统来说,为了能达到要求的性能,嵌入代码经常是必须的方法。
- 三重条件操作符的知识。这个操作符存在C语言中的原因是它使得编译器能产生比 if-then-else 更优化的代码,了解这个用法是很重要的。
懂得在宏中小心地把参数
用括号括起来
19.关键字const有什么含意?修饰类呢?static的作用,用于类呢?还有extern c的作用
const 意味着"只读",下面的声明都是什么意思?
1. const int a;
- a是一个常整型数。
2. int const a;
- a是一个常整型数。
3. const int *a;
- 意味着a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。
4. int * const a;
- 是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。
5.int const *a const;
- 意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。
结论:
1.关键字const的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的。
2.如果你曾花很多时间清理其它人留下的垃圾,你就会很快学会感谢这点多余的信息。(当然,懂得用const的程序员很少会留下的垃圾让别人来清理的) 通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少bug的出现。
使用技巧
- 欲阻止一个变量被改变,可以使用 const 关键字。在定义该 const 变量时,通常需要对它进行初始化,因为以后就没有机会再去改变它了;
- 对指针来说,可以指定指针本身为 const,也可以指定指针所指的数据为 const,或二者同时指定为 const;
- 在一个函数声明中,const 可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值;
- 对于类的成员函数,若指定其为 const 类型,则表明其是一个常函数,不能修改类的成员变量;
- 对于类的成员函数,有时候必须指定其返回值为 const 类型,以使得其返回值不为“左值”。
20.关键字volatile有什么含意?并给出三个不同的例子。
一个定义为 volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。
下面是volatile变量的几个例子:
- 并行设备的硬件寄存器(如:状态寄存器)
- 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
- 多线程应用中被几个任务共享的变量
21.一个参数既可以是const还可以是volatile吗? 一个指针可以是volatile 吗?解释为什么
1.是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。
2.是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。
22.static 关键字的作用:
函数体内 static 变量的作用范围为该函数体,不同于 auto 变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值;
在模块内的 static 全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问;
在模块内的 static 函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明它的模块内;
在类中的 static 成员变量属于整个类所拥有,对类的所有对象只有一份拷贝;
在类中的 static 成员函数属于整个类所拥有,这个函数不接收 this 指针,因而只能访问类的static 成员变量。
23.线程与进程的区别和联系?
进程和线程都是由操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性
进程和线程的主要差别在于它们是不同的操作系统资源管理方式。
进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。
线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉。所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。
但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
24.列举几种进程的同步机制,并比较其优缺点。
- 原子操作
- 信号量机制
- 自旋锁
- 管程
- 会合
- 分布式系统
25.进程之间通信的途径
共享存储系统消息传递系统管道:以文件系统为基础
26.进程死锁的原因
资源竞争及进程推进顺序非法
更多同类型文章请参考
iOS面试进阶篇(一)
iOS面试进阶篇(二)
iOS面试进阶篇(三)
iOS面试进阶篇(四)
iOS面试进阶篇(五)
书写整理多不容易,觉得写的好的打赏一下吧。