weak引用变量是否线程安全

1、在ARC出现之前,Objetive-C的内存管理需要手工执行release&retain操作,这些极大增加了代码的编写难度,同时带来很多的crash。

   同时大量的delegate是unretain的,如果忘记在dealloc中主动设置为空,将带来野指针的隐患。由于dealloc是一个线程不安全的方法

   在MRC的环境下面,如果一个对象在一个线程中正在释放过程当中,这个对象在另外一个线程收到通知,极有可能带来Crash,

  这个取决于执行的方法中访问的对象内存是否被破坏掉。

2、ARC出现之后带来了weak修饰符,降低了野指针出现的概率,同时将dealloc方法进行了修改,用户不需要主动调用[super dealloc],由编译器插入。

   同时编译器插入了一个cxx_dealloc的方法,这个方法真正释放该对象持有的变量,dealloc方法只能算作即将释放的一个回调,那么ARC下面dealloc是怎么执行的呢。

3、dealloc的方法执行

  1、一个对象执行release方法,引用计数减少1,变成0

  2、调用该对象的dealloc方法

  3、该对象dealloc方法执行完毕调用父类的dealloc方法

  4、调用NSObject的dealloc方法

  5、NSObject的dealloc方法执行_objc_rootDealloc(self);

  6、_objc_rootDealloc 中调用 object_dispose()

  7、object_dispose 中调用 objc_destructInstance()

  8、objc_destructInstance 调用 objc_clear_deallocating

  

 1 void *objc_destructInstance(id obj)
 2 {
 3     if (obj) {
 4         Class isa_gen = _object_getClass(obj);
 5         class_t *isa = newcls(isa_gen);
 6
 7         // Read all of the flags at once for performance.
 8         bool cxx = hasCxxStructors(isa);
 9         bool assoc = !UseGC && _class_instancesHaveAssociatedObjects(isa_gen);
10
11         // This order is important.
12         if (cxx) object_cxxDestruct(obj);
13         if (assoc) _object_remove_assocations(obj);
14
15         if (!UseGC) objc_clear_deallocating(obj);
16     }
17
18     return obj;
19 }
20 void objc_clear_deallocating(id obj)
21 {
22     assert(obj);
23     assert(!UseGC);
24
25     SideTable *table = SideTable::tableForPointer(obj); /* *** THIS LINE *** */
26
27     // clear any weak table items
28     // clear extra retain count and deallocating bit
29     // (fixme warn or abort if extra retain count == 0 ?)
30     OSSpinLockLock(&table->slock);
31     if (seen_weak_refs) {
32     arr_clear_deallocating(&table->weak_table, obj); /* *** THIS LINE *** */
33     }
34     table->refcnts.erase(DISGUISE(obj)); /* *** THIS LINE *** */
35     OSSpinLockUnlock(&table->slock);
36 }

  在上面的objc_destructInstance代码中,首先释放object_cxxDestruct方法,这里面会逐级往上移出对象的实例变量

  然后移除关联对象

  第三步中清空了weak指针

  可以看出来,清除一个对象的实例变量是统一清理的,由下逐级往上。

  在清理weak指针的时候如何保证这个对象在dealloc收到消息的时候还是线程安全的呢?

  答案下面:

    得到一个weak 指针的时候会执行下面的方法,该方法中有一个spinlock,这个锁在清理weak指针的时候同时会用到,所以是线程安全的。

    也就是这个weak指针获得的结果要么为空,要么不为空,只要不为空,就代表这个指针指向的内存区域没有被释放掉,其对象内部的实例变量还是有可能被清理掉的。

    这个时候向这个指针发送消息,不会带来crash,但是逻辑可能异常,这种情况理论上存在。

id objc_loadWeakRetained(id *location)
{
    id result;

    SideTable *table;
    spinlock_t *lock;

 retry:
    result = *location;
    if (!result) return nil;

    table = SideTable::tableForPointer(result);
    lock = &table->slock;

    spinlock_lock(lock);
    if (*location != result) {
        spinlock_unlock(lock);
        goto retry;
    }

    result = weak_read_no_lock(&table->weak_table, location);

    spinlock_unlock(lock);
    return result;
}

  

4、参考资料

  http://stackoverflow.com/questions/30673101/is-it-safe-to-read-a-weak-pointer-while-its-being-deallocated

  http://stackoverflow.com/questions/14854635/how-can-the-objective-c-runtime-know-whether-a-weakly-referenced-object-is-still/14854977#14854977

  http://blog.sunnyxx.com/2014/04/02/objc_dig_arc_dealloc/

  

时间: 2024-10-10 06:44:52

weak引用变量是否线程安全的相关文章

浅谈变量,数据变量与引用变量

