iOS面试题-第十四页

作者: 像羽毛那样轻 | 来源:发表于2016-03-30 10:38 被阅读48次

    62.谈谈Object-C的内存管理方式及过程?

    答: 1).当你使用new,alloc和copy方法创建一个对象时,该对象的保留计数器值为1.当你不再使用该对象时,你要负责向该对象发送一条release或autorelease消息.这样,该对象将在使用寿命结束时被销毁.

    2).当你通过任何其他方法获得一个对象时,则假设该对象的保留计数器值为1,而且已经被设置为自动释放,你不需要执行任何操作来确保该对象被清理.如果你打算在一段时间内拥有该对象,则需要保留它并确保在操作完成时释放它.

    3).如果你保留了某个对象,你需要(最终)释放或自动释放该对象.必须保持retain方法和release方法的使用次数相等.

    63.Object-C有私有方法吗?私有变量呢?

    答: objective-c – 类里面的方法只有两种, 静态方法和实例方法. 这似乎就不是完整的面向对象了,按照OO的原则就是一个对象只暴露有用的东西. 如果没有了私有方法的话, 对于一些小范围的代码重用就不那么顺手了. 在类里面声名一个私有方法

    @interface Controller : NSObject { NSString *something; }

    + (void)thisIsAStaticMethod;

    – (void)thisIsAnInstanceMethod;

    @end

    @interface Controller (private) -

    (void)thisIsAPrivateMethod;

    @end

    @private可以用来修饰私有变量

    在Objective‐C中,所有实例变量默认都是私有的,所有实例方法默认都是公有的

    64.Object-C有多继承吗?没有的话用什么代替?cocoa 中所有的类都是NSObject 的子类

    答: 多继承在这里是用protocol 委托代理 来实现的

    你不用去考虑繁琐的多继承 ,虚基类的概念.

    ood的多态特性 在 obj-c 中通过委托来实现.

    65.内存管理 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的)

    66. 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

    67. Objective-C堆和栈的区别?

    答: 管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak。

    申请大小:

    栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在 WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因 此,能从栈获得的空间较小。

    堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。

    碎片问题:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出

    分配方式:堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。

    分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的。

    68. ViewController的didReceiveMemoryWarning怎么被调用:

    答:[supper didReceiveMemoryWarning];

    69.什么时候用delegate,什么时候用Notification?

    答: delegate针对one-to-one关系,用于sender接受到reciever的某个功能反馈值。

    notification针对one-to-one/many/none,reciver,用于通知多个object某个事件。

    70.用预处理指令#define声明一个常数,用以表明1年中有多少秒(忽略闰年问题)

    答:

    #define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL

    我在这想看到几件事情:

    #define 语法的基本知识(例如:不能以分号结束,括号的使用,等等)

    懂得预处理器将为你计算常数表达式的值,因此,直接写出你是如何计算一年中有多少秒而不是计算出实际的值,是更清晰而没有代价的。

    意识到这个表达式将使一个16位机的整型数溢出-因此要用到长整型符号L,告诉编译器这个常数是的长整型数。

    如果你在你的表达式中用到UL(表示无符号长整型),那么你有了一个好的起点。记住,第一印象很重要。

    71.写一个”标准"宏MIN ,这个宏输入两个参数并返回较小的一个。

    答:

    #define MIN(A,B) ((A) <= (B) ? (A) : (B))

    这个测试是为下面的目的而设的:

    标识#define在宏中应用的基本知识。这是很重要的,因为直到嵌入(inline)操作符变为标准C的一部分,宏是方便产生嵌入代码的唯一方

    法,

    对于嵌入式系统来说,为了能达到要求的性能,嵌入代码经常是必须的方法。

    三重条件操作符的知识。这个操作符存在C语言中的原因是它使得编译器能产生比 if-then-else 更优化的代码,了解这个用法是很重要的。

    懂得在宏中小心地把参数用括号括起来

    我也用这个问题开始讨论宏的副作用,例如:当你写下面的代码时会发生什么事?

    least = MIN(*p++, b);

    结果是:

    ((*p++) <= (b) ? (*p++) : (*p++))

    这个表达式会产生副作用,指针p会作三次++自增操作。

    72.关键字const有什么含意?修饰类呢?static的作用,用于类呢?还有extern c的作用

    答:

    const 意味着"只读",下面的声明都是什么意思?

    const int a;

    int const a;

    const int *a;

    int * const a;

    int const * a const;

    前两个的作用是一样,a是一个常整型数。

    第三个意味着a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。

    第四个意思a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。

    最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。

    结论:

    关键字const的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的。

    如果你曾花很多时间清理其它人留下的垃圾,你就会很快学会感谢这点多余的信息。(当然,懂得用const的程序员很少会留下的垃圾让别人来清理的)  通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少bug的出现。

    1).欲阻止一个变量被改变,可以使用 const 关键字。在定义该 const 变量时,通常需要对它进行初

    始化,因为以后就没有机会再去改变它了;

    2).对指针来说,可以指定指针本身为 const,也可以指定指针所指的数据为 const,或二者同时指

    定为 const;

    3).在一个函数声明中,const 可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值;

    4).对于类的成员函数,若指定其为 const 类型,则表明其是一个常函数,不能修改类的成员变量;

    5).对于类的成员函数,有时候必须指定其返回值为 const 类型,以使得其返回值不为“左值”。

    73. 关键字volatile有什么含意?并给出三个不同的例子。

    答:一个定义为 volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。

    下面是volatile变量的几个例子:

    并行设备的硬件寄存器(如:状态寄存器)

    一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)

    多线程应用中被几个任务共享的变量

    74. 一个参数既可以是const还可以是volatile吗? 一个指针可以是volatile 吗?解释为什么。

    答:1).是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。

    2).是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。

    75 . static 关键字的作用:

    答:

    1).函数体内 static 变量的作用范围为该函数体,不同于 auto 变量,该变量的内存只被分配一次,

    因此其值在下次调用时仍维持上次的值;

    2).在模块内的 static 全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问;

    3).在模块内的 static 函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明

    它的模块内;

    4).在类中的 static 成员变量属于整个类所拥有,对类的所有对象只有一份拷贝;

    5).在类中的 static 成员函数属于整个类所拥有,这个函数不接收 this 指针,因而只能访问类的static 成员变量。

    76. 线程与进程的区别和联系?

    答:

    1). 进程和线程都是由操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性

    2). 进程和线程的主要差别在于它们是不同的操作系统资源管理方式。

    3). 进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。

    4.)线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉。所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。

    5). 但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。

    77. 列举几种进程的同步机制,并比较其优缺点。

    答: 原子操作  信号量机制    自旋锁    管程,会合,分布式系统

    78. 进程之间通信的途径

    答:共享存储系统消息传递系统管道:以文件系统为基础

    79. 进程死锁的原因

    答:资源竞争及进程推进顺序非法

    80. 死锁的4个必要条件

    答:互斥、请求保持、不可剥夺、环路

    相关文章

      网友评论

        本文标题:iOS面试题-第十四页

        本文链接:https://www.haomeiwen.com/subject/ojnslttx.html