针对目前开发中已大多使用ARC自动引用计数技术,因此常用关键字有strong、weak、assign、copy、retain、nonatomic、atomic
strong:释放旧对象将旧对象的值赋予输入对象,再将输入对象的索引值计数增加1
weak:不增加引用计数,不持有对象,所以不能决定对象的释放,对比assign好处是,当对象消失时指针自动归为nil
assign:适用于基础数据类型,不增加引用计数,如(NSInteger、CGFloat、int等)
copy:建立一个索引计数为1的对象,然后释放旧对象,对实行了NSCopying协议的对象类型有效(NSString、NSDictionary、NSArray、block)
nonnull与nullable:是iOS9之后的新特性,用于声明属性是否可以为nil,若对nonnull属性赋值nil时,则会报警告。用于调用属性时可以知道该属性是否可能为nil,并针对处理。默认情况下为nullable状态,可以赋值为nil
atomic:与nonatomic相对应,用于决定编译器生成的getter和setter是否为原子操作,atomic设置成员变量@property属性时,默认为atomic提供线程安全
nonatomic:非原子性访问对于属性赋值时不加锁,多线程并发访问会提高性能,若不加此属性则默认setter和getter方法都为原子性访问
readonly:此属性为只读
readwrite:可读写,默认属性
何为原子性访问?
当多线程环境下同时调用一个setter时,可能会出现无法获取完整的数据。使用atomic属性时,则会一个线程在执行完setter全部语句前,不会让另一个线程开始执行setter,以此保证数据完整性。因此,在多线程环境下执行原子性访问是很有必要的,但同时原子性操作会耗费系统资源。
其它扩展问题:
主要是为了防止循环引用问题。
弱引用
在VC中,VC的view就是tableview,相当于VC强引用着tableview。当设置delegate时,是为了让tableview成为代理,若此时代理设置为强引用,则tableview的delegate强引用VC,导致了循环引用。
在声明为copy后,block才会在堆中,栈中的block生命周期是和栈绑定的。也可以用retain,block的retain行为默认为copy行为实现的,block变量默认是声明为栈变量的,为了能在block的声明域外使用,所以要把block copy到堆中,为了属性声明和实际操作一致,最好声明为copy。
线程安全问题,声明block属性时,需要确认是否有多个线程同时访问修改block。若没有,则声明为nonatomic,若不确定时,使用atomic。为了安全起见,在调用时需要把block先赋值给本地变量,以防止block改变。若不这么操作,即使先判断了block不为空,调用前,一旦另一个线程把block置为空,程序会crash。
代码示例:
if (self.myBlock)
{
//此时,走到这里,self.myBlock可能被另一个线程改为空,造成crash
//注意:atomic只会确保myBlock的原子性,这种操作本身还是非线程安全的
self.myBlock(123);
}
所以正确的代码是(ARC):
MyBlockType block = self.myBlock;
//block现在是本地不可变的
if (block)
{
block(123);
}
在MRC环境下,需要手动retain一下,以防变量变为野指针。
Objective-C对象所占内存总是分配在“堆空间”,且堆内存由开发者释放,即release;
由编译器管理自动释放的,在方法中定义的变量通常在栈内。
栈区(stack):由编译器自动分配释放,存放函数的参数值,局部变量等值。其操作方式类似于数据结构中的栈。
栈对象: 优点: 1.高速,在栈上分配内存是非常快的。 2.简单,栈对象有自己的生命周期,你永远不可能发生内存泄露。因为他总是在超出他的作用域时被自动销毁了 在objective-c中只支持一个类型对象:block 堆区(heap):一般由程序员分配释放,若程序员不释放,则可能会引起内存泄漏。注 堆和数据结构中的堆栈不一样,其类似于链表。 缺点: 栈对象严格的定义了生命周期也是其主要的缺点,栈对象的生命周期不适于Objective-C的引用计数内存管理方法。
堆对象: 优点:可以自己控制对象的生命周期。 缺点:需要程序员手动释放,容易造成内存泄漏。
一般情况下,不希望字符串的值跟着变化时,使用copy;希望属性变量跟着变化,就使用strong。
以上情况是针对NSMutableString赋值为NSString时,才会有所不同。若都为NSString,则使用copy和strong都一样,NSString本身不能改变自身的值,是不可变的。
因此,对于源头是可变变量时,不可变变量仅仅是指针引用,当源头改变时,若使用strong声明,不可变变量会跟随变化;而copy声明,是深拷贝,不会跟随改变。
不需要。在释放时,调用clearDeallocating函数。该函数首先根据对象地址获取所有weak指针地址的数据,然后遍历数据把其中的数据置为nil,最后把记录从weak表中删除,清理对象的记录。
原理:weak对象会放入一个hash表中,用weak指向的对象内存地址作为key,因此该对象引用计数为0时就回dealloc,在hash表中找到所有以该对象内存地址为key的weak对象,从而置为nil。
(1)、调⽤用objc_release
(2)、因为对象的引⽤用计数为0,所以执行dealloc
(3)、在dealloc中,调⽤用了了_objc_rootDealloc函数
(4)、在_objc_rootDealloc中,调⽤了object_dispose函数
(5)、调用objc_destructInstance
(6)、最后调用objc_clear_deallocating,详细过程如下:
a. 从weak表中获取废弃对象的地址为键值的记录
b. 将包含在记录中的所有附有 weak修饰符变量量的地址,赋值为 nil
c. 将weak表中该记录删除
d. 从引⽤用计数表中删除废弃对象的地址为键值的记录
基本数据类型: atomic、assign、readwrite
基本OC对象: atomic、strong、readwrite
@property有两个对应词,一个是@synthesize,一个是@dynamic。若都没声明,则默认是@synthesize var = _var;
@synthesize若无手动实现setter方法和getter方法,编译器会自动加上两个方法
@dynamic告诉编译器,setter和getter方法由用户实现,不自动生成。对于只读属性的只需提供getter即可。当一个属性被声明为@dynamic var并没有提供getter和setter方法,当执行到需要setter和getter方法时,导致崩溃。编译通过,执行时才执行相应方法,即所谓的动态绑定。
a. 若指定了成员变量的名称,则会生成一个指定名称的成员变量 b. 若成员已经存在,则不再生成
在两者中,都会生成setter和getter方法的声明。protocol中是希望遵守协议中的对象实现该属性;category需要增加属性的实现时,需要分别使用两个函数:objc_setAssociatedObject和objc_getAssociatedObject
重写只读属性的getter时;
重写setter和getter时
使用了@dynamic时
@protocol中定义了所有属性时
在category定义了所有属性时
重载了属性时
不能向编译后得到的类增加实例变量
可以向运行时创建的类添加实例变量
原因:
编译后的类已经注册在runtime中,类结构体中objc_ivar_list实例变量的链表和instance_size实例变量的内存大小已确定,runtime会调用class_setvarlayout或class_setWeaklvarLayout来处理strong、weak引用。所以不能向存在的类中增加实例变量。
运行时创建的类可以添加实例变量,是调用class_addIvar函数,但是在调用objc_allocateClassPair之后,objc_registerClassPair之前