iOS中几个小知识点整理
一、NSString用copy还是strong
首先看下copy修饰可变变量与不可变变量的区别:
copy修饰可变变量:在赋值指针的同时,也会复制指针指向的内存区域,是深拷贝。
copy修饰不可变变量:等同于strong,增加引用计数,是浅拷贝。
同理,属性NSString,
- 当源字符串是NSString时,不管是strong还是copy,属性的对象都是指向源对象,copy操作是浅拷贝。
- 当源字符串是NSMutableString时,strong修饰 增加了源字符串的引用计数,而copy修饰string字符串则是对源字符串进行了深拷贝,产生了新对象,且copy属性对象指向这个新的对象,这个copy属性的类型始终为NSString,是不可变的,因为声明的属性类型是NSString。
当我们声明NSString属性时,不希望它中途改变,所以大多数情况下我们用copy修饰,避免因为源字符串是可变字符串时对其进行修改。所以当用copy时,源字符串是可变字符串时,源字符串改变,copy属性不随之改变(它是一个新的对象,新的内存地址)。
二、assign和weak的区别
assign:只进行简单的赋值,没有其他操作。当assign指向的内存被释放时,不会自动赋值为nil,会发生野指针问题
weak:arc模式下属性修饰符,当属性所指向的内存被释放时,自动置为nil,避免野指针的发生。
arc模式下,指针变量一定要用weak修饰,基本数据类型及结构体类型用assign修饰。
三、单例相关
优点:只会创建一个对象,节约内存。并且很容易被外界访问。
缺点:
- 一个类只有一个对象,可能会造成责任过多,违背了面向对象的单一职责原则。
- 单例没有抽象层,所以扩展困难
- 不能过多创建单例,因为单例从创建到程序关闭会一直存在,过多的单例会影响性能,浪费资源。
代码示例:
+(instancetype)sharedInstance
{
static dispatch_once_t once;
static id sharedInstance;
dispatch_once(&once,^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
代码解析:
sharedInstance声明为静态变量,静态变量所在的方法返回时,静态变量不会被释放。sharedInstance变量的初始值是nil,当程序第一次执行sharedInstance方法时会创建一个对象,并将新创建的对象的地址赋值给sharedInstance变量。再次执行sharedInstance方法时,无论多少次sharedInstance变量仍然会指向最初那个创建的对象。因为指向对象的sharedInstance变量是强引用的,并且程序永远不会释放该变量,所以sharedInstance变量指向的对象也不会释放。
线程安全:dispatch_once保证程序在运行过程中只会被运行一次,如果线程1执行了sharedInstance方法,创建了一个实例变量,线程2就不会再去执行dispatch_once里面的代码了,从而保证程序只会创建一个实例对象。
四、ARC和MRC
MRC环境下编程时属性用retain和assign修饰,ARC时引入了strong和weak。
ARC下,不显式指定任何属性关键字时,默认的关键字是atomic,readwrite,strong(对象),assgin(基本数据类型)。
ARC/MRC分别都是如何进行内存管理的?
程序在运行过程中会创建大量的对象,在ObjC中对象存储在堆中,系统并不会自动释放堆中的内存,基本类型是由系统自己管理的,放在栈上。如果一个对象创建并使用后没有得到及时释放那么就会占用大量内存,内存的积累使得系统卡顿,甚至被app杀死(发生crash)。
1.MRC环境下:谁创建,谁释放,谁持有,谁释放,减少了各个模块的逻辑耦合。与变量相关的内存管理有retain,release和autorelease。retain和release方法操作的是引用记数,当引用记数为零时,便自动释放内存。并且可以用NSAutoreleasePool对象,对加入自动释放池(autorelease调用)的变量进行管理,当drain时回收内存。
与属性相关的修饰符有标识符@property (nonatomic/atomic,retain/assign/copy, readonly/readwrite) Number* num;
- nonatomic/atomic,表示该属性是否是对多线程安全的,是不是使用线程锁,默认为atomic,
- retain/assign/copy,是有关对该属性的内存管理的。retain和copy都需要手动释放,assign不会引起引用计数的变化,因为它是直接赋值,两个变量将指向同一个内存地址,另一个变量在使用的时候,该变量不能进行释放。如果其中一个变量释放了,另一个变量将引起野指针问题。如果是基本数据类型,使用assign不会有问题。
2.ARC环境下:变量修饰符常用的是__strong和__weak。block中解决循环引用问题时常用这对修饰符。
属性修饰符有property (nonatomic/atomic, assign/retain/strong/weak/unsafe_unretained/copy,readonly/readwrite)
强引用strong增加对象的引用计数,如果是弱引用,则将内存对象在系统中建立映射表,一旦内存对象的引用计数为0,则将该内存对象的所有弱引用对象指针置为nil。
五、KVO和KVC
KVO的使用,就三步:
1.注册需要观察的对象的属性addObserver:forKeyPath:options:context:
2.实现observeValueForKeyPath:ofObject:change:context:方法,这个方法当观察的属性变化时会自动调用
3.取消注册观察removeObserver:forKeyPath:context:
KVC:键值编码,是一种间接访问实例变量的机制(不通过存取方法,访问对象的实例变量),有两个方法:setValue:forKey,valueForKey
KVO:键值观察,使用观察者模式,可以使对象获取其他对象属性变化的通知。
KVO是利用isa-swizzling(类型混合指针机制)实现的,得益于Runtime的动态能力,isa指针在运行时被修改,指向一个中间类,而不是原来实例变量真正的类。这个中间类是被监听对象的派生类,重写了setter方法,在setter方法中加入了通知机制,这样当使用KVC修改属性值时,就会发送通知,从而实现了KVO。
键值观察通知依赖于NSObject的两个方法:willChangeValueForKey:和 didChangevlueForKey: 这两个方法分别在属性被修改之前和之后调用,属性被修改的代码是setValue: forKey: 所以只有在使用KVC命令约定时KVO才可以使用 isa 混写来做到修改属性时触发通知机制。