最近在简书上看到一篇大牛@J_Knight写的面试题, 发现好多知识点都比较模糊,所有就去各种找答案总结一下,记录一下以后备用~~
如有侵权,告知即删!
一.基础篇
1:为什么说objective -C 是一门动态语言?
(1):Objective-C具有相当多的动态特性,表现为三方面:动态类型(Dynamic typing)、动态绑定(Dynamic binding)和动态加载(Dynamic loading)。动态——必须到运行时(run time)才会做的一些事情。
(2):动态类型:即运行时再决定对象的类型,这种动态特性在日常的应用中非常常见,简单来说就是id类型。事实上,由于静态类型的固定性和可预知性,从而使用的更加广泛。静态类型是强类型,而动态类型属于弱类型,运行时决定接受者。
(3:)动态绑定:基于动态类型,在某个实例对象被确定后,其类型便被确定了,该对象对应的属性和响应消息也被完全确定。
(4):动态加载:根据需求加载所需要的资源,最基本就是不同机型的适配,例如,在Retina设备上加载@2x的图片,而在老一些的普通苹设备上加载原图,让程序在运行时添加代码模块以及其他资源,用户可根据需要加载一些可执行代码和资源,而不是在启动时就加载所有组件,可执行代码可以含有和程序运行时整合的新类。
2:讲一下MVC和MVVM,MVP?
(1):MVC是一种架构模式,M表示MOdel,V表示视图View,C表示控制器Controller:
Model负责存储、定义、操作数据;
View用来展示书给用户,和用户进行操作交互;
Controller是Model和View的协调者,Controller把Model中的数据拿过来给View用。Controller可以直接与Model和View进行通信,而View不能和Controller直接通信。View与Controller通信需要利用代理协议的方式,当有数据更新时,MOdel也要与Controller进行通信,这个时候就要用Notification和KVO,这个方式就像一个广播一样,MOdel发信号,Controller设置监听接受信号,当有数据更新时就发信号给Controller,Model和View不能直接进行通信,这样会违背MVC设计模式。
(2):如何理解MVVM设计模式
ViewModel层,就是View和Model层的粘合剂,他是一个放置用户输入验证逻辑,视图显示逻辑,发起网络请求和其他各种各样的代码的极好的地方。说白了,就是把原来ViewController层的业务逻辑和页面逻辑等剥离出来放到ViewModel层。
View层,就是ViewController层,他的任务就是从ViewModel层获取数据,然后显示。
(3):如何理解MVP设计模式(没用过,找了一下概念)
从字面意思来理解,MVP即Modal View Presenter(模型 视图 协调器),MVP实现了Cocoa的MVC的愿景。MVP的协调器Presenter并没有对ViewController的声明周期做任何改变,因此View可以很容易的被模拟出来。在Presenter中根本没有和布局有关的代码,但是它却负责更新View的数据和状态。
MVP 是第一个如何协调整合三个实际上分离的层次的架构模式,既然我们不希望 View 涉及到 Model,那么在显示的 View Controller(其实就是 View)中处理这种协调的逻辑就是不正确的,因此我们需要在其他地方来做这些事情。例如,我们可以做基于整个 App 范围内的路由服务,由它来负责执行协调任务,以及 View 到 View 的展示。这个出现并且必须处理的问题不仅仅是在 MVP 模式中,同时也存在于以下集中方案中。
MVC和MVP的区别就是,在MVP中M和V没有直接通信。
3:为什么代理要用weak?代理的delegate和dataSource有什么区别?block和代理的区别?
(1) weak是弱引用,一旦不进行使用后,永远不会使用了,就不会产生野指针,也不会造成内存泄露问题.
在说下weak和assign的区别: weak和assign都不会增加引用计数,区别是修饰的对象在释放时所做的操作不同,weak是会把对象置为nil,assign则不会,assign一般适用与基本数据类型
(2):代理的delegate和dataSource有什么区别:一般比较熟悉的就是应用在UITableView中.
UITableView需要一个数据源(dataSource)来显示数据,UITableView会向数据源查询一共有多少行数据以及每一行显示什么数据等。没有设置数据源的UITableView只是个空壳。凡是遵守UITableViewDataSource协议的OC对象,都可以是UITableView的数据源。
通常都要为UITableView设置代理对象(delegate),以便在UITableView触发一下事件时做出相应的处理,比如选中了某一行。凡是遵守了UITableViewDelegate协议的OC对象,都可以是UITableView的代理对象。一般会让控制器充当UITableView的dataSource和delegate
(3):block和代理的区别,一般还会加上Notification,所以一起记录下
blockblock被ObjC看成是对象,它封装了一段代码,这段代码可以在任何时候执行。Blocks可以作为函数参数或者函数的返回值,而其本身又可以带输入参数或返回值。它和传统的函数指针很类似,但是有区别:blocks是inline的,并且它对局部变量是只读的。block类似一些其它Web编程语言中的“匿名函数”。在objc中通常使用block实现代理方法实现的功能,也就是回调。使用代理需要设置代理的数据接收者,而且代理方法是被分离开来处理的,block可以将这些分离的代码放到一个代码块中。
delegatedelegate,又称委托或者代理,它是一种设计模式.delegate主要是用于两个对象之间的交互,并且解除两个通信对象的耦合性,iOS大量使用代理模式,主要是用于视图与使用对象之间的通信交互.
官方文档翻译解释:
代理是一种简单而功能强大的设计模式,这种模式用于一个对象“代表”另外一个对象和程序中其他的对象进行交互。 主对象(这里指的是delegating object)中维护一个代理(delegate)的引用并且在合适的时候向这个代理发送消息。这个消息通知“代理”主对象即将处理或是已经处理完了某一个事件。这个代理可以通过更新自己或是其它对象的UI界面或是其它状态来响应主对象所发送过来的这个事件的消息。或是在某些情况下能返回一个值来影响其它即将发生的事件该如何来处理。代理的主要价值是它可以让你容易的定制各种对象的行为。注意这里的代理是个名词,它本身是一个对象,这个对象是专门代表被代理对象来和程序中其他对象打交道的。委托是objC中使用非常频繁的一种设计模式,它的实现与协议的使用是分不开的.
Notification通知中心概述:通知中心实际上是在程序内部提供了消息广播的一种机制。通知中心不能在进程间进行通信。实际上就是一个二传手,把接收到的消息,根据内部的一个消息转发表,来将消息转发给需要的对象。通知中心是基于观察者模式的,它允许注册、删除观察者。一个NSNotificationCenter可以有许多的通知消息NSNotification,对于每一个NSNotification可以有很多的观察者Observer来接收通知。
使用区别
delegate与block一般用于两个对象1对1之间的通信交互、delegate需要定义协议方法,代理对象需要实现协议方法并且需要建立代理关系才可以实现通信。 block更加简洁,不需要定义繁琐的协议方法,但是如果通信时间比较多的话,建议使用delgate。 Notfication主要用于1对多的通信,而且通信对象之间不需要建立关系,但是使用通知,代码的可读性差。
4:属性的实质是什么?包括哪几个部分?属性默认的关键字都有哪些?@dynamic关键字和@synthesize关键字是用来做什么的?属性的默认关键字是什么?
(1):属性的实质是什么:其实不太懂这个实质是什么意思,一般属性是一个类中用来描述对象的抽象概念,一个类可以有很多属性,一个属性可以描述对象的一个特征.包括:实例变量,setter/getter方法.
(2)属性的关键字:经常使用的关键字有strong,weak,assign,copy,nonatomic,atomic....等。
(3)@dynamic关键字:是开发者自已提供相应的属性声明,@dynamic意思是由开发人员提供相应的代码:对于只读属性需要提供setter,对于读写属性需要提供 setter 和getter。查阅了一些资料确定@dynamic的意思是告诉编译器,属性的获取与赋值方法由用户自己实现, 不自动生成。
(4)@synthesize关键字:是系统自动生成getter和setter属性声明;@synthesize的意思是,除非开发人员已经做了,否则由编译器生成相应的代码,以满足属性声明;
(5):属性的默认关键字是什么(这个不太明白什么意思,求答案?)
5:NSString为什么要用copy关键字,如果用strong会有什么问题?(注意:这里没有说用strong就一定不行。使用copy和strong是看情况而定的)
(1).因为父类指针可以指向子类对象,使用 copy 的目的是为了让本对象的属性不受外界影响,使用 copy 无论给我传入是一个可变对象还是不可对象,我本身持有的就是一个不可变的副本.
(2).如果我们使用是 strong ,那么这个属性就有可能指向一个可变对象,如果这个可变对象在外部被修改了,那么会影响该属性.
这个例子就非常清楚:原文链接
如下所示,当修饰符为copy时,因为NSMutableString是NSString类型的子类,所以可以用指针self.name指向mStr,但是我们知道copy的含义是指当重新赋值时深拷贝新对象再赋值给self.name.
所以此时self.name的指针和mStr的指针指向的对象就不同了,所以当给mStr对象发送方法appendString的时候,修改的只是mStr(此时的值变为mutablestring----addstring),而self.name依然不变(mutablestring----);相反当修饰符为strong时,因为strong的意思是指针指向原对象,并且引用计数+1,所以self.name和mStr指向同一个对象,当修改mStr时self.name也会一起变化。所以为了避免NSString类型的值被修改,一般建议用copy修饰符修饰。
@interface ViewController ()
@property (nonatomic,copy) NSString *name;
//@property (nonatomic,strong) NSString *name;
@end
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableString *mStr = [NSMutableString stringWithFormat:@"mutablestring----"];
[self.name](http://self.name) = mStr;
[mStr appendString:@"addstriing"];//name的修饰符为copy时,name的结果为mutablestring----
NSLog(@"%@",mStr);//name的修饰符为strong时,name的结果为mutablestring----addstriing
NSLog(@"%@",[self.name](http://self.name));
6: 如何令自己所写的对象具有拷贝功能?
若想令自己所写的对象具有拷贝功能,则需实现 NSCopying 协议。如果自定义的对象分为可变版本与不可变版本,那么就要同时实现 NSCopying与 NSMutableCopying协议。
具体步骤:
需声明该类遵从 NSCopying 协议
实现 NSCopying 协议。该协议只有一个方法:- (id)copyWithZone:(NSZone *)zone;
7:可变集合类 和 不可变集合类的 copy 和 mutablecopy有什么区别?如果是集合是内容复制的话,集合里面的元素也是内容复制么?
(1)这里主要涉及到iOS 的深浅拷贝这里有篇很好的参考文章,我先总结下回答上面的问题:
可变集合不管是copy还是mutableCopy都会产生新对象(内容拷贝)
不可变集合copy是浅拷贝(指针拷贝)不会产生新对象,mutableCopy是深拷贝会产生新对象(内容拷贝)
如果是深拷贝集合里面的元素内容也是内容复制.
8:为什么IBOutlet修饰的UIView也适用weak关键字?
参考链接
文章告诉我们:
因为既然有外链那么视图在xib或者storyboard中肯定存在,视图已经对它有一个强引用了。
不过这个回答漏了个重要知识,使用storyboard(xib不行)创建的vc,会有一个叫_topLevelObjectsToKeepAliveFromStoryboard的私有数组强引用所有top level的对象,所以这时即便outlet声明成weak也没关系.
9:nonatomic和atomic的区别?atomic是绝对的线程安全么?为什么?如果不是,那应该如何实现?
atomic:默认是有该属性的,这个属性是为了保证程序在多线程情况下,编译器会自动生成一些互斥加锁代码,避免该变量的读写不同步问题。nonatomic:如果该对象无需考虑多线程的情况,请加入这个属性,这样会让编译器少生成一些互斥加锁代码,可以提高效率。
在iOS中使用同步锁的开销比较大, 这会带来性能问题。一般情况下并不要求属性必须是“原子的”,因为这并不能保证“线程安全”(thread safety),若要实现“线程安全”的操作,还需采用更为深层的锁定机制才醒。(具体怎么做还不清楚,没研究过)
例如:一个线程在连续多次读取某个属性值的过程中有别的线程在同时改写该值,那么即便将属性声明为atomic,也还是会读取到不同的属性值。
10:UICollectionView自定义layout如何实现?
项目中并没用到需要自定义layout的地方,度娘出一篇详解文章奉上链接
11:用StoryBoard开发界面有什么弊端?如何避免?
(1)项目中用到StoryBoard的地方较多谈下想法,首先团队开发中用StoryBoard,如果模块划分的不是那么独立,会有多人共同写一个模块的情况,那最后不要用StoryBoard,应为SVN经常会冲突导致更新下的代码会有问题,可能需要恢复到上导致冲突前一版本才能解决,这样如果我们有要提交的代码可能就需要苦逼的在写一次(想当年我们小组6个人共同开发是,就被一小伙伴坑惨了).但是如果模块划分的比较独立的话可以每个人各自创建一个StoryBoard,想怎么玩都没事.还有别的小伙伴提议每个类都创建一个StoryBoard,这样我并不赞成,不过你喜欢就好~.
12:进程和线程的区别?同步异步的区别?并行和并发的区别?
(1)进程:资源分配的最小独立单元,进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位.
(2)线程:进程下的一个分支,是进程的实体,是CPU调度和分派的基本单元,它是比进程更小的能独立运行的基本单位,线程自己基本不拥有系统资源,只拥有一点在运行中必不可少的资源(程序计数器、一组寄存器、栈),但是它可与同属一个进程的其他线程共享进程所拥有的全部资源。
(3)同步和异步:同步指第一个任务不执行完,不会开始第二个,异步是不管第一个有没有执行完,都开始第二个。
(4):并行和并发:并行性(parallelism)指两个或两个以上事件或活动在同一时刻发生。在多道程序环境下,并行性使多个程序同一时刻可在不同CPU上同时执行。
并发性(Concurrence):指两个或两个以上的事件或活动在同一时间间隔内发生。并发的实质是一个物理CPU(也可以多个物理CPU) 在若干道程序之间多路复用,并发性是对有限物理资源强制行使多用户共享以提高效率。
区别:一个处理器同时处理多个任务和多个处理器或者是多核的处理器同时处理多个不同的任务。
13:线程间通信?
-
线程间通信的体现:
(1 ).一个线程传递数据给另一个线程
(2).在一个线程中执行完特定任务后,转到另一个线程继续执行任务
-
线程间通信常用的方法
(1) NSThread可以先将自己的当前线程对象注册到某个全局的对象中去,这样相互之间就可以获取对方的线程对象,然后就可以使用下面的方法进行线程间的通信了,由于主线程比较特殊,所以框架直接提供了在主线程执行的方法
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5, 2_0);
用法如下:
//点击屏幕开始执行下载方法
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self performSelectorInBackground:@selector(download) withObject:nil];
}
//下载图片
- (void)download
{
// 1.图片地址
NSString *urlStr = @"[http://d.jpg](http://d.jpg)";
NSURL *url = [NSURL URLWithString:urlStr];
// 2.根据地址下载图片的二进制数据
NSData *data = [NSData dataWithContentsOfURL:url];
NSLog(@"---end");
// 3.设置图片
UIImage *image = [UIImage imageWithData:data];
// 4.回到主线程,刷新UI界面(为了线程安全)
[self performSelectorOnMainThread:@selector(downloadFinished:) withObject:image waitUntilDone:NO];
// [self performSelector:@selector(downloadFinished:) onThread:[NSThread mainThread] withObject:image waitUntilDone:YES];
}
- (void)downloadFinished:(UIImage *)image
{
self.imageView.image = image;
NSLog(@"downloadFinished---%@", [NSThread currentThread]);
}
(2). GCD一个线程传递数据给另一个线程,如:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"donwload---%@", [NSThread currentThread]);
// 1.子线程下载图片
NSURL *url = [NSURL URLWithString:@"[http://d.jpg](http://d.jpg)"];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
// 2.回到主线程设置图片
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"setting---%@ %@",
[NSThread currentThread], image);
[self.button setImage:image forState:UIControlStateNormal];
});
});}
14:GCD的一些常用的函数?(group,barrier,信号量,线程同步)
GCD(Grand Central Dispatch)宏大的中央调度,串行队列、并发队列、主线程队列;
同步和异步:同步指第一个任务不执行完,不会开始第二个,异步是不管第一个有没有执行完,都开始第二个。
串行和并行:串行是多个任务按一定顺序执行,并行是多个任务同时执行;代码是在分线程执行,在主线程嘟列中刷新UI。
GCD:GCD核心是将任务放在分发队列中去执行.分发队列分为两种:
串行队列:SerialQueue.如果将任务放在一个串行队列中.那么任务会顺序执行.遵循FIFO,可以实现线程同步.但是当有多个serialQueue时,串行队列和串行队列中的任务可以实现并发执行.串行队列两种:
系统自带的 : 主队列;当把任务放在主队列中时,任务会按照顺序在主队列中执行.
dispatch_queue_t queue = dispatch_get_main_queue();
自己创建的 : 自己创建的队列中的任务会在其他线程中顺序执行.
dispatch_queue_t queue = dispatch_queue_create(“com.xxw.serialQueue", DISPATCH_QUEUE_SERIAL);
并行队列分为两种:
(1).系统自带的: 使用系统提供好的globalQueue会开辟子线程来执行任务,任务的执行是并发执行的.
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
(2).自己创建的: 一般情况下,不回你自己创建并行队列,因为系统提供好的globalQueue(在子线程)可以满足需求.
dispatch_queue_t queue = dispatch_queue_create("com.fy.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
- GCD其他用法
(1).使用dispatch_after 延迟特定的时间去做某事
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ });
(2).dispatch_sync函数会阻塞当前线程,去对应的队列中去执行block中的任务,当任务,执行完之后才会回到原来的线程中dispatch_sync(dispatch_get_main_queue(), ^{ });
(3)dispatch_apply重复执行dispatch_apply(arr.count, dispatch_queue_create("com.fy.apply", DISPATCH_QUEUE_SERIAL), ^(size_t i) {});
(4).barrier 函数在使用的时候队列必须是自己创建的的并行队列,否则barrier顺序起不到作用;
dispatch_queue_t queue = dispatch_queue_create("com.fy.barrier", DISPATCH_QUEUE_CONCURRENT);
dispatch_barrier_async(queue, ^{
dispatch_async(queue, ^{
});
dispatch_async(queue, ^{
});});
(5).使用group_notify函数/ 只有当小组内所有任务完成之后,才会执行group_notify里面的内容(PS:小组内最起码要有一组任务);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_main_queue(), ^{ });
dispatch_group_notify(group, dispatch_get_main_queue(), ^{ });
dispatch_group_async(group, dispatch_get_main_queue(), ^{ });
(6).dispatch_once_t``` static dispatch_once_t oneToken;dispatch_once(&oneToken, ^{ NSLog(@"你就是我的唯一");});
15:如何使用队列来避免资源抢夺?
(1)加各种锁参考文章:链接
16:数据持久化的几个方案(fmdb用没用过)?
数据存储的核心都是写文件。
属性列表:只有NSString、NSArray、NSDictionary、NSData可writeToFile;存储依旧是plist文件。plist文件可以存储的7中数据类型:array、dictionary、string、bool、data、date、number。
对象序列化(对象归档):对象序列化通过序列化的形式,键值关系存储到本地,转化成二进制流。通过runtime实现自动化归档/解档。实现NSCoding协议必须实现的两个方法:1.编码(对象序列化):把不能直接存储到plist文件中得到数据,转化为二进制数据,NSData,可以存储到本地;2.解码(对象反序列化):把二进制数据转化为本来的类型。
SQLite 数据库:大量有规律的数据使用数据库。
CoreData :通过管理对象进行增、删、查、改操作的。它不是一个数据库,不仅可以使用SQLite数据库来保持数据,也可以使用其他的方式来存储数据。如:XML。
17:说一下AppDelegate的几个方法?从后台到前台调用了哪些方法?第一次启动调用了哪些方法?从前台到后台调用了哪些方法?
参考文章链接
(1)从后台到前台调用了哪些方法:
从后台到前台
- (void)applicationWillEnterForeground:(UIApplication *)application { 被称为从后台到活动状态的转换的一部分; 在这里你可以撤消许多在输入背景所做的更改。 }
从后他到前台时会调用 应用程序将要进入活跃状态
- (void)applicationDidBecomeActive:(UIApplication *)application { 在应用程序处于非活动状态时,重新启动已暂停(或尚未启动)的任何任务。 如果应用程序先前在后台,则可选择刷新用户界面。 }
从后台到前台时会调用 在这里可以设置允许后台运行
- (void)applicationDidEnterBackground:(UIApplication *)application { 使用此方法释放共享资源,保存用户数据,使计时器无效,并存储足够的应用程序状态信息以将应用程序恢复到其当前状态,以防以后终止。 如果您的应用程序支持后台执行,则调用此方法,而不是applicationWillTerminate:当用户退出时。 }
(2)第一次启动调用了哪些方法:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
在应用程序启动后覆盖自定义点。
launchOptions中的值:
1.用户直接启动,lauchOptions内无数据;
2.其他应用程序通过openURL:启动,则UIApplicationLaunchOptionsURLKey对应的对象为启动URL(NSURL),
UIApplicationLaunchOptionsSourceApplicationKey对应启动的源应用程序的bundle ID (NSString);
3.本地通知启动,则UIApplicationLaunchOptionsLocalNotificationKey对应的是为启动应用程序的的本地通知对象(UILocalNotification);
4.远程通知启动,则UIApplicationLaunchOptionsRemoteNotificationKey对应的是启动应用程序的的远程通知信息userInfo(NSDictionary);
5.其他键:
UIApplicationLaunchOptionsAnnotationKey userInfo包含带注释属性列表的对象
UIApplicationLaunchOptionsLocationKey 应用程序是响应CoreLocation事件而启动的
UIApplicationLaunchOptionsNewsstandDownloadsKey userInfo包含NKAssetDownload的NSArray
UIApplicationLaunchOptionsBluetoothCentralsKey userInfo包含CBCentralManager恢复标识符的NSArray
UIApplicationLaunchOptionsBluetoothPeripheralsKey userInfo包含CBPeripheralManager恢复的NSArray
UIApplicationLaunchOptionsShortcutItemKey userInfo包含用于启动应用程序的UIApplicationShortcutItem。
键入选项字典传递给应用程序:[will | didFinishLaunchingWithOptions和UIApplicationDidFinishLaunchingNotification的信息
UIApplicationLaunchOptionsUserActivityDictionaryKey 存在用户活动时启动选项中存在的子词典
UIApplicationLaunchOptionsUserActivityTypeKey 键入活动类型的用户活动字典
UIApplicationLaunchOptionsCloudKitShareMetadataKey 此键的存在表示应用程序已启动,以便处理CloudKit共享邀请。 此键的值是共享元数据对象。
return YES;
}
(3)从前台到后台调用了哪些方法:
将要从活跃到不活跃 如:前台到后台 切换应用程序 在此期间,应用程序不接收消息或事件,比如网络电话。
- (void)applicationWillResignActive:(UIApplication *)application { 当应用程序即将从活动状态迁移到非活动状态时发送。 这可能发生在某些类型的临时中断(例如来电或SMS消息)时,或者当用户退出应用程序并且开始转换到后台状态时。 使用此方法暂停正在进行的任务,禁用计时器,并使渲染回调无效。 游戏应该使用这种方法暂停游戏。 }
18:NSCache优于NSDictionary的几点?
NSCache胜过NSDictionary之处在于
(1):当系统资源将要耗尽时,它可以自动删减缓存。如果采用普通的字典,那么就要自己编写挂钩,在系统发出“低内存”通知时手工删减缓存。
(2):NSCache并不会“拷贝”键,而是会“保留”它。此行为用NSDictionary也可以实现,然而需要编写相当复杂的代码。NSCache对象不拷贝键的原因在于:很多时候,键都是不支持拷贝操作的对象来充当的。因此,NSCache不会自动拷贝键,所以说,在键不支持拷贝操作的情况下,该类用起来比字典更方便。
(3):NSCache是线程安全的,而NSDictionary则绝对不具备此优势。
19:实现description方法能取到什么效果?
一般情况下,我们在使用NSLog 和 %@ 输出某个对象时,就会调用这个对象的 description 方法,它的返回值就是 NSString 字符串类型,所以 description 默认实现返回的格式是 <类名: 对象的内存地址>.那么,既然description方法的默认实现是返回类名和对象的内存地址,所以在必要情况下,我们需要重写description方法以达到改变输出结果目的,覆盖description方法的默认实现,重写完description方法后.我们就可以得到我们想要的信息,真正用的到位的话,调试起来会很方便,很省时间,提高效率的。
20:objc使用什么机制管理对象内存?
iOS中采用的是引用计数的机制来管理内存,如果一块内存区域的引用计数不为0,那么就说明有对象持或者是在使用这一块内存,如果引用计数为0的话那么说明这块内存没有对象使用,可以被系统回收掉。iOS借助于引用计数的增减来辅助我们进行内存的申请和释放.内存管理的原则:
(1)、自己创建的对象,自己可以持有(比如以alloc、new、copy、mutableCopy开头的方法可以创建对象);
(2)、不是自己创建的对象也可以持有,通过retain;
(3)、自己持有的对象在不需要使用的时候要负责释放、释放可以通过release或者是autorelease进行释放,
(4)、不是自己持有的对象不能进行释放,比如便利构造器得到的对象。
在内存管理的过程中我们需要谨记的原则就是我们造成的引用计数的增加和我们造成的引用计数的减少要保持一致。在我们使用属性的过程中,要注意不同的语义控制(assign、retain、copy)的setter方法实现的不同,对于retain和copy来说,他们的内部实现都是先把旧值release和把新值retain。另外对于发送autorelease消息的对象会被加到最近的自动释放池中,当自动释放池释放的时候,会给里面的所有对象发送一次release消息。iOS5.0之后苹果推出了ARC、ARC是编译器的特性,不是OC的语言特性,是编译器在静态编译的基础上(command + shift + B),编译器在合适的地方给我们加了retain、release、autorelease这些代码,不用我们自己去手动写这些代码了。ARC中属性的关键字是strong和weak,其中strong和MRC下的retain作用相同,都是持有一个对象,weak和MRC下的assigin类型,是一个弱引用,不持有一个对象,但是weak只能修饰对象类型,不能修饰基本类型,并且weak会在指向的对象被销毁的时候指针自动置nil。