92.自动释放池是什么,如何工作?
一、什么是自动释放池
1、Autorelease pool
自动释放池(Autorelease pool)是OC的一种内存自动回收机制,可以将一些临时变量通过自动释放池来回收统一释放
自动释放池本事销毁的时候,池子里面所有的对象都会做一次release操作
2、autorelease
任何OC对象只要调用autorelease方法,就会把该对象放到离自己最近的自动释放池中(栈顶的释放池)。
二:O-C当中的内存释放,并不是像java/.net那样有一个自动的释放池,开发人员不用去关心有关内存释放的问题,O-C里面的自动释放池比c语言的手动内存管理要好一些,但是相对于java/.net来说又弱一些,所以说O-C当中的释放属于半自动的释放池。
三、如何创建一个自动释放池
//ios5.0新方式
@autoreleasepool
{
}
//ios5.0之前的老方式
NSAutoreleasePool *pool=[[NSAutoreleasePoolalloc]init];
[pool release];
四、自动释放池如何释放对象内存
黄金法则:如果对一个对象使用了alloc,[mutable]copy,retain,那么必须使用相应的release或者autorelease.
93.obj-c的优点
objc优点:
1) Cateogies
2) Posing
3)动态识别
4)指标计算
5)弹性讯息传递
6)不是一个过度复杂的C衍生语言
7) Objective-C与C++可混合编程
缺点:
1)不支援命名空间
2)不支持运算符重载
3)不支持多重继承
4)使用动态运行时类型,所有的方法都是函数调用,所以很多编译时优化方法都用不到。(如内联函数等),性能低劣。
OC没有多重继承,Java也没有C++才有
OC使用协议来实现多重继承
94.什么时候用delegate,什么时候用Notification
参数的不同:使用delegate参数更加直观,使用notification参数不那么直观,所以能使用delegate的地方,尽量使用delegate
传递的长度:有时候你的页面会一次跳好几个页面,那么你想把这个页面的数据传回到底层是很麻烦的事情,因为你要把delegate指针从底层界面一直传上来。
传递多个数据:当你在同一个对象传递给多个对象,用delegate就不可行了,就可以考虑使用notification。
95.类别的作用
1对系统的类或自己定义的类的扩充(只是指,不能声明属性Instance variable)e.g. base64 MD5.但是不能保证所有的方法都能被调用。尤其是在指定一个与系统的方法相同的方法时一定要注意。
文件名:NSString+additions.h
NSString+additions.m
@interface NSString (Base64)
2可以声明私有方法。
ZYViewController.m
@interface kViewController (additions)
//{
//BOOL isOK;
//}
- (void)ok;
@property
@end
3可以声明分散类的方法(NSIndexPath)
分散类体现出类别不仅可以扩充系统的方法,而且可以扩充自己定义的类。
由第3个作用可知:不管是否使用的分散类在任何的.h文件中,我只考虑类别中的方法包裹在@interface className ..@end,那么我只明确将来类别中的方法就属于className这个类。
96.浅复制和深复制的区别
简单的来说就是,在有指针的情况下,浅拷贝只是增加了一个指针指向已经存在的内存,
而深拷贝就是增加一个指针并且申请一个新的内存,使这个增加的指针指向这个新的内存,采用深拷贝的情况下,
释放内存的时候就不会出现在浅拷贝时重复释放同一内存的错误!
我列举一个例子来说吧:
你正在编写C++程序中有时用到,操作符的重载。最能体现深层拷贝与浅层拷贝的,就是‘=’的重载。
看下面一个简单的程序:
class string{
char *m_str;
public:
string(char *s){
m_str=s;
}
string()
{};
String & operator=(const string s){
m_str=s.m_str;
return *this
}
};
int main(){
string s1("abc"),s2;
s2=s1;
cout<
上面的=重载其是就是实现了浅拷贝原因。是由于对象之中含有指针数据类型.s1,s2恰好指向同一各内存。所以是浅拷贝。而你如果修改一下原来的程序:
string&operator=(const string&s){
if(strlen(m_str)!=strlen(s.m_str))
m_str=new char[strlen(s.m_str)+1];
if(*this!=s)
strcopy(m_str,s.m_str);
return *this;
}
这样你就实现了深拷贝,原因是你为被赋值对象申请了一个新的内存所以就是深拷贝。
97.代理的作用
代理的目的是改变或传递控制链。允许一个类在某些特定时刻通知到其他类,而不需要获取到那些类的指针。可以减少框架复杂度。
另外一点,代理可以理解为java中的回调监听机制的一种类似。
98.我们说的OC是动态运行时语言是什么意思?
多态。
主要是将数据类型的确定由编译时,推迟到了运行时。
这个问题其实浅涉及到两个概念,运行时和多态。
简单来说,运行时机制是我们直到运行时才去决定一个对象的类别,以及调用该类别对象指定方法。
多态:不同对象以自己的方式响应相同的消息的能力叫做多态。
意思就是假设生物类(life)都用有一个相同的方法-eat;那人类属于生物,猪也属于生物,都继承了life后,实现各自的eat,但是调用是我们只需调用各自的eat方法。
也就是不同的对象以自己的方式响应了相同的消息(响应了eat这个选择器)。
因此也可以说,运行时机制是多态的基础。
98.什么是ARC?请简述一下ARC的原理。
1)ARC是iOS 5推出的新功能,全称叫ARC(Automatic Reference Counting)。简单地说,就是代码中自动加入了retain/release,原先需要手动添加的用来处理内存管理的引用计数的代码可以自动地由编译器完成了
2)、ARC的规则就是只要对象没有强指针引用,就会被释放掉,换而言之只要还有一个强引用指针变量指向对象,那么这个对象就会存在内存中。弱指针指向的对象,会被自动变成空指针(nil指针),从而不会引发野指针错误。
99.简述视图控制器的生命周期
1)、init函数(init;initWithFrame;initWithCoder;等)--初始化
2)、awakeFromNib--在loadView之前的工作放在这里
3)、viewDidLoad--注意,一个ViewController一个生命周期内这个函数只会调用一次
4)、viewWillAppear--view将要出现,每次View消失再出现都会调用
5)、viewWillLayoutSubviews--简要对子试图进行布局
6)、viewDidLayoutSubivews--完成对子试图布局
7)、viewDidAppear--视图将要出现在屏幕上
---上述代码不含部分
8)、viewWillDisappear--View将要消失
9)viewDidDisappear--View已经消失
100.描述一下线程的生命周期
新建(new Thread)--> 就绪(runnable)--> 运行(running) --> 死亡(dead)--> 堵塞(blocked)
101.常用的设计模式
1)、代理模式2)、观察者模式3)、MVC模式4)、单例模式5)工厂模式
102.如何增强iOS应用程序的性能。
初级
1、使用ARC进行内存管理
2.在适当的情况下使用reuse Identifier
3.尽可能将View设置为不透明(Opaque)
4.避免臃肿的XIBs
5.不要阻塞主线程
6.让图片的大小跟UIImageView一样
7.选择正确的集合
8.使用GZIP压缩
中级:
9.重用和延迟加载View
10.缓存、缓存、缓存
11.考虑绘制
12.处理内存警告
13.重用花销很大的对象
14.使用Sprite Sheets
15.避免重新处理数据
16.选择正确的数据格式
17.设置适当的背景图片
18.降低Web内容的影响
19.设置阴影路径
20.优化TableView
21.选择正确的数据存储方式
高级
22.加速启动时间
23.使用Autorelease Pool
24.缓存图片—或者不缓存
25.尽量避免Date格式化
89.static关键字的作用
在C语言中,关键字static有三个明显的作用:
1).在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。
2).在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。
3).在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。
4)设置变量的存储域,函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值;
5)限制变量的作用域,在模块内的static全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问;
6)限制函数的作用域,在模块内的static函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明它的模块内;
7)在类中的static成员变量意味着它为该类的所有实例所共享,也就是说当某个类的实例修改了该静态成员变量,其修改值为该类的其它所有实例所见;
8)在类中的static成员函数属于整个类所拥有,这个函数不接收this指针,因而只能访问类的static成员变量。
103.属性readwrite , readonly , assign , retain , copy , nonatomic ,各是什么作用,在哪种情况下用?
assign用于简单数据类型,如NSInteger,double,bool,
retain和copy用于对象,
readwrite是可读可写特性;需要生成getter方法和setter方法时
readonly是只读特性只会生成getter方法不会生成setter方法;不希望属性在类外改变
assign是赋值特性,setter方法将传入参数赋值给实例变量;仅设置变量时;
retain表示持有特性,setter方法将传入参数先保留,再赋值,传入参数的retaincount会+1;
copy表示赋值特性,setter方法将传入对象复制一份;需要一份新的变量时。
nonatomic非原子操作,atomic原子性操作。原子性指的是一个操作不可以被中途cpu暂停然后调度,即不能被中断,要不就执行完,要不就不执行,就是为了多线程安全的。
一般使用nonatomic。
104.内存管理的几条原则是什么?按照默认法则,哪些关键字生成的对象需要手动释放,在和property结合的时候怎样有效的避免内存泄露?
当使用new、alloc或copy方法创建一个对象时,该对象引用计数器为1。如果不需要使用该对象,可以向其发送release或autorelease消息,在其使用完毕时被销毁。
如果通过其他方法获取一个对象,则可以假设这个对象引用计数为1,并且被设置为autorelease,不需要对该对象进行清理,如果确实需要retain这个对象,则需要使用完毕后release。
如果retain了某个对象,需要release或autorelease该对象,保持retain方法和release方法使用次数相等。
使用new、alloc、copy关键字生成的对象和retain了的对象需要手动释放。设置为autorelease的对象不需要手动释放,会直接进入自动释放池。
105.描述下tableView cell的重用机制,谈谈你是如何优化UITableView
//重用机制简单的说意思一行一行的cell都是在复用的,滑动tableview的时候,刚离开视图的cell会被放到复用池中,等下一个cell需要显示时,会先看复用池中有没有cell如果有的时候,就从复用池中拿出来cell,没有的话就重新创建cell
1、使用不透明视图。
不透明的视图可以极大地提高渲染的速度。因此如非必要,可以将table cell及其子视图的opaque属性设为YES(默认值)。
其中的特例包括背景色,它的alpha值应该为1(例如不要使用clearColor);图像的alpha值也应该为1,或者在画图时设为不透明。
2、不要重复创建不必要的tablecell。
前面说了,UITableView只需要一屏幕的UITableViewCell对象即可。因此在cell不可见时,可以将其缓存起来,而在需要时继续使用它即可。
而UITableView也提供了这种机制,只需要简单地设置一个identifier即可:
staticNSString *CellIdentifier = @"xxx";
UITableViewCell *cell = [tableViewdequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell =[[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefaultreuseIdentifier:CellIdentifier] autorelease];
}
值得一提的是,cell被重用时,它内部绘制的内容并不会被自动清除,因此你可能需要调用setNeedsDisplayInRect:或setNeedsDisplay方法。
此外,在添加table cell的时候,如果不需要动画效果,最好不要使用insertRowsAtIndexPaths:withRowAnimation:方法,而是直接调用reloadData方法。因为前者会对所有indexPaths调用tableView:cellForRowAtIndexPath:方法,即便该cell并不需要显示(不知道是不是bug),这就可能创建大量多余的cell。勘误:只是在模拟器上测试如此,真机调试时没有这种bug。
3、减少视图的数目。
UITableViewCell包含了textLabel、detailTextLabel和imageView等view,而你还可以自定义一些视图放在它的contentView里。然而view是很大的对象,创建它会消耗较多资源,并且也影响渲染的性能。
如果你的tablecell包含图片,且数目较多,使用默认的UITableViewCell会非常影响性能。奇怪的是,使用自定义的view,而非预定义的view,明显会快些。
当然,最佳的解决办法还是继承UITableViewCell,并在其drawRect:中自行绘制:
-(void)drawRect:(CGRect)rect {
if (image) {
[image drawAtPoint:imagePoint];
self.image = nil;
} else {
[placeHolder drawAtPoint:imagePoint];
}
[textdrawInRect:textRect withFont:font lineBreakMode:UILineBreakModeTailTruncation];
}
不过这样一来,你会发现选中一行后,这个cell就变蓝了,其中的内容就被挡住了。最简单的方法就是将cell的selectionStyle属性设为UITableViewCellSelectionStyleNone,这样就不会被高亮了。
此外还可以创建CALayer,将内容绘制到layer上,然后对cell的contentView.layer调用addSublayer:方法。这个例子中,layer并不会显著影响性能,但如果layer透明,或者有圆角、变形等效果,就会影响到绘制速度了。解决办法可参见后面的预渲染图像。
4、不要做多余的绘制工作。
在实现drawRect:的时候,它的rect参数就是需要绘制的区域,这个区域之外的不需要进行绘制。
例如上例中,就可以用CGRectIntersectsRect、CGRectIntersection或CGRectContainsRect判断是否需要绘制image和text,然后再调用绘制方法。
5、预渲染图像。
你会发现即使做到了上述几点,当新的图像出现时,仍然会有短暂的停顿现象。解决的办法就是在bitmap context里先将其画一遍,导出成UIImage对象,然后再绘制到屏幕,详细做法可见《利用预渲染加速iOS设备的图像显示》。
6、不要阻塞主线程。
做到前几点后,你的tableview滚动时应该足够流畅了,不过你仍可能让用户感到不爽。常见的现象就是在更新数据时,整个界面卡住不动,完全不响应用户请求。
出现这种现象的原因就是主线程执行了耗时很长的函数或方法,在其执行完毕前,无法绘制屏幕和响应用户请求。其中最常见的就是网络请求了,它通常都需要花费数秒的时间,而你不应该让用户等待那么久。
解决办法就是使用多线程,让子线程去执行这些函数或方法。这里面还有一个学问,当下载线程数超过2时,会显著影响主线程的性能。因此在使用ASIHTTPRequest时,可以用一个NSOperationQueue来维护下载请求,并将其maxConcurrentOperationCount设为2。而NSURLRequest则可以配合GCD来实现,或者使用NSURLConnection的setDelegateQueue:方法。
当然,在不需要响应用户请求时,也可以增加下载线程数,以加快下载速度:
-(void)scrollViewDidEndDragging:(UIScrollView *)scrollViewwillDecelerate:(BOOL)decelerate {
if (!decelerate) {
queue.maxConcurrentOperationCount = 5;
}
}
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
queue.maxConcurrentOperationCount = 5;
}
-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
queue.maxConcurrentOperationCount = 2;
}
此外,自动载入更新数据对用户来说也很友好,这减少了用户等待下载的时间。例如每次载入50条信息,那就可以在滚动到倒数第10条以内时,加载更多信息:
-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell*)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
if (count - indexPath.row< 10 && !updating) {
updating = YES;
[self update];
}
}// update方法获取到结果后,设置updating为NO还有一点要注意的就是当图片下载完成后,如果cell是可见的,还需要更新图像:
NSArray*indexPaths = [self.tableView indexPathsForVisibleRows];
for (NSIndexPath *visibleIndexPath in indexPaths) {
if (indexPath == visibleIndexPath) {
MyTableViewCell *cell =(MyTableViewCell *)[self.tableView cellForRowAtIndexPath:indexPath];
cell.image= image;
[cell setNeedsDisplayInRect:imageRect];
break;
}
}//也可不遍历,直接与头尾相比较,看是否在中间即可。最后还是前面所说过的insertRowsAtIndexPaths:withRowAnimation:方法,插入新行需要在主线程执行,而一次插入很多行的话(例如50行),会长时间阻塞主线程。而换成reloadData方法的话,瞬间就处理完了。
106.做过IM开发么,谈谈对XMPPFramework的了解
//XMPPFramework是一个OS X/iOS平台的开源项目,使用Objective-C实现了XMPP协议(RFC-3920),同时还提供了用于读写XML的工具,大大简化了基于XMPP的通信应用的开发。
XMPP中常用对象们
XMPPStream:xmpp基础服务类
XMPPRoster:好友列表类
XMPPRosterCoreDataStorage:好友列表(用户账号)在core
data中的操作类
XMPPvCardCoreDataStorage:好友名片(昵称,签名,性别,年龄等信息)在coredata中的操作类
XMPPvCardTemp:好友名片实体类,从数据库里取出来的都是它
xmppvCardAvatarModule:好友头像
XMPPReconnect:如果失去连接,自动重连
XMPPRoom:提供多用户聊天支持
XMPPPubSub:发布订阅
源码地址:http://code.google.com/p/xmppframework/,目前需要使用git才能download到源码。
107.你是如何实现多屏幕适配的
autoResizing
autoLayout
sizeClass
1、autoLayout
帮我们确定在不同设备、不同(父view)环境下,同一个可视单元所应具有合适的位置和尺寸(任何两个视图的关系都可以确定)
1. autoLayout的用法:
ï直接建立约束条件
•[self.viewaddConstraint: [NSLayoutConstraintconstraintWithItem:blueView attribute:NSLayoutAttributeLeftrelatedBy:NSLayoutRelationEqual toItem:redView attribute:NSLayoutAttributeLeftmultiplier:1 constant:0]];
这样虽然代码量比较大,但是是绝对可行的办法,也是使用autoLayout最根本的办法之一。
ï使用VFL语言
- (void)viewDidLoad {
[super viewDidLoad];
UIButton *button=[[UIButtonalloc]init];
[button setTitle:@"点击一下"forState:UIControlStateNormal];
button.translatesAutoresizingMaskIntoConstraints=NO;
[button setBackgroundColor:[UIColorblackColor]];
[self.view addSubview:button];
NSArray *constraints1=[NSLayoutConstraint
constraintsWithVisualFormat:@"H:|-[button]-|"options:0 metrics:nil
views:NSDictionaryOfVariableBindings(button)];
NSArray *constraints2=[NSLayoutConstraint
constraintsWithVisualFormat:@"V:|-20-[button(==30)]"options:0metrics:nil
views:NSDictionaryOfVariableBindings(button)];
[self.viewaddConstraints:constraints1];
[self.viewaddConstraints:constraints2];
}
ï使用使用第三方库,如:Masonry、UIView+AutoLayout……
autoLayout的好处:
ï你基本上可以不用考虑3.5寸和4寸以及即将上市的x.x寸屏幕不同分辨率的问题,你终于可以不用在viewDidLoad方法里判断不同分辨率下,不同控件应该放在哪里,或者针对不同分辨率写不同的storyboard和xib;
ï你可以抛弃那些根据不同文字来计算tableViewCell、UILabel高度的代码了,因为autolayout会帮你自动计算好;
ï如果你的布局在横屏竖屏下变化不是特别大,你不用再为横着竖着写两套代码或者写两个storyboard/xib了;
2.sizeClass
在iOS8中,新增了Size Classes特性,它是对当前所有iOS设备尺寸的一个抽象。那我们就只把屏幕的宽和高分别分成三种情况:Compact:紧凑、Regular:宽松、Any:任意。
这样宽和高三三一整合,一共9中情况。如下图所示,针对每一种情况。我们可以在每种情况下设置不同的布局(包括控件的约束,甚至是控件是否显示)
sizeClass.png
对sizeClass的理解:sizeClass的实质是将iOS屏幕分成了不同的抽象概念,这些不同的抽象组合,对应着不同的设备屏幕。所以,利用sizeClass可以针对同一套UI,来适配所有的屏幕。注意:这些所有的适配,都是利用autoLayout来实现的,sizeClass只是负责提供不同的屏幕尺寸。
108.用变量a给出下面的定义
a)一个整型数(An integer)
b)一个指向整型数的指针(A pointer to an integer)
c)一个指向指针的的指针,它指向的指针是指向一个整型数(A pointer to a pointer to an integer)
d)一个有10个整型数的数组(An array of 10 integers)
e)一个有10个指针的数组,该指针是指向一个整型数的(An array of 10 pointers to integers)
f)一个指向有10个整型数数组的指针(A pointer to an array of 10 integers)
g)一个指向函数的指针,该函数有一个整型参数并返回一个整型数(A pointer to a function that takes an integer as an argument and returns an integer)
h)一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数
a) int a; // An integer
b) int *a; // A pointer to an integer
c) int **a; // A pointer to a pointer to an integer
d) int a[10]; // An array of 10 integers
e) int *a[10]; // An array of 10 pointers to integers
f) int (*a)[10]; // A pointer to an array of 10 integers
g) int (*a)(int);
// A pointer to a function a that takes an integer argument and returns an integer
h) int (*a[10])(int);
109.简单说明你对block的理解以及使用block有什么好处。
block是对象,它封装了一段代码,这段代码可以在任何时候执行。block可以作为函数参数或者函数的返回值,而其本身又可以带输入参数或返回值。它和传统的函数指针很类似,但是有区别:block是inline的,并且它对局部变量是只读的。
好处: Blocks更清晰。比如一个viewController中有多个弹窗事件,Delegate就得对每个事件进行判断识别来源。而Blocks就可以在创建事件的时候区分开来了。这也是为什么现在苹果API中越来越多地使用Blocks而不是Delegate。
110.setValue:forKey和setObject:forKey的区别是什么?
答: 1, setObject:forkey:中value是不能够为nil的,不然会报错。
setValue:forKey:中value能够为nil,但是当value为nil的时候,会自动调用removeObject:forKey方法
2, setValue:forKey:中key的参数只能够是NSString类型,而setObject:forKey:的可以是任何类型
111.iOS 使用@try ...@catch...@finally捕获异常
Object - C提供了@try...@catch...@finally来捕获异常。最近在项目中有常用到,用于处理iOS各个不同版本之间的差异。在使用时将可能异常的代码放在@try后面的代码块中,当程序发生异常时,这个异常就可以被@catch会捕捉。
Object - C将可能出现错误的代码放在@try块中,所以的异常处理逻辑都可以放在@catch块中处理,最后使用@finally块来回收资源。异常处理机制语法:
catch是抓取代码块1中的异常
代码块2是出异常后的处理
代码块3是不管出不出异常都会执行,如果代1或代2中有return,代3会在return后执行
总结:
1、不管有木有出现异常,finally块中代码都会执行;
2、当try和catch中有return时,finally仍然会执行;
3、finally是在return后面的表达式运算后执行的(此时并没有返回运算后的值,而是先把要返回的值保存起来,管finally中的代码怎么样,返回的值都不会改变,任然是之前保存的值),所以函数返回值是在finally执行前确定的;
4、finally中最好不要包含return,否则程序会提前退出,返回值不是try或catch中保存的返回值。
这个异常捕获机制就是哪里用到就写到那里
@try {
代码块1(可能出现异常的语句)
//执行的代码,其中可能有异常。一旦发现异常,则立即跳到catch执行。否则不会执行catch里面的内容
} @catch (NSException *exception) {
代码块2(发生异常时进行处理)
//除非try里面执行代码发生了异常,否则这里的代码不会执行
} @finally {
代码块3(始终要进行处理的语句)
//不管什么情况都会执行,包括try catch 里面用了return ,可以理解为只要执行了try或者catch,就一定会执行 finally
}
112.简单说明你对block的理解以及使用block有什么好处
block是对象,它封装了一段代码,这段代码可以在任何时候执行。block可以作为函数参数或者函数的返回值,而其本身又可以带输入参数或返回值。它和传统的函数指针很类似,但是有区别:block是inline的,并且它对局部变量是只读的。
好处: Blocks更清晰。比如一个viewController中有多个弹窗事件,Delegate就得对每个事件进行判断识别来源。而Blocks就可以在创建事件的时候区分开来了。这也是为什么现在苹果API中越来越多地使用Blocks而不是Delegate。
void(^ablock)(int) = ^(int xxx){
NSLog(@"%d",xxx);
xxx++;
NSLog(@"%d",xxx);
};
void(^bblock)() = ^(){
NSLog(@"这是block");
};
ablock(5);
bblock();
113.setValue:forKey和setObject:forKey的区别是什么?
1, setObject:forkey:中value是不能够为nil的,不然会报错。
setValue:forKey:中value能够为nil,但是当value为nil的时候,会自动调用removeObject:forKey方法
2, setValue:forKey:中key的参数只能够是NSString类型,而setObject:forKey:的可以是任何类型
[dic setObject:<#(nonnull id)#> forKey:<#(nonnull id<NSCopying>)#>];
[dic setValue:<#(nullable id)#> forKey:<#(nonnull NSString *)#>];
114.同步和异步
1.同步意为着线程阻塞,在主线程中使用此方法会不响应任何用户事件。所以,在应用程序设计时,大多被用在专门的子线程增加用户体验,或用异步请求代替。
2.异步请求的好处是不阻塞当前线程,但相对于同步请求略为复杂,至少要添加两个回调方法来获取异步事件
从用户的体验来说,异步请求数据的APP比同步请求的APP操作更加流畅,快捷
115.谈谈Object-C的内存管理方式及过程
从一段内存被申请之后,就存在一个变量用于保存这段内存被使用的次数,我们暂时把它称为计数器,当计数器变为0的时候,那么就是释放这段内存的时候,比如说,当在程序A里面一段内存被成功申请完成之后,那么这个计数器就从0变成了1(我们把这个过程叫做alloc)然后程序B也需要使用这个内存,那么计数器就从1变成了2(我们把这个过程叫做retain)紧接着程序A不再需要这段内存了,那么程序A就把这个计数器减1(我们把这个过程叫做release)程序B也不再需要这段内存的时候,那么也把计数器减1(这个过程还是release)当系统(也就是Foundation)发现这个计数器变成了0,那么就会调用内存回收程序把这段内存回收(我们把这个过程叫做dealloc)
[a alloc] +1
[b retain] +1
[a release] -1
[b release] -1
[a dealloc] nil
116.Objective-C堆和栈的区别?
管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生内存泄漏(memory leak)。
申请大小:
栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示内存溢出(overflow)。因此,能从栈获得的空间较小。
堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。
碎片问题:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出
分配方式:堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。
分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的。
117.__block和__weak修饰符的区别是什么?
1,在MRC时代,__block修饰,可以避免循环引用;ARC时代,__block修饰,同样会引起循环引用问题;
2,__block不管是ARC还是MRC模式下都可以使用,可以修饰对象,还可以修饰基本数据类型;
3,__weak只能在ARC模式下使用,也只能修饰对象,不能修饰基本数据类型;
4,__block对象可以在block中被重新赋值,__weak不可以;
118.Http通信协议的原理,与Socket协议的区别有哪些?
HTTP协议:简单对象访问协议,对于应用层,HTTP协议是基于TCP连接的
tcp协议:对应于传输层
ip协议:对应于网络层
TCP/IP是传输层协议,主要解决数据如何在网络中传输;而HTTP是应用层协议,主要解决如何包装数据。
Socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,才能使用TCP/IP协议。
http连接:http连接就是所谓的短连接,即客户端向服务器端发送一次请求,服务器端响应后连接即会断掉;
socket连接:socket连接就是所谓的长连接,理论上客户端和服务器端一旦建立起连接将不会主动断掉;但是由于各种环境因素可能会是连接断开,比如说:服务器端或客户端主机down了,网络故障,或者两者之间长时间没有数据传输,网络防火墙可能会断开该连接以释放网络资源。
119.写一个单例模式
+(SingalClass*)shareInstance {
static SingalClass *single;
static dispatch_once_t once;
dispatch_once(&once, ^{
single=[SingalClass alloc];
});
return single;
}
120.冒泡排序OC
//冒泡排序
-(void)bubbleSort:(NSMutableArray*)array int:(int)n
{
for (int i = 0; i<n-1; ++i) {
for (int j=0; j<n-i-1; ++j) {
if ([array[j] intValue] > [array[j+1] intValue]) {
NSNumber* temp = array[j];
[array replaceObjectAtIndex:j withObject:array[j+1]];
[array replaceObjectAtIndex:j+1 withObject:temp];
}
}
}
NSLog(@"%@",array);
}
//改良版的冒泡排序
-(void)bubbleSort1:(NSMutableArray*)array int:(int)n
{
int i= n -1; //初始时,最后位置保持不变
while ( i> 0) {
int pos= 0; //每趟开始时,无记录交换
for (int j= 0; j< i; j++)
{
if ([array[j] intValue]> [array[j+1] intValue]) {
pos= j; //记录交换的位置
NSNumber* tmp = array[j];
[array replaceObjectAtIndex:j withObject:array[j+1]];
[array replaceObjectAtIndex:j+1 withObject:tmp];
}
}
i= pos; //为下一趟排序作准备
}
NSLog(@"%@",array);
}
//改良版的冒泡排序第二版
-(void)bubbleSort2:(NSMutableArray*)array int:(int)n
{
int low = 0;
int high= n -1; //设置变量的初始值
int j;
NSNumber* tmp;
while (low < high) {
for (j= low; j< high; ++j) //正向冒泡,找到最大者
{
if ([array[j] intValue]> [array[j+1] intValue])
{
tmp = array[j];
[array replaceObjectAtIndex:j withObject:array[j+1]];
[array replaceObjectAtIndex:j+1 withObject:tmp];
}
}
--high; //修改high值, 前移一位
for ( j=high; j>low; --j) //反向冒泡,找到最小者
{
if ([array[j] intValue]<[array[j-1] intValue])
{
tmp = array[j];
[array replaceObjectAtIndex:j withObject:array[j-1]];
[array replaceObjectAtIndex:j-1 withObject:tmp];
}
}
++low; //修改low值,后移一位
}
NSLog(@"%@",array);
}