OC基础知识总结一
1、详述OC的内存管理机制。
OC使用引用计数(retainCount)的机制来管理对象。自己生成的对象,自己持有。非自己生成的对象自己也能处理。不在需要自己持有的对象时,释放掉。非自己持有的对象无法释放。
a、在MRC中,retain与release配对使用,retain引用计数+1,release引用计数-1。
b、与alloc配对使用的方法是dealloc,alloc是开辟内存空间,dealloc是销毁开辟的内存空间。
c、readwrite、readonly读写控制。
readwrite即声明getter方法又声明setter方法。
readonly告诉编译器之声明getter方法。
默认属性为readwrite。
d、nonatomic,atomic为原子性控制
nonatomic非原子性控制,此时setter、getter方法不会做多线程处理,提高系统性能。
atomtic为原子性,此时setter、getter方法会做多线程处理,要不断的对setter、getter方法加锁解锁来保证线程安全,但是也降低了系统的性能。
2、delegate为什么用assign或者weak?详述!
a、delegate之所以使用weak来修饰,是为了防止循环引用,weak属性的变量是不为其所属对象所持有,并且在该对象被销毁后,此weak变量的值会自动被赋值为nil。而assign属性一般是对C基本数据类型成员变量的声明,当然也可以用在对象成员变量上,只是其代表的意义只是单纯的拷贝所赋值变量的值。
场景:对某成员变量B赋值某对象A的指针,则B只是简单地保存此指针的值,并不持有对象A,那么如果A被销毁,B就会指向一个已经被销毁的对象,如果再对其发送消息会引起崩溃。
b、扩展空指针和野指针:
空指针:没有存储任何内存地址的指针就称为空指针。如:NSString *str= nil/NULL;
野指针:指针指向一个已经被销毁的内存。
总结:使用野指针是非常危险的,容易引发崩溃。而使用空指针发消息是没有任何问题的。
c、使用weak的情况
assign是指针赋值,不对引用计数操作,使用之后如果没有置为nil,可能就会产生野指针;而weak一旦不进行使用后,永远不会使用了,就不会产生野指针!
3、Block的使用
a:为什么使用weakSelf?
因为Block是一个结构体,它会将一个全局变量保存为一个属性(__strong),而self强引用了Block这会造成循环引用,所以使用__weak修饰weakSelf。
b:为什么在Block里面使用strongSelf?
为了保证block在执行完毕之前self不会被释放,而strongSelf是为了保证Block内部执行的时候不会被释放,但是存在执行前就已经被释放的情况,导致strongSelf=nil。注意判空处理,防止出现崩溃。typeof是编译时确定变量类型,所以这里写self 不会被循环引用。
总结:
外部使用了weakSelf,里面使用strongSelf却不会造成循环,究其原因就是因为weakSelf是block截获的属性,而strongSelf是一个局部变量会在“函数”执行完释放。
另外:
a:__block可以让block修改局部变量,而__weak不能。
b:MRC中__block是不会引起retain;但在ARC中__block则会引起retain,因为,block也是一个强引用,引起循环引用,会引起循环引用。所以ARC中应该使用__weak。
4、深拷贝和浅拷贝的区别?
深拷贝:是重新复制了一份数据包括内存和指针,相当于克隆
浅拷贝:只是赋值指针,指向的还是原来的内存地址。当原来的内存数据有变化,浅拷贝的数据也会改变。
扩展1:NSString使用copy和strong的区别?
1、在NSString的时候源数据为不可变时copy是浅拷贝,新的指针指向相同的内存地址,这一点和strong相同,引用计数都增加。
2、在源数据可变的时候copy是深拷贝,生成了一个新的对象。而strong还是指向原有对象。
结果:在源数据改变的时候,copy修饰的属性可以保证数据的不变。strong在源数据为可变类型时会随着源数据的变化而变化。
链接:http://www.cocoachina.com/ios/20150512/11805.html
5、load和initialize有什么用处?
在Objective-C中,runtime会自动调用每个类的两个方法。+(void)load会在类初始加载时调用,+(void)initialize会在第一次调用类的类方法或实例方法之前被调用。这两个方法是可选的,且只有在实现了它们时才会被调用。
共同点:两个方法都只会被调用一次。
6、atomic是绝对的线程安全么?
a:atomic并不是绝对的线程安全,当使用nonatomic时,属性的setter、getter操作是非原子性的,所以当多个线程同时对某一属性读和写操作时,属性的最终结果是不能预测的。
b:当使用atomic时,虽然属性的读写是原子性的,但是仍然可能出现线程错误:当线程A进行写操作时,此时,其他线程的读写操作会因为该操作而等待。当A线程进行写操作结束后,B线程进行写操作,当A线程需要读操作时获取的是B写的值,这样就破坏了线程的安全。如果有C再A线程操作之前release了该属性,那么会导致程序崩溃。
所以,仅仅使用atomic并不会使得线程安全,我们还要添加lock确保线程的安全。
atomic所说的线程安全只是保证getter和setter存取方法的线程安全,并不能保证整个对象的安全。
7、如何处理UI的耗时操作?
a:将耗时操作的IO放到子线程中去处理,然后到主线程中更新UI。
b:采用预加载的方式,提前处理耗时操作。优点是UI更加流程,缺点耗内存。
c:采用懒加载方式,耗时操作不立即使用,采用延时加载,优点提高界面的流畅度,在需要加载时才显示,缺点需要稍稍等待。
8、优化tableView方法?
1、行高的缓存有rowheigh就不要用heightForRowAtIndexPath用方法,防止方法的多次调用消耗。每次数据源变化时例如插入删除,尽量刷新特定行,不要全局刷新。
2、加载网络数据或者从内存中取得数据要开启分线程
3、layer添加圆角比较耗时,这样会离屏渲染,需要牺牲更多的性能。比如图片显示有圆角时,可以通过coreGraphic来生成带圆角的图片。
4、重用cell,防止重复绘制,减少渲染次数,提高性能。
5、减少subView的数量,尽量在同一个view上显示。
6、尽量少动态给cell添加view,可以初始化的时候就添加,通过hide控制是否显示。
9、线程知识点?
1、线程同步的方式包括:互斥锁、读写锁、条件变量、信号量和令牌。
* 并行和并发的区别?
并行:多个任务同时执行
并发:多个任务每隔一段时间执行
2、NSOperation与GCD的关系
NSOperation是基于GCD进行封装的,面向对象的。GCD是基于C语言的。
3、 默认最大并发
NSOperation系统默认的并发数是-1,所有任务全部并发执行
GCD没有默认并发数,我们可以通过信号量来设置并发量。dispatch_semaphore。
4、线程取消
[NSOperation cancel]。一旦取消无法再恢复,正在执行的任务无法取消。
5、 [thread cancel]可以关闭线程?
[thread cancel]取消线程,不能再开始。[NSThread exit];才是关闭线程。
10、NSBlockOperation和NSInvocationOperation有什么关系和区别?
相同点:
1、NSBlockOperation和NSInvocationOperation都是NSOperation的子类。通过子类执行任务,不添加到任务中在主线程中执行任务。
不同点:
1、NSBlockOperation可以在Block中执行任务。通过addExecutionBlock:方法添加更多的操作遵循FIFO。
注只要NSBlockOperation封装的操作数>0, 就会异步执行操作。线程为串行异步执行。
2、NSInvocationOperation是使用方法执行操作。
扩展1: NSOperation可以像GCD一样设置串行并行么?
答:不可以直接设置情形执行,但是可以这是依赖来实现串行执行,NSOperation本身就是并行执行。
扩展2: NSOperation的添加进队列后可不可以追加依赖?GCD任务组添加监听后可不可以追加任务?
答:都不可以。
11、performSelector和直接调用方法的不同?
1、performSelector是运行时系统负责去找函数/方法的,在编译时候不做任何校验;但是使用performSelector的话一定是在运行时候才能发现,如果方法不存在,此时程序崩溃。
2、直接调用方法在编译是会校验,如果test2方法不存在,那么直接调用在编译时候就能够发现。
扩展:可以使用runtime来处理没有实现的方法。
12、ViewController生命周期
整个控制器声明周期: viewDidLoad -> viewWillAppear -> viewWillLayoutSubviews -> viewDidLayoutSubviews -> viewDidAppear -> viewWillDisappear -> viewDidDisappear
initWithCoder:(NSCoder *)aDecoder:(如果使用storyboard或者xib)
loadView:加载view
viewDidLoad:view加载完毕
viewWillAppear:控制器的view将要显示
viewWillLayoutSubviews:控制器的view将要布局子控件
viewDidLayoutSubviews:控制器的view布局子控件完成
这期间系统可能会多次调用viewWillLayoutSubviews 、 viewDidLayoutSubviews 俩个方法
viewDidAppear:控制器的view完全显示
viewWillDisappear:控制器的view即将消失的时候
这期间系统也会调用viewWillLayoutSubviews 、viewDidLayoutSubviews 两个方法
viewDidDisappear:控制器的view完全消失的时候
13、声明属性的set/get写法
总结:我们在针对assign属性的时候不需要设置if判断。针对retain、copy都要进行if判断设置。
1、set方法和get方法是用来对数据进行设置和取值的。
2、在set方法中我们可以对传入的数据进行判断,以防止输入一些不合理的值。
3、我们可以利用@property来实现set和get方法的声明,利用@synthesize对set和get方法进行实现。这样可以简化我们要输入的代码。
4、定义属性的时候最好在前面加上下划线。
- @synthesize的作用以及使用情况
aaa@qq.com 的作用:是为属性添加一个实例变量名,或者说别名。同时会为该属性生成 setter/getter 方法。
2.禁止@synthesize:如果某属性已经在某处实现了自己的 setter/getter ,可以使用 @dynamic 来阻止 @synthesize 自动生成新的 setter/getter 覆盖。
3.内存管理:@synthesize 和 ARC 无关。
4.使用:一般情况下无需对属性添加 @synthesize ,但一些特殊情形仍然需要,例如protocol中声明的属性。
在.h文件中
@property(nonatomic,assign)NSInteger age;
@property(nonatomic,retain)NSArray*array;
@property(nonatomic,copy)NSString *name;
在.m文件中
先声明合成参数
@synthesize age =_age;
@synthesize array =_array;
@synthesize name =_name;
//assain修饰的属性 生成的set get方法
-(void)setAge:(NSInteger)age
{
_age =age;
}
-(NSInteger )age
{
return _age;
}
//retain 修饰的属性
-(void)setArray:(NSArray *)array
{
if (_array !=array)
{
[_array release];
_array =[array retain];
}
}
-(NSArray*)array{
return [[_array retain]autorelease];
}
//copy修饰的属性
-(void)setName:(NSString *)name
{
if (_name !=name) {
[_name release];
_name = [name copy];
}
}
-(NSString *)name
{
return [[_name retain]autorelease];
}
14、@dynamic与@synthesize的最基本的理解
1、@property有两个对应的词,一个是@synthesize,一个是@dynamic。如果@synthesize和@dynamic都没写,那么默认的就是@syntheszie var = _var;
2、@synthesize的语义是如果你没有手动实现setter方法和getter方法,那么编译器会自动为你加上这两个方法。
3、@dynamic告诉编译器,属性的setter与getter方法由用户自己实现,不自动生成。(当然对于readonly的属性只需提供getter即可)。假如一个属性被声明为@dynamic var,然后你没有提供@setter方法和@getter方法,编译的时候没问题,但是当程序运行到instance.var =someVar,由于缺setter方法会导致程序崩溃;或者当运行到 someVar = var时,由于缺getter方法同样会导致崩溃。编译时没问题,运行时才执行相应的方法,这就是所谓的动态绑定。
传送门
15、OC的struct
1、结构体只能封装属性,而类不仅可以封装属性还可以封装方法。
2、结构体变量分配在栈.OC对象分配在堆。
* 什么时候使用结构体?
a.封装数据只有属性,且属性较少。
* 什么时候使用类?
a.封装数据即有属性也有行为。
b.只有属性,但是属性比较多。
3、结构体赋值是 直接赋值的值. 而对象的指针 赋值的是对象的地址。
- (void)structMethod{
struct MyDate1 {
int year;
int month;
int day;
};
// 定义结构体类型变量
// 结构体类型变量为 d1,并赋值
struct MyDate1 d1 = {2016, 1, 6};
// 结构体类型变量值的调用
NSLog(@"d1: %d/%d/%d", d1.year, d1.month, d1.day);
}
推荐阅读
-
OC基础知识总结一
-
一些flash memory/SSD的基础知识 博客分类: Storage Flash读书CacheBlog
-
typedef的一些使用总结
-
面试必备基础知识 — Java基础(一)
-
Redis基础知识总结(面试必备)
-
【java】JETM--Java™的执行时间测量库简单学习(一) 博客分类: java知识总结 javajetm
-
跟着官网学Struts2使用(一) 博客分类: javaWeb知识总结 mavenstruts2java
-
【java】JTable中某一列设置为下拉列表 博客分类: java知识总结 javaJTable
-
展望未来,总结过去10年的程序员生涯,给程序员小弟弟小妹妹们的一些总结性忠告 生活工作医疗面试招聘
-
【若泽大数据实战第十六天】Hive的安装部署 + 课程一个月总结