1、读写权限
readwrite(默认) :同时产生setter/getter方法
readonly:只有getter方法,没有sette方法
2、原子性
atomic(默认)、nonatomic
原子性 是保证赋值获取的线程安全,添加/移除不保证其线程安全。
3、引用计数
retain /strong:(ARC默认strong)
weak:ARC下使用,仅修饰对象类型,不增加修饰对象的引用计数,当所修饰对象被释放后,会自动置为nil,不会产生悬垂指针。
assgin:既可以修饰基本数据类型,也可以修饰对象类型。setter方法的实现是直接赋。修饰对象类型时,不改变对象的引用计数,会产生悬垂指针(当修饰的对象被释放后,assgin仍指向原对象指针,导致悬垂指针,这时候继续通过该指针访问元对象的话,就可能导致程序崩溃)
unsafe-unretained(mrc):与weak类似,但是在销毁时不能自动销毁,容易形成野指针。
copy:与strong类似,设置方法会拷贝一份副本。一般用于修饰字符串和集合类的不可变量,block也用copy修饰。
4、其他关键字
__weak:ARC情况下使用,弱引用对象,一般是在block内部使用__weak 重定义捕获到的self(或其他),避免循环引用,
__strong:ARC情况下使用,强引用对象,一般与__weak一起使用,防止在block里面用__weak修饰的对象过早被释放;
__block:允许block改变外部传进的变量值(具体原因查看 block知识小结)
@synthesize:可对属性的成员变量进行重命名
@dynamic:告诉编译器,属性的setter方法和getter方法由用户自己实现,不自动生成,若是用户没有自己实现,那么在运行到该属性的点语法时会由于找不到setter和getter方法而导致崩溃。
引用计数管理
MRC是手动管理引用计数。
ARC是LLVM(编译器)和runtime协作实现自动引用计数。
引用计数表,是哈希表。
通过对象地址找到对象的引用计数。
第0位:weakly_refrenced,当前对象是否有弱引用指针
第1位:deallocating,当前对象是否进行dealloc操作
第2-63位:RC,引用计数值,计算值时需要向右偏移两位,计算出的才是正确的值。
部分方法源码分析:
alloc 经过一系列调用最终调用c函数的calloc方法,实际上此时的没有设置引用计数+1,
但是读取的retainCount 为1,是因为读取retainCount方法中,设置了1的局部变量。
retain
1、根据当前指针,在sideTables中哈希查找获取当前对象的sideTable
2、在sideTable中哈希查找获取当前对象的引用计数值
3、引用计数+1,实际是偏移量+1操作
objc_object::sidetable_retain(bool locked)
{
#if SUPPORT_NONPOINTER_ISA
ASSERT(!isa().nonpointer);
#endif
SideTable& table = SideTables()[this];
if (!locked) table.lock();
size_t& refcntStorage = table.refcnts[this];
if (! (refcntStorage & SIDE_TABLE_RC_PINNED)) {
refcntStorage += SIDE_TABLE_RC_ONE;
}
table.unlock();
return (id)this;
}
retainCount
1、根据当前指针,在sideTables中哈希查找获取当前对象的sideTable
2、设置局部变量(所以alloc来的数据就是此值)
3、查询获得当前对象的引用计数表
4、查找结果做向右偏移的操作,再结合局部变量做加1操作
所以alloc出来的读取的it数据是0,因为局部变量是1,经过操作之后,获取到的retainCount为1
objc_object::sidetable_retainCount() const
{
SideTable& table = SideTables()[this];
size_t refcnt_result = 1;
table.lock();
RefcountMap::iterator it = table.refcnts.find(this);
if (it != table.refcnts.end()) {
// this is valid for SIDE_TABLE_RC_PINNED too
refcnt_result += it->second >> SIDE_TABLE_RC_SHIFT;
}
table.unlock();
return refcnt_result;
}
release
1、根据当前指针,在sideTables中哈希查找获取当前对象的sideTable
2、查询获得当前对象的引用计数表
3、引用计数表中对应值 减一
4、当引用计数为0时,触发dealloc操作
objc_object::sidetable_release(bool locked, bool performDealloc)
{
#if SUPPORT_NONPOINTER_ISA
ASSERT(!isa().nonpointer);
#endif
SideTable& table = SideTables()[this];
bool do_dealloc = false;
if (!locked) table.lock();
auto it = table.refcnts.try_emplace(this, SIDE_TABLE_DEALLOCATING);
auto &refcnt = it.first->second;
if (it.second) {
do_dealloc = true;
} else if (refcnt < SIDE_TABLE_DEALLOCATING) {
// SIDE_TABLE_WEAKLY_REFERENCED may be set. Don't change it.
do_dealloc = true;
refcnt |= SIDE_TABLE_DEALLOCATING;
} else if (! (refcnt & SIDE_TABLE_RC_PINNED)) {
refcnt -= SIDE_TABLE_RC_ONE;
}
table.unlock();
if (do_dealloc && performDealloc) {
this->performDealloc();
}
return do_dealloc;
}