精华内容
下载资源
问答
  • 这篇文章并不会详细介绍block在内存中到底是以什么形式存在的,主要会介绍block是如何持有并且释放对象的。文章中的代码都出自Facebook开源的用于检测循环引用的框架FBRetainCycleDetector,这是分析该框架文章中的...
  • 目录Java编程思想(一)第1~4章:概述Java编程思想(二)第5章:初始化和清理Java...内部类Java编程思想(八)第11章:持有对象Java编程思想(九)第12章:异常Java编程思想(十)第13章:字符串Java编程思想(十一...

    目录
    Java编程思想(一)第1~4章:概述
    Java编程思想(二)第5章:初始化和清理
    Java编程思想(三)第6章:访问权限
    Java编程思想(四)第7章:复用类
    Java编程思想(五)第8章:多态
    Java编程思想(六)第9章:接口
    Java编程思想(七)第10章:内部类
    Java编程思想(八)第11章:持有对象
    Java编程思想(九)第12章:异常
    Java编程思想(十)第13章:字符串
    Java编程思想(十一)第14章:类型信息
    Java编程思想(十二)第15章:泛型
    Java编程思想(十三)第16章:数组
    Java编程思想(十四)第17章:深入研究容器
    Java编程思想(十五)第18章:Java I/O系统
    Java编程思想(十六)第19章:枚举
    Java编程思想(十七)第20章:注解
    Java编程思想(十八)第21章:并发

    第十一章、持有对象

    目录

    11.1 迭代器(Iterator)

    11.2 List

    11.3 Set

    11.4 Map

    11.5 Collection 和 Iterator

    11.6 容器的元素类型(支持泛型)

    11.7 过时容器(废弃)


    Java容器架构图:

     

    11.1 迭代器(Iterator)

    Iterator迭代器:使得客户端程序员不必知道或关心容器类的底层结构。

    ListIterator:只能用于各种List类容器的访问。ListIterator可以双向移动,而Iteraotr只能向前移动。

     

    11.2 List

    ArrayList&LinkedList:都可自动扩容。

    ArrayList:底层是数组结构,即连续存储空间,因可自动扩容,所以可以把ArrayList当作“可自动扩充自身尺寸的数组”看待。

    优点:随机读取元素快; 缺点:在列表中间插入&删除数据慢;

    LinkedList:是链表结构。

    优点:在列表中间插入&删除数据快;缺点:随机读取元素慢(链表结构特征,需要指针遍历);

    SparseList:Android提供的容器,优点:

    CopeOnArrayList: 支持并发,用于多线程机制。

     

    11.2.1 Stack(LIFO)

    LinkdedList:具有能够直接实现栈(Stack)的所有功能的方法,因此可以直接将LinkedList作为栈使用。

     

    11.2.2 Queue(FIFO)

    LinkdedList:也提供了支持队列(Queue)行为的方法,并且实现了Queue接口,所以也可以用作Queue。

    PriorityQueue:Java SE1.5新增容器,支持先弹出优先级高的元素(可实现Comparator接口)。

     

    11.3 Set

    不保存重复元素,包含下列容器:

    HashSet:使用散列函数? 优点:可快速查找元素(WHY??:)

    TreeSet:元素存储在红-黑树中

    LinkedHashSet:散列+链表 优点:快速查找+ 插入|删除 元素 场景下的效率。

    EnumSet:

    CopeOnWriteArraySet: 支持并发,用于多线程机制。

     

    11.4 Map

    K-V映射表,将对象映射到其他对象的能力是一种解决编程问题的杀手锏。

    HashMap:优点:快速访问元素

    TreeMap: 优点:保持“Key”排序(??)

    LinkedHashMap:散列(快速访问元素)+链表(插入|删除元素性能优)

    EnumMap:

    ConcurrentHashMap:支持并发,支持多线程场景;

    WeakHashMap:弱引用的持有对象,利于GC回收,避免内存泄露?

     

    11.5 Collection 和 Iterator

    Collection:在Java中,Collection是描述所有序列容器的共性的根接口,它可能会被认为是一个“附属接口”,即因为要表示其他若干个接口的共性而出现的接口。而在标准C++类库中并没有其容器的任何公共基类。

    Iterator:容器之间的所有共性都是通过迭代器达成的。Java将两种方法绑定到了一起,因为实现Collection就意味着需要提供iterator()方法。

    11.5.1 Foreach与迭代器

    foreach语法用于任何实现了Iterable接口的类。

    Collection接口扩展了Iterable接口,所以所有Collection对象都适用foreach语法。

    11.5.2 选择策略

    a. Java容器:建议针对Collection接口编程,可for-each;

    b. 自定义容器:建议实现Iterator,可实现迭代(若自行实现Collection,逻辑较复杂 )。

     

    11.6 容器的元素类型(支持泛型)

    11.6.1 支持泛型

    a. 容器&持有对象的类型之前解耦,可支持任意类型的数据。

    b. 类型编译期检测:有了泛型,容器就可以指定并检查它们所持有对象的类型;

     

    11.6.2 持有基本数据类型(自动装箱&拆箱机制)

    a. 在Java中,任何基本类型都不能作为类型参数。因此不能创建ArrayList<int> 或 HashMap<int, int>之类的东西。

    b. 自动装箱&拆箱机制,实现可以持有基本数据类型:利用自动包装机制和基本类型的包装器来解决,自动包装机制将自动地实现int 到 Integer的双向转换,

     

    11.7 过时容器(废弃)

    Vector /HashTable / Stack : (WHY?? 已有替代的容器)

    展开全文
  • Java编程思想第11章持有对象.ppt
  • java容器(持有对象)

    2014-09-26 20:08:26
    归纳了java中常用容器包括List、set、map等
  • Java编程思想(八) —— 持有对象 独在异乡为异客,每逢中秋倍思亲

    书中的原标题是——holding your object,把握你的对象,译者翻译成持有对象。这是用的最多的类之一。

     

    作者说的,如果一个程序包含固定数量的且其生命周期都是已知的对象,那么这是一个非常简单的程序。确实,如果数组大小已知,那么就很简单了。

     

    除了数组,Java提供了容器类来holding object。

     

    1)泛型和类型安全的容器

    ArrayList,可以自动扩充大小的数组,add插入对象,get访问对象,size查看对象数目。

     

    class Apple{}
    
    public class Box {
        public static void main(String[] args) {
            ArrayList<Apple> a = new ArrayList<Apple>();
            a.add(new Apple());
        }
    }


    泛型(就是跟在ArrayList后面的那个尖括号指明Apple类型的标识)的添加可以在编译期间防止将错误类型的对象放进容器中。

     

    同样,容器也可以用foreach语法。

     

    2)概念

    容器类作用是保存对象。分为两个:

    一、Collection,List顺序保存元素,Set不能有重复元素,Queue按照排队来。

    二、Map,键值对,通过键找值或者被称为字典。

     

    对了,写了这么多篇,忘记说一件重要的事情了,懂得查API文档,最好看英文版。Collection是什么,类还是接口,一查就知道了。

     

    public interface List<E>
    extends Collection<E>
    
    
    public class ArrayList<E>
    extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, Serializable
    

    这样的关系就出来了。

     

    3)添加一组元素

     

    public class AddGroup {
        public static void main(String[] args) {
            Collection<Integer> c = new ArrayList<Integer>(Arrays.asList(1,2,3,4));
            Integer[] group = {5,6,7,8 };
            c.addAll(Arrays.asList(group));
            System.out.println(c);
            Collections.addAll(c, 9,0);
            System.out.println(c);
        }
    }
    
    //[1, 2, 3, 4, 5, 6, 7, 8]
    //[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
    

     

     

     

    Collections.addAll,Adds all of the specified elements to the specified collection.

    将其他元素添加到Collection c中,而Collection.addAll是添加一组元素。

     

    4)容器的打印

    其实上面的代码已经看出,具体的容器已经实现了自己的toString方法。

     

    5)List

    List有两种:ArrayList,随机访问元素快,中间插入和删除操作慢。

                         LinkedList,随机访问慢,但是中间插入和删除快,类似链表。

     

    List常用方法:

     

    class Member{
        int age;
        Member(int i){
            age = i;
        }
        public String toString(){
            return "member"+age;
        }
    }
    
    public class ListMethod {
        public static void main(String[] args) {
            List<Member> members = new ArrayList<Member>();
            Member member1 = new Member(1);
            
            //添加元素
            members.add(member1);
            
            //判断容器是否为空
            System.out.println(members.isEmpty());
            
            //判断容器是否包含该元素
            System.out.println(members.contains(member1));
            
            //显示索引
            System.out.println(members.indexOf(member1));
            
            //移除元素
            members.remove(member1);
            System.out.println(members);
            
            Member member2 = new Member(2);
            Member member3 = new Member(3);
            Member member4 = new Member(4);
            members.add(member2);
            members.add(member3);
            members.add(member4);
            
            //类似subString,从索引0开始截取到1,包含0和1
            System.out.println(members.subList(0, 2));
            
            //移除 不同于remove
            //removeAll(Collection<?> c) 
            //remove(int index)   remove(Object o) 
            members.removeAll(members);
            System.out.println(members);
        }
        
    }


    6)迭代器

     

    在没用迭代器之前,遍历是这样写的:

     

            for(int i = 0; i < newList.size(); i++){
                System.out.println(newList.get(i));
            }


    而迭代器被称为轻量级对象,创建的代价小。

     

     

     

     

            Iterator<Member> iterator = members.iterator();
            while(iterator.hasNext()){
                System.out.println(iterator.next());
            }


    next移动下一个元素,但是拿到的当前元素。hasNext检查是否还有元素,Iteratorf容器返回一个Iterator。

     

     

    7)ListIterator

    这个之前没听过,Iterator只能向前移动,ListIterator可以双向移动。

     

            ListIterator<Member> iterator = members.listIterator();
            while(iterator.hasNext()){
                System.out.println(iterator.next());
            }
            
            while(iterator.hasPrevious()){
                System.out.println(iterator.previous());
            }


    向前输出,向后输出。

     

     

    8)LinkedList

    插入移除高效。

    方法很容易理解,属于自解释型的方法名。

     

    public class TestLinkedList {
        public static void main(String[] args) {
            LinkedList<Member> members = new LinkedList<Member>();
            Member member1 = new Member(1);
            Member member2 = new Member(2);
            Member member3 = new Member(3);
            members.add(member1);
            members.add(member2);
            members.add(member3);
            
            //返回列表头
            System.out.println(members.peek());
            
            //移除并返回列表头
            System.out.println(members.removeFirst());
            System.out.println(members);
            
            //返回并移除表头
            System.out.println(members.poll());
            System.out.println(members);
            
            //removelast 移除最后一个
            members.add(member1);
            members.add(member2);
            System.out.println(members.removeLast());
            System.out.println(members);
            
            
            //addLast和add一样 都是往列表尾插入元素 addFirst自然就是表头
            members.add(member2);
            members.addFirst(member2);
            members.addLast(member2);
            System.out.println(members);
        }
    }

     

     

     

    9)Stack

    栈,后进先出,

    其实用LinkedList就可以实现栈的功能了,push,进的时候,只需要addFirst,pop,出的时候,只需要removeFirst,这样就达到了先进后出

     

    10)Set

    Set,元素不重复。

    Set与Collection有完全一样的接口,但是不同List,虽然Set就是Collection,但是行为不同,这就是多态和继承的应用了。

     

    public class TestSet {
        public static void main(String[] args) {
            Set<Integer> set = new HashSet<Integer>();
            Random r = new Random(400);
            
            for(int i = 0;i<20;i++){
                set.add(r.nextInt(300));
            }
            System.out.println(set);
        }
    }


    HashSet,没有重复元素,顺序也无规律,其实是使用了散列,以后会提到。

     

     

    如果:

      Set<Integer> set = new TreeSet<Integer>();

    会发现是有序的,TreeSet将元素存储在了红黑树里面。

     

    书中有一处错误:LinkedHashSet写成LinkedHashList了,它也使用散列,但是看起来使用了链表维护元素插入顺序。

     

    持有对象这一章的内容不会难,因为平时用的太多,就是内容多,接下来下一篇会写Map,Queue和总结这一章的东西。

     

     

    ------------------------- 独在异乡为异客,每逢中秋倍思亲-----------------------------

    展开全文
  • 这一章节我们来讨论游戏,synchronized持有对象锁与类锁的不同点-两种锁是并行的东西,没有交集。1.同步持有对象锁或者类锁package com.ray.deepintothread.ch02.topic_3; public class SynchInstance5 { public ...

    这一章节我们来讨论游戏,synchronized持有对象锁与类锁的不同点-两种锁是并行的东西,没有交集。

    1.同步持有对象锁或者类锁

    package com.ray.deepintothread.ch02.topic_3;
    
    public class SynchInstance5 {
    	public static void main(String[] args) throws InterruptedException {
    		MyTestObjectFive myTestObjectFive = new MyTestObjectFive();
    		for (int i = 0; i < 2; i++) {
    			ThreadFive threadFive = new ThreadFive(myTestObjectFive);
    			Thread thread = new Thread(threadFive);
    			thread.setName("" + i);
    			thread.start();
    		}
    	}
    }
    
    class ThreadFive implements Runnable {
    
    	private MyTestObjectFive myTestObjectFive;
    
    	public ThreadFive(MyTestObjectFive myTestObjectFive) {
    		this.myTestObjectFive = myTestObjectFive;
    	}
    
    	@Override
    	public void run() {
    		try {
    			if (Integer.parseInt(Thread.currentThread().getName()) % 2 == 0) {
    				myTestObjectFive.test1();
    			} else {
    				myTestObjectFive.test2();
    			}
    
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    	}
    }
    
    class MyTestObjectFive {
    	public synchronized void test1() throws InterruptedException {
    		for (int i = 0; i < 5; i++) {
    			System.out.println(i);
    			Thread.sleep(100);
    		}
    	}
    
    	public synchronized void test2() throws InterruptedException {
    		for (int i = 0; i < 5; i++) {
    			System.out.println(i);
    			Thread.sleep(100);
    		}
    	}
    }

    输出:

    0
    1
    2
    3
    4
    0
    1
    2
    3
    4


    package com.ray.deepintothread.ch02.topic_3;
    
    public class SynchInstance6 {
    	public static void main(String[] args) throws InterruptedException {
    		for (int i = 0; i < 2; i++) {
    			ThreadSix threadSix = new ThreadSix();
    			Thread thread = new Thread(threadSix);
    			thread.setName("" + i);
    			thread.start();
    		}
    	}
    }
    
    class ThreadSix implements Runnable {
    
    	@Override
    	public void run() {
    		try {
    			if (Integer.parseInt(Thread.currentThread().getName()) % 2 == 0) {
    				MyTestObjectSix.test1();
    			} else {
    				MyTestObjectSix.test2();
    			}
    
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    	}
    }
    
    class MyTestObjectSix {
    	public static synchronized void test1() throws InterruptedException {
    		for (int i = 0; i < 5; i++) {
    			System.out.println(i);
    			Thread.sleep(100);
    		}
    	}
    
    	public static synchronized void test2() throws InterruptedException {
    		for (int i = 0; i < 5; i++) {
    			System.out.println(i);
    			Thread.sleep(100);
    		}
    	}
    }

    输出:

    0
    1
    2
    3
    4
    0
    1
    2
    3
    4

    像前面章节一样,当我们同步持有对象锁或者类锁,输出的是按照顺序的


    2.当同时持有对象锁和类锁

    package com.ray.deepintothread.ch02.topic_3;
    
    public class SynchInstance4 {
    	public static void main(String[] args) throws InterruptedException {
    		MyTestObjectFour myTestObjectThree = new MyTestObjectFour();
    		for (int i = 0; i < 2; i++) {
    			ThreadFour threadFour = new ThreadFour(myTestObjectThree);
    			Thread thread = new Thread(threadFour);
    			thread.setName("" + i);
    			thread.start();
    		}
    	}
    }
    
    class ThreadFour implements Runnable {
    
    	private MyTestObjectFour myTestObjectThree;
    
    	public ThreadFour(MyTestObjectFour myTestObjectThree) {
    		this.myTestObjectThree = myTestObjectThree;
    	}
    
    	@Override
    	public void run() {
    		try {
    			if (Integer.parseInt(Thread.currentThread().getName()) % 2 == 0) {
    				myTestObjectThree.test1();
    			} else {
    				MyTestObjectFour.test2();
    			}
    
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    	}
    }
    
    class MyTestObjectFour {
    	public synchronized void test1() throws InterruptedException {
    		for (int i = 0; i < 5; i++) {
    			System.out.println(i);
    			Thread.sleep(100);
    		}
    	}
    
    	public static synchronized void test2() throws InterruptedException {
    		for (int i = 0; i < 5; i++) {
    			System.out.println(i);
    			Thread.sleep(100);
    		}
    	}
    }

    输出:

    0
    0
    1
    1
    2
    2
    3
    3
    4
    4


    从输出可以看见,两者就像平行的关系,没有交集,交替的出现。但也是有秩序的。


    总结:这一章节讨论了synchronized持有对象锁与类锁的不同点


    这一章节就到这里,谢谢

    ------------------------------------------------------------------------------------

    我的github:https://github.com/raylee2015/DeepIntoThread


    目录:http://blog.csdn.net/raylee2007/article/details/51204573


    展开全文
  • iOS 中的 block 是如何持有对象

    千次阅读 2016-08-14 11:25:17
    Block 是 Objective-C 中笔者最喜欢的特性,它为 Objective-C 这门语言提供了强大的函数式编程...这篇文章主要会介绍 block 是如何持有并且释放对象的。文章中的代码都出自 Facebook 开源的用于检测循环引用的框架 F

    Block 是 Objective-C 中笔者最喜欢的特性,它为 Objective-C 这门语言提供了强大的函数式编程能力,而最近苹果推出的很多新的 API 都已经开始原生的支持 block 语法,可见它在 Objective-C 中变得越来越重要。


    这篇文章主要会介绍 block 是如何持有并且释放对象的。文章中的代码都出自 Facebook 开源的用于检测循环引用的框架 FBRetainCycleDetector。


    为什么会谈到 block


    可能很多读者会有这样的疑问,本文既然是对 FBRetainCycleDetector 解析的文章,为什么会提到 block?原因其实很简单,因为在 iOS 开发中大多数的循环引用都是因为 block 使用不当导致的,由于 block 会 retain 它持有的对象,这样就很容易造成循环引用,最终导致内存泄露。


    在 FBRetainCycleDetector 中存在这样一个类 FBObjectiveCBlock,这个类的 - allRetainedObjects 方法就会返回所有 block 持有的强引用,这也是文章需要关注的重点。


    - (NSSet *)allRetainedObjects {

    NSMutableArray *results = [[[super allRetainedObjects] allObjects] mutableCopy];

    __attribute__((objc_precise_lifetime)) id anObject = self.object;

    void *blockObjectReference = (__bridge void *)anObject;

    NSArray *allRetainedReferences = FBGetBlockStrongReferences(blockObjectReference);

    for (id object in allRetainedReferences) {

    FBObjectiveCGraphElement *element = FBWrapObjectGraphElement(self, object, self.configuration);

    if (element) {

    [results addObject:element];

    }

    }

    return [NSSet setWithArray:results];

    }


    这部分代码中的大部分都不重要,只是在开头调用父类方法,在最后将获取的对象包装成一个系列 FBObjectiveCGraphElement,最后返回一个数组,也就是当前对象 block 持有的全部强引用了。


    Block 是什么?


    对 block 稍微有了解的人都知道,block 其实是一个结构体,其结构大概是这样的:


    struct BlockLiteral {

    void *isa;

    int flags;

    int reserved;

    void (*invoke)(void *, ...);

    struct BlockDescriptor *descriptor;

    };

    struct BlockDescriptor {

    unsigned long int reserved;

    unsigned long int size;

    void (*copy_helper)(void *dst, void *src);

    void (*dispose_helper)(void *src);

    const char *signature;

    };


    在 BlockLiteral 结构体中有一个 isa 指针,而对 isa了解的人也都知道,这里的 isa 其实指向了一个类,每一个 block 指向的类可能是 __NSGlobalBlock__、__NSMallocBlock__ 或者 __NSStackBlock__,但是这些 block,它们继承自一个共同的父类,也就是 NSBlock,我们可以使用下面的代码来获取这个类:


    static Class _BlockClass() {

    static dispatch_once_t onceToken;

    static Class blockClass;

    dispatch_once(&onceToken, ^{

    void (^testBlock)() = [^{} copy];

    blockClass = [testBlock class];

    while(class_getSuperclass(blockClass) && class_getSuperclass(blockClass) != [NSObject class]) {

    blockClass = class_getSuperclass(blockClass);

    }

    [testBlock release];

    });

    return blockClass;

    }


    Objective-C 中的三种 block __NSMallocBlock__、__NSStackBlock__ 和 __NSGlobalBlock__ 会在下面的情况下出现:



    • 在 ARC 中,捕获外部了变量的 block 的类会是 __NSMallocBlock__ 或者 __NSStackBlock__,如果 block 被赋值给了某个变量在这个过程中会执行 _Block_copy 将原有的 __NSStackBlock__ 变成 __NSMallocBlock__;但是如果 block 没有被赋值给某个变量,那它的类型就是 __NSStackBlock__;没有捕获外部变量的 block 的类会是 __NSGlobalBlock__ 即不在堆上,也不在栈上,它类似 C 语言函数一样会在代码段中。


    • 在非 ARC 中,捕获了外部变量的 block 的类会是 __NSStackBlock__,放置在栈上,没有捕获外部变量的 block 时与 ARC 环境下情况相同。


    如果我们不断打印一个 block 的 superclass 的话最后就会在继承链中找到 NSBlock 的身影:



    然后可以通过这种办法来判断当前对象是不是 block:


    BOOL FBObjectIsBlock(void *object) {

    Class blockClass = _BlockClass();

    Class candidate = object_getClass((__bridge id)object);

    return [candidate isSubclassOfClass:blockClass];

    }


    Block 如何持有对象


    在这一小节,我们将讨论 block 是如何持有对象的,我们会通过对 FBRetainCycleDetector 的源代码进行分析最后尽量详尽地回答这一问题。


    重新回到文章开头提到的 - allRetainedObjects 方法:


    - (NSSet *)allRetainedObjects {

    NSMutableArray *results = [[[super allRetainedObjects] allObjects] mutableCopy];

    __attribute__((objc_precise_lifetime)) id anObject = self.object;

    void *blockObjectReference = (__bridge void *)anObject;

    NSArray *allRetainedReferences = FBGetBlockStrongReferences(blockObjectReference);

    for (id object in allRetainedReferences) {

    FBObjectiveCGraphElement *element = FBWrapObjectGraphElement(self, object, self.configuration);

    if (element) {

    [results addObject:element];

    }

    }

    return [NSSet setWithArray:results];

    }


    通过函数的符号我们也能够猜测出,上述方法中通过 FBGetBlockStrongReferences 获取 block 持有的所有强引用:


    NSArray *FBGetBlockStrongReferences(void *block) {

    if (!FBObjectIsBlock(block)) {

    return nil;

    }

    NSMutableArray *results = [NSMutableArray new];

    void **blockReference = block;

    NSIndexSet *strongLayout = _GetBlockStrongLayout(block);

    [strongLayout enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {

    void **reference = &blockReference[idx];

    if (reference && (*reference)) {

    id object = (id)(*reference);

    if (object) {

    [results addObject:object];

    }

    }

    }];

    return [results autorelease];

    }


    而 FBGetBlockStrongReferences 是对另一个私有函数 _GetBlockStrongLayout 的封装,也是实现最有意思的部分。


    几个必要的概念


    在具体介绍 _GetBlockStrongLayout 函数的源代码之前,我希望先对其原理有一个简单的介绍,便于各位读者的理解;在这里有三个概念需要介绍,首先是 block 持有的对象都存在的位置。


    如何持有对象


    在文章的上面曾经出现过 block 的结构体,不知道各位读者是否还有印象:


    struct BlockLiteral {

    void *isa;

    int flags;

    int reserved;

    void (*invoke)(void *, ...);

    struct BlockDescriptor *descriptor;

    // imported variables

    };


    在每个 block 结构体的下面就会存放当前 block 持有的所有对象,无论强弱。我们可以做一个小实验来验证这个观点,我们在程序中声明这样一个 block:


    NSObject *firstObject = [NSObject new];

    __attribute__((objc_precise_lifetime)) NSObject *object = [NSObject new];

    __weak NSObject *secondObject = object;

    NSObject *thirdObject = [NSObject new];

    __unused void (^block)() = ^{

    __unused NSObject *first = firstObject;

    __unused NSObject *second = secondObject;

    __unused NSObject *third = thirdObject;

    };


    然后在代码中打一个断点:

    代码中 block 由于被变量引用,执行了 _Block_copy,所以其类型为 __NSMallocBlock__,没有被变量引用的 block 都是 __NSStackBlock__。


    1. 首先打印 block 变量的大小,因为 block 变量其实只是一个指向结构体的指针,所以大小为 8,而结构体的大小为 32;

    2. 以 block 的地址为基址,偏移 32,得到一个指针

    3. 使用 $3[0] $3[1] $3[2] 依次打印地址为 0x1001023b0 0x1001023b8 0x1001023c0 的内容,可以发现它们就是 block 捕获的全部引用,前两个是强引用,最后的是弱引用


    这可以得出一个结论:block 将其捕获的引用存放在结构体的下面,但是为什么这里的顺序并不是按照引用的顺序呢?接下来增加几个变量,再做另一次实验:


    在代码中多加入了几个对象之后,block 对持有的对象的布局的顺序依然是强引用在前、弱引用在后,我们不妨做一个假设:block 会将强引用的对象排放在弱引用对象的前面。但是这个假设能够帮助我们在只有 block 但是没有上下文信息的情况下区分哪些是强引用么?我觉得并不能,因为我们没有办法知道它们之间的分界线到底在哪里。


    dispose_helper


    第二个需要介绍的是 dispose_helper,这是 BlockDescriptor 结构体中的一个指针:


    struct BlockDescriptor {

    unsigned long int reserved;                // NULL

    unsigned long int size;

    // optional helper functions

    void (*copy_helper)(void *dst, void *src); // IFF (1<<25)

    void (*dispose_helper)(void *src);         // IFF (1<<25)

    const char *signature;                     // IFF (1<<30)

    };


    上面的结构体中有两个函数指针,copy_helper 用于 block 的拷贝,dispose_helper 用于 block 的 dispose 也就是 block 在析构的时候会调用这个函数指针,销毁自己持有的对象,而这个原理也是区别强弱引用的关键,因为在 dispose_helper 会对强引用发送 release 消息,对弱引用不会做任何的处理。


    FBBlockStrongRelationDetector


    最后就是用于从 dispose_helper 接收消息的类 FBBlockStrongRelationDetector 了;它的实例在接受 release 消息时,并不会真正的释放,只会将标记 _strong 为 YES:


    - (oneway void)release {

    _strong = YES;

    }

    - (oneway void)trueRelease {

    [super release];

    }


    只有真正执行 trueRelease 的时候才会向对象发送 release 消息。


    因为这个文件覆写了 release 方法,所以要在非 ARC 下编译:


    #if __has_feature(objc_arc)

    #error This file must be compiled with MRR. Use -fno-objc-arc flag.

    #endif


    如果 block 持有了另一个 block 对象,FBBlockStrongRelationDetector 也可以将自身 fake 成为一个假的 block 防止在接收到关于 block 释放的消息时发生 crash:


    struct _block_byref_block;

    @interface FBBlockStrongRelationDetector : NSObject {

    // __block fakery

    void *forwarding;

    int flags;   //refcount;

    int size;

    void (*byref_keep)(struct _block_byref_block *dst, struct _block_byref_block *src);

    void (*byref_dispose)(struct _block_byref_block *);

    void *captured[16];

    }


    该类的实例在初始化时,会设置 forwarding、byref_keep 和 byref_dispose,后两个方法的实现都是空的,只是为了防止 crash:


    + (id)alloc {

    FBBlockStrongRelationDetector *obj = [super alloc];

    // Setting up block fakery

    obj->forwarding = obj;

    obj->byref_keep = byref_keep_nop;

    obj->byref_dispose = byref_dispose_nop;

    return obj;

    }

    static void byref_keep_nop(struct _block_byref_block *dst, struct _block_byref_block *src) {}

    static void byref_dispose_nop(struct _block_byref_block *param) {}


    获取 block 强引用的对象


    到现在为止,获取 block 强引用对象所需要的知识都介绍完了,接下来可以对私有方法 _GetBlockStrongLayout 进行分析了:


    static NSIndexSet *_GetBlockStrongLayout(void *block) {

    struct BlockLiteral *blockLiteral = block;

    if ((blockLiteral->flags & BLOCK_HAS_CTOR)

    || !(blockLiteral->flags & BLOCK_HAS_COPY_DISPOSE)) {

    return nil;

    }

    ...

    }


    • 如果 block 有 Cpp 的构造器/析构器,说明它持有的对象很有可能没有按照指针大小对齐,我们很难检测到所有的对象

    • 如果 block 没有 dispose 函数,说明它无法 retain 对象,也就是说我们也没有办法测试其强引用了哪些对象


    static NSIndexSet *_GetBlockStrongLayout(void *block) {

    ...

    void (*dispose_helper)(void *src) = blockLiteral->descriptor->dispose_helper;

    const size_t ptrSize = sizeof(void *);

    const size_t elements = (blockLiteral->descriptor->size + ptrSize - 1) / ptrSize;

    void *obj[elements];

    void *detectors[elements];

    for (size_t i = 0; i < elements; ++i) {

    FBBlockStrongRelationDetector *detector = [FBBlockStrongRelationDetector new];

    obj[i] = detectors[i] = detector;

    }

    @autoreleasepool {

    dispose_helper(obj);

    }

    ...

    }


    1. 从 BlockDescriptor 取出 dispose_helper 以及 size(block 持有的所有对象的大小)

    2. 通过 (blockLiteral->descriptor->size + ptrSize - 1) / ptrSize 向上取整,获取 block 持有的指针的数量

    3. 初始化两个包含 elements 个 FBBlockStrongRelationDetector 实例的数组,其中第一个数组用于传入 dispose_helper,第二个数组用于检测 _strong 是否被标记为 YES

    4. 在自动释放池中执行 dispose_helper(obj),释放 block 持有的对象


    static NSIndexSet *_GetBlockStrongLayout(void *block) {

    ...

    NSMutableIndexSet *layout = [NSMutableIndexSet indexSet];

    for (size_t i = 0; i < elements; ++i) {

    FBBlockStrongRelationDetector *detector = (FBBlockStrongRelationDetector *)(detectors[i]);

    if (detector.isStrong) {

    [layout addIndex:i];

    }

    [detector trueRelease];

    }

    return layout;

    }


    因为 dispose_helper 只会调用 release 方法,但是这并不会导致我们的 FBBlockStrongRelationDetector 实例被释放掉,反而会标记 _string 属性,在这里我们只需要判断这个属性的真假,将对应索引加入数组,最后再调用 trueRelease 真正的释放对象。


    我们可以执行下面的代码,分析其工作过程:


    NSObject *firstObject = [NSObject new];

    __attribute__((objc_precise_lifetime)) NSObject *object = [NSObject new];

    __weak NSObject *secondObject = object;

    NSObject *thirdObject = [NSObject new];

    __unused void (^block)() = ^{

    __unused NSObject *first = firstObject;

    __unused NSObject *second = secondObject;

    __unused NSObject *third = thirdObject;

    };

    FBRetainCycleDetector *detector = [FBRetainCycleDetector new];

    [detector addCandidate:block];

    [detector findRetainCycles];


    在 dispose_helper 调用之前:

    obj 数组中的每一个位置都存储了 FBBlockStrongRelationDetector 的实例,但是在 dispose_helper 调用之后:


    索引为 4 和 5 处的实例已经被清空了,这里对应的 FBBlockStrongRelationDetector 实例的 strong 已经被标记为 YES、加入到数组中并返回;最后也就获取了所有强引用的索引,同时得到了 block 强引用的对象。


    总结


    其实最开始笔者对这个 dispose_helper 实现的机制并不是特别的肯定,只是有一个猜测,但是在询问了 FBBlockStrongRelationDetector 的作者之后,才确定 dispose_helper 确实会负责向所有捕获的变量发送 release 消息,如果有兴趣可以看这个 issue。这部分的代码其实最开始源于 mikeash 大神的 Circle

    展开全文
  • Java容器可以说是增强程序员编程能力的基本工具,本系列将带您深入理解容器类。 容器的用途 ...我们可以通过创建引用来持有对象,如 Class clazz; 也可以通过数组来持有多个对象,如 Class[...
  • 第十一章 持有对象

    千次阅读 多人点赞 2020-10-09 14:40:38
    基本概念 java容器的作用是 保存对象 Controller 独立元素的序列(可以用foreach遍历) (1) List 按插入顺序保存对象(可重复) (2) Set 不能有重复元素 (3) Queue 按照 排队规则 来确定对象产生的顺序 Map 一组成对的...
  • 组合 继承 持有对象 Adapter设计模式 组合(持有对象)与继承的区别
  • 现在做的系统,牵扯到对象持有另一个对象的概念,在数据库设计表结构时:table 1 中持有table 2 ,简单画一个图吧 便于理解: Table 1  Id_1 (pk)  projId  projStatusId  userGrop   Table 2  ...
  • 第11章 持有对象

    2020-03-18 15:43:47
  • 先说说概念,什么持有持有的,就是“保存对象”,说白了就是对象的灵活(按需)存取,这个需就是Java容器类类库各种类的用武之地。  放一张书上的简单的容器分类图,其中点线框表示接口,实线框表示普通的(具体...
  • 属性持有对象

    2010-01-02 18:05:00
    看flash CS3帮助文档的时候,偶然看到这个概念——属性持有对象。通过几个例子,我们可以对这个概念有个初步的了解。首先就我们常用的Bitmap类对象和BitmapData对象来说,Bitmap类对象属性bitmapData持有BitmapData...
  • 2.2 容器可以通过调用iterator函数来创建一个对应类型的iterator对象,换句话说iterator对象可以接受任何容器调用iterator函数对它的初始化。这也就解释了为何迭代器可以同一不同类型容器的代码重用问题。 看下面的...
  • 在java中为我们提供了大量的持有对象的方法,这些方法可以是我们的程序看起来更简洁,更强大,更高效。好了,闲话不说,接下来我们就简单的总结一下java中的持有对象都有哪些。。。 (1)数组,数组将数字和对象...
  • java中的持有对象之间的继承关系
  • 在Java中内部类的定义与使用一般为成员内部类与匿名内部类,他们的对象都会隐式持有外部类对象的引用,影响外部类对象的回收。通过反编译我们可以来验证这个理论。 public class Outer { private String name; ...
  • //返回指定对象上此 Field 表示的字段的值 System.out.println(field.get(ti)); } catch (IllegalArgumentException | IllegalAccessException e) { // TODO Auto-generated catch block e....
  • colletion只是持有对象的引用,包括list,set,map等,remove,clear之类的函数只是去除对引用的持有。
  • 单例对象释放问题

    千次阅读 2018-03-08 12:21:21
    单例模式就是全局只有一个类A产生的对象不允许产生多个对象。1.static成员变量实现了同类对象间信息共享2.static成员类外存储,求类大小,并不包含在内3.static成员是命名空间属于累的全局变量,存储在data区4....
  • iOS内存管理

    千次阅读 2016-07-16 01:40:17
    iOS内存管理主要参考资料:《Effective Objective-C 2.0》,《Objective-C高级编程 ...1.引用计数在引用计数架构下,每个对象都有个可以递增或递减的计数器,用以表示当前有多少个事物想令此对象继续存活下去。这在OC
  • 考虑一个场景,在运行时你必须找到一个Java线程是否对特定的对象加锁,例如,确认NewsReader线程是否持有NewsPaper对象的锁?如果这个问题出现在任何核心的Java面试中,那么我会自动假设可能有至少两个答案,一个是...
  • Java编程思想 第九章持有你的对象 持有你的对象 Java 有能力在任意时刻任何地点产生任意个数的对象 Java 提供对象持有方式 array(内置, Collection(公用程序库) 容器类都提供 对象置入方法对象取出方法 Arrays ...
  • 在java中,有时为了对象之间的通信,会让两个类互相持有对方引用 在刚学习c++的面向对象时,不能直接像java一样做,而是要有一些小技巧。
  • 如题,两个对象相互持有,当其他所有引用都结束时,只有这两个对象相互有引用,GC会回收吗
  • 首先,如果在未持有对象锁的情况下调用object.wait()/notify(),直接会报错,JDK已经做好保护。 其次,为什么要这么设计?其实这是一种安全设计,为了防止wait错过notify。请看下面代码: boolean wakeuped = false...
  • 再看上面的代码,每个线程中都new了一个Sync类的对象,也就是产生了三个Sync对象,由于不是同一个对象,所以可以多线程同时运行synchronized方法或代码段。 为了验证上述的观点,修改一下代码,让三个线程使用...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 284,846
精华内容 113,938
关键字:

持有对象