Java是一种面向对象语言 Java程序中一切皆对象,也就是说运行中的Java程序其实是很多对象依照设计要求不断 创建 修改 联系其他对象 修改其他对象 完成使命后被垃圾收集器回收. 那么程序设计就要求定义对象的功能,设置对象之间的联系(接口),设计对象之间相互操作的顺序,根据操作结果的不同再进行更多不同的操作. 对象的定义指对对象的准确描述(对象有什么属性特点,具备什么能力-方法),定义对象的文件我们称之为类,所以类可以有属性和方法: (其实对象是非常非常非常抽象的,它的抽象体现在 1.范围广

实习第二天-对象-对象引用-引用变量-精-精-精-下雨天

class Person{ } Person是一个数据类型-引用类型 数据类型-变量名   Person a;  声明一个引用类型的变量a,然后在栈中给引用变量a分配了内存空间 初学Java时,在很长一段时间里,总觉得基本概念很模糊.后来才知道,在许多Java书中,把对象和对象的引用混为一谈.可是,如果我分不清对象与对象引用, 那实在没法很好地理解下面的面向对象技术.把自己的一点认识写下来,或许能让初学Java的朋友们少走一点弯路. 为便于说明,我们先定义一个简单的类: class Vehicl

PHP字符串中引用变量问题

php中字符串引用变量主要是数据库操作字符串的不同 1,普通字符串引用变量 a.外层必须引用双引号 b.字符串中的变量可写如:$s 或者{$s} 2.数据库操作字符串(数据库操作指令) a.字符串外层必须使用双引号 b.变量必须用大括号{}(赋值字符串属性时除外) 如: `    c.当需变量与数据库属性相赋值或者作逻辑运算时,需要对应数据属性的类型:如     上述代码中数据库中的属性 title,authordid,content均为字符串,所以变量要用单引号'{$title}'或者'$ti

引用变量

引用变量是一种特殊类型的变量,将函数形参声明为此种类型的变量,形参将成为原变量的一个引用(而不是拷贝).一个引用变量的实质是另一个变量的一个别名,任何对引用变量的改变实际上都会作用到原变量上. 声明一个引用变量应在变量名前放置一个“&”.如:int &refVar;  int & refVar;  int& refVar; #include<iostream>    using namespace std; int main(){ int count = 1;

那些年我们一起追过的的&quot;引用变量&quot;--总结1

hello ,好久没来了. 今天我来总结一下有关引用变量的注意事项,一是加深一下自己的理解,二是对这块不太理解的同学可以看看. 大神可飘过,有什么不对或不足的地方请多多指教,谢谢. 假设场景: 有一个统计游戏玩家信息调查问卷系统,玩家填写了调查问卷,会给玩家一些奖励,当然目前这不是我们关注的部分. 我们需要记录一下玩家的姓名,年龄,邮箱,以及玩家曾经玩过的游戏有哪些. 既然要记录玩家玩过的游戏,必然要有Game类: package indi.bruce.summary; public class

Java基础-被final修饰的引用变量的指向

final修饰的引用变量一旦初始化赋值之后就不能再指向其他的对象,那么该引用变量指向的对象的内容可变吗?看下面这个例子: public class Test { public static void main(String[] args) { final MyClass myClass = new MyClass(); System.out.println(++myClass.i); } } class MyClass { public int i = 0; } 这段代码可以顺利编译通过并且有输

JAVA 变量的定义和使用【引用变量的特殊性】

Java中主要有2类主变量: 基本数据类型:byte.short.int.long 和 float.double:或者叫主数据类型 和 引用数据类型:数组.类.接口:引用变量只会保存引用,而不是保存对象本身: 引用传递就是一段内存的使用权,一块内存可以供多人使用: 因为,Java中[数组是对象],不管里面存放的是主数据类型还是对象引用,但是可以声明出可以装在主数据类型数值的数组, 数组对象可以有主数据类型的元素,但是数组本身绝对不是主数据类型??????? 所以,Java中数组的使用,必须经过“

C++学习笔记29,引用变量(1)

引用变量在创建的时候就必须初始化.无法创建一个未被初始化的引用. #include <iostream> using namespace std; int main() { int x=10; int y=20; int &r1; } 编译结果: 如果引用未被初始化,编译将报错. 修改引用: 引用总是指向初始化的那个变量,也就是说,引用一旦被创建并初始化之后就无法改变.这一规则有点让人迷惑.. 如果声明了一个引用的同时使用一个变量赋值了,那么这个引用就会一直指向这个变量. 在此后使用变

c#问答篇:对象与引用变量-----初学者的困惑

转自:http://www.cnblogs.com/huangyu/archive/2004/08/02/29622.html 从宏观的角度来看,对象是类的实例.比如: //定义一个名为Someone的类,代表这么一些人(通过指定年龄,性别,性格等基本信息)class Someone {  public int age;  public string sex;  public string name;  //other...}//......//创建一个Someone类的对象,代表昵称为nemo