GitHub: https://github.com/LiCheng244/LCUtils
个人博客: http://www.licheng244.com/
1. Object-c的类可以多重继承么?可以实现多个接口么?Category是什么?重写一个类的方式用继承好还是分类好?为什么?
Object-c的类不可以多重继承;可以实现多个接口,通过实现多个接口可以完成C++的多重继承;Category是类别,又叫分类;一般情况用分类好,用Category去重写类的方法,仅对本Category有效,不会影响到其他类与原有类的关系。
2. #import 跟#include 又什么区别,@class呢, #import<> 跟 #import”"又什么区别?
import是Objective-C导入头文件的关键字;
include是C/C++导入头文件的关键字;
使用#import头文件会自动只导入一次,不会重复导入,相当于#include和#pragma once;
c语言解决重复导入头文件的方法是:在头文件的最开始加上宏定义的方法(MYLIB_H)。
@class告诉编译器某个类只是一个类名,当执行时,才去查看类的实现文件,可以解决头文件的相互包含;这种写法叫做类的前置声明。
import<>用来包含系统的头文件,#import””用来包含用户头文件。
3. 属性readwrite,readonly,assign,retain,copy,nonatomic 各是什么作用,在那种情况下用?
readwrite 是可读可写特性;需要生成getter方法和setter方法时
readonly 是只读特性 只会生成getter方法 不会生成setter方法 ;不希望属性在类外改变
assign 是赋值特性,setter方法将传入参数赋值给实例变量;仅设置变量时;
retain 表示持有特性,setter方法将传入参数先保留,再赋值,传入参数的retaincount会+1;
copy 表示拷贝特性,setter方法将传入对象复制一份;需要完全一份新的变量时。
nonatomic 非原子操作,决定编译器生成的setter getter是否是原子操作,atomic表示多线程安全,一般使用nonatomic
4. 写一个setter方法用于完成@property (nonatomic,retain)NSString *name,写一个setter方法用于完成@property(nonatomic,copy)NSString *name
- (void)setName:(NSString *) str
{
[str retain];
[name release];
name = str;
} - (void)setName:(NSString *)str
{
id t = [str copy];
[name release];
name = t;
}
######5. 对于语句NSString*obj = [[NSData alloc] init]; obj在编译时和运行时分别时什么类型的对象?
>编译时是NSString的类型;运行时是NSData类型的对象
######6. 常见的object-c的数据类型有那些, 和C的基本数据类型有什么区别?如:NSInteger和int
>object-c的数据类型有NSString,NSNumber,NSArray,NSMutableArray,NSData等等,这些都是class,创建后便是对象。
而[C语言](http://lib.csdn.net/base/c)的基本数据类型int,只是一定字节的内存空间,用于存放数值;
NSInteger是基本数据类型,并不是NSNumber的子类,当然也不是NSObject的子类。NSInteger是基本数据类型Int或者Long的别名(NSInteger的定义typedef long NSInteger),它的区别在于,NSInteger会根据系统是32位还是64位来决定是本身是int还是Long。
######7. id 声明的对象有什么特性?
>Id 声明的对象具有运行时的特性,即可以指向任意类型的objcetive-c的对象;
######8. Objective-C如何对内存管理的,说说你的看法和解决方法?
>Objective-C的内存管理主要有三种方式ARC(自动内存计数)、手动内存计数、内存池。
######9. 内存管理的几条原则时什么?按照默认法则.哪些关键字生成的对象需要手动释放?在和property结合的时候怎样有效的避免内存泄露?
>谁申请,谁释放。遵循Cocoa Touch的使用原则;
内存管理主要要避免“过早释放”和“内存泄漏”,对于“过早释放”需要注意@property设置特性时,一定要用对特性关键字,对于“内存泄漏”,一定要申请了要负责释放,要细心。
关键字alloc 或new 生成的对象需要手动释放;
设置正确的property属性,对于retain需要在合适的地方释放。
######10. 如何对iOS设备进行性能测试?
>Profile-> Instruments ->Time Profiler
######11. Object C中创建线程的方法是什么?如果在主线程中执行代码,方法是什么?如果想延时执行代码、方法又是什么?
>线程创建有三种方法:`使用NSThread创建`、`使用GCD的dispatch`、`使用子类化的NSOperation,然后将其加入NSOperationQueue`;
在主线程执行代码,方法是`performSelectorOnMainThread`;
如果想延时执行代码可以用`performSelector:onThread:withObject: afterDelay:` 或者使用GCD的函数: `
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{// 2秒后异步执行这里的代码...});`
######12. 浅复制和深复制的区别?
>**浅层复制:**只复制指向对象的指针,而不复制引用对象本身。
**深层复制:**复制引用对象本身。
意思就是说我有个A对象,复制一份后得到A_copy对象后,对于浅复制来说,A和A_copy指向的是同一个内存资源,复制的只不过是是一个指针,对象本身资源还是只有一份,那如果我们对A_copy执行了修改操作,那么发现A引用的对象同样被修改,这其实违背了我们复制拷贝的一个思想。深复制就好理解了,内存中存在了两份独立对象本身。
**用网上一哥们通俗的话将就是:**
浅复制好比你和你的影子,你完蛋,你的影子也完蛋
深复制好比你和你的克隆人,你完蛋,你的克隆人还活着。
######13. 类别的作用?继承和类别在实现中有何区别?
>类别主要有3个**作用:**
(1) 将类的实现分散到多个不同文件或多个不同框架中。
(2) 创建对私有方法的前向引用。
(3) 向对象添加非正式协议。
**category:** 可以在不获悉,不改变原来代码的情况下往里面添加新的方法,只能添加,不能删除修改。并且如果类别和原来类中的方法产生名称冲突,则类别将覆盖原来的方法,因为类别具有更高的优先级。
**继承:** 可以增加、扩展父类方法,并且可以增加属性。
######14. 类别和类扩展的区别?
>category和extensions的不同在于 extensions可以添加属性。另外extensions添加的方法是必须要实现的。extensions可以认为是一个私有的Category。
######15. 代理的作用?
>代理的目的是改变或传递控制链。允许一个类在某些特定时刻通知到其他类,而不需要获取到那些类的指针。可以减少框架复杂度。另外一点,代理可以理解为java中的回调监听机制的一种类似。
######16. 什么是推送消息?
>iOS中消息推送机制又叫消息机制,其包括两类:一类是**本地通知**;另一类是推送通知,也叫**远程通知**。
两种通知在iOS中的表现一致,可以通过横幅或者弹出提醒两种形式告诉用户,并且点击通知可以会打开应用程序。
但是实现原理却完全不同:
**本地通知: **是由本地应用触发的,它是基于时间行为的一种通知形式;
**推送通知:**是由应用服务提供商发起的,通过苹果的APNs(Apple Push Notification Server)发送到应用客户端,如下图:![](http://upload-images.jianshu.io/upload_images/1975627-3d0fc96dc7372a95?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
######17. 什么时候使用NSMutableArray,什么时候使用NSArray?
>当数组在程序运行时,需要不断变化的,使用NSMutableArray;
当数组在初始化后,便不再改变的,使用NSArray。
需要指出的是,使用NSArray只表明的是该数组在运行时不发生改变,即不能往NSAarry的数组里新增和删除元素,但不表明其数组內的元素的内容不能发生改变。
NSArray是线程安全的,NSMutableArray不是线程安全的,多线程使用到NSMutableArray需要注意。
######17. frame和bounds有什么不同?
>**frame**指的是:该view在父view坐标系统中的位置和大小。(参照点是父亲的坐标系统)
**bounds**指的是:该view在本身坐标系统中 的位置和大小。(参照点是本身坐标系统)
######18. 谈谈Object-C的内存管理方式及过程?
>1). 当你使用new,alloc和copy方法创建一个对象时,该对象的保留计数器值为1.当你不再使用该对象时,你要负责向该对象发送一条release或autorelease消息.这样,该对象将在使用寿命结束时被销毁.
2). 当你通过任何其他方法获得一个对象时,则假设该对象的保留计数器值为1,而且已经被设置为自动释放,你不需要执行任何操作来确保该对象被清理.如果你打算在一段时间内拥有该对象,则需要保留它并确保在操作完成时释放它.
3). 如果你保留了某个对象,你需要(最终)释放或自动释放该对象.必须保持retain方法和release方法的使用次数相等.
######19. Objective-C堆和栈的区别?
>**管理方式:**
对于栈来讲,是由编译器自动管理,无需我们手工控制;
对于堆来说,释放工作由程序员控制,容易产生memory leak。
**申请大小:**
**栈:**在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在 WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。
**堆:**堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。
碎片问题:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出
**分配方式:**
堆都是动态分配的,没有静态分配的堆。
栈有2种分配方式:静态分配和动态分配。
静态分配是编译器完成的,比如局部变量的分配。
动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。
**分配效率:**
栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。
堆则是C/C++函数库提供的,它的机制是很复杂的。
######20. static 关键字的作用?
>1). 函数体内 static 变量的作用范围为该函数体,不同于 auto 变量,该变量的内存只被分配一次,
因此其值在下次调用时仍维持上次的值;
2). 在模块内的 static 全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问;
3). 在模块内的 static 函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明它的模块内;
4). 在类中的 static 成员变量属于整个类所拥有,对类的所有对象只有一份拷贝;
5). 在类中的 static 成员函数属于整个类所拥有,这个函数不接收 this 指针,因而只能访问类的static 成员变量。
######21. 线程与进程的区别和联系?
>1). 进程和线程都是由操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性
2). 进程和线程的主要差别在于它们是不同的操作系统资源管理方式。
3). 进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。
4). 线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉。所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。
5). 但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
######22. 关于静态变量static?
>在函数或方法范围外定义的变量以及用static修饰的变量都是静态变量。静态变量的生命周期是从程序开始到程序结束。
类方法中可以使用带有static修饰的静态变量, 但是无论生成多少该类的对象, 都只有一个静态变量, 也就是说多个对象共享一个静态变量。
一个对象对静态变量赋值后, 在使用这个变量之前, 如果有另外一个对象修改了该静态变量的值, 如果不进行同步的话就会发生错误。
利用静态变量的这种性质可以实现对象间的信息共享和消息传递等。
######23. super 和 self 的区别?
>**super: ** super和self不同, 并不确定指向某个对象。super只能被用于调用父类的方法, 不能通过super完成赋值, 也不能把方法的返回值指定为super。
**self: ** 是指收到当前消息的实例变量, 例如:B类继承A类, B类方法中调用[self a]; B类中没有a方法, 是继承自A类中的, A类中a方法调用了b方法[self b]; , 此时这个self 指的是B类的对象。
######24. 动态绑定?
>**动态绑定: **在程序执行时才确定对象的属性和需要响应的消息。
OC中的消息是执行时才去绑定的。运行时系统首先会确定接受者的类型(动态类型识别),然后根据消息名在类的方法列表里选择响应的方法执行,如果没有就到父类中继续寻找, 如果一直到NSObject也没有找到要调用的方法, 就会包不能识别消息的错误。
######25. nil 和 null 等等.
>**nil: **空指针, 表示一个空的对象, 这个的对象的指针指向空。值为0。初始化方法失败的时候通常会返回nil。
######26. 静态类型检查的总结
>OC的静态类型检查是在编译期完成的。
>1). 对于id类型的变量, 调用任何方法都会通过编译(当然调用不恰当的方法会出现运行时错误)
2). id类型的变量和被静态类型的变量之间是可以相互赋值的
3). 被定义为静态类型对象的变量, 如果调用了类或父类中未定义的方法, 编译器就会提示警告
4). 如果是静态类型的变量, 父类类型的实例变量不可以赋值给子类类型的实例变量
5). 如果是静态类型的变量, 子类类型的实例变量可以赋值给父类类型的实例变量
6). 若要判断到底是哪一个类的方法被执行了, 不要看变量所声明的类型, 而要看司机执行时这个变量的类型
7). id类型并不是(NSObject *)类型
######27. 重载
> **重载: **指的是一个函数、运算符或者方法定义有多种功能, 并根据情况来选择合适的功能。
OC 可以通过动态绑定让同一个消息选择器执行不同的功能来实现重载。
######28. 为什么不可以直接访问属性, 而是要通过getter 和 setter方法来实现?
>为了封装。
如果允许直接访问类的实例变量, 那么当类的实现发生了变化, 实例变量被删除或者作用发生了变化时, 所有调用这个类的外部模块都需要修改。
如果使用类getter/setter的形式, 当类的实现发生了变化时则只需要修改getter/setter接口, 外部调用部分不需要做任何修改。
虽然子类可以直接访问父类的实例变量, 但是要尽量用getter/setter方法来访问父类中的实例变量, 这样可以使程序尽可能的低耦合。
######29. 实例变量的可见性
>三种修饰符: private、protected、public
![](http://upload-images.jianshu.io/upload_images/1975627-eda82138d45f322e.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)