iOS开发之__NSArray0、__NSArrayI、__NSArrayM和__NSPlaceholderArray

概述

最近在整理CoreAnimation,写代码的时候遇到了下面的问题

-[__NSArrayI rectValue]: unrecognized selector sent to instance 0x608000252060

造成unrecognized selector sent to instance XXX,大部分情况下是因为对象被提前release了,在你心里不希望他release的情况下,指针还在,对象已经不在了,网上有好多的解决办法,这里不在多说。但是这个__NSArrayI是个什么鬼?

Class Clusters

首先说一下Class Clusters(类簇)是抽象工厂模式在iOS下的一种实现,iOS中如NSString、NSArray、NSDictionary以及NSNumber都运作在这一模式下。在我们完全不知情的情况下,隐藏了很多具体的实现类,只暴露出简单的接口。

NSArray的类簇

在《effective objective-c 2.0编写高质量iOS与OS X代码的52个有效方法》中这样写道:系统框架中有许多类簇,大部分collection类都是类族。例如NSArray与其可变版本NSMutableArray。这样看来实际上有两个抽象基类,一个用于不可变数组,一个用于可变数组。尽管具备公共接口的类有两个,但任然可以合起来算一个类族。不可变的类定义了对所有数组都通用的方法,而可变类则定义了那些只适用于可变数组的方法。两个类共同属于同一个类族,这意味着二者在实现各自类型的数组时可以共用实现代码,此外还能把可变数组复制成不可变数组,反之亦然。

在使用NSArray的alloc方法来获取实例的时候,该方法会首先分配一个属于某类的实例,此实例充当“占位数组”(placeholder array)。该数组稍后会转为另一个类的实例,而那个类则是NSArray的实体子类。这个过程稍显复杂,其完整的解释已超出本书的范围。

像NSArray这样的类的背后其实是个类族(对于大部分collection类都是这样),明白这一点很重要,否则就可能写出下面这种代码:

id mybeAnArray = /**********/;

if ( [mybeAnArray class]==[NSArray class] ){

//will never be hit

}

你要是知道NSArray是个类族,那就会明白上面的代码错在哪里:其中if语句永远不可能为真。[mybeAnArray class]所返回的类绝不可能是NSArray类本身,因为由NSArray的初始化方法所返回的那个实例其类型是隐藏在类族公共接口(publlic facade)后面的某个内部类型(internal type)。

不过,任然有方法判断出某个实例所属的类是否位于类族中。如下:

id mybeAnArray = /**********/;

if( [mybeAnArray isKindOfClass:[NSArray class]]){

//will be hit

}

言归正传,下面我们来说说__NSArray0、__NSArrayI、__NSArrayM和__NSPlaceholderArray到底是什么鬼。

__NSPlacehodlerArray

为了验证,我们把将原有的alloc+init拆开写:

id arr1 = [NSArray alloc];

id arr2 = [NSMutableArray alloc];

id arr3 =[arr1 init];

id arr4=[arr2 init];

输出结果是:

arr1=__NSPlaceholderArray

arr2=__NSPlaceholderArray

arr3=__NSArray0

arr4=__NSArrayM

发现通过alloc之后都生成了__NSPlaceholderArray。后面的init都是把消息发送给了这个中间对象。再由它做工厂,生成真的对象。

所以总结一下就是:__NSArrayI是NSArray的真正类型 ,__NSArrayM是NSMutableArray真正类型。

原文发布于微信公众号 - iOS开发笔记(roclel)

原文发表时间:2017-08-01

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏ShaoYL

IOS开发系列—Objective-C之Foundation框架

1926
来自专栏進无尽的文章

编码篇 - NSInvocation的简单使用

在认识 NSInvocation 之前,iOS开发中我们一般会使用以下两种方式去调用一个方法

1482
来自专栏Alice

IOS6学习笔记(三)

1.ARC空声明变量   使用ARC的另一个优势是所有未初始化的变量默认都是“空值化”的。这意味着像下面这样的声明使用ARC编译后指向的是空值(nil):  ...

1899
来自专栏数据结构与算法

6261:汉诺塔问题

6261:汉诺塔问题 总时间限制: 1000ms 内存限制: 65536kB描述 约19世纪末,在欧州的商店中出售一种智力玩具,在一块铜板上有三根杆,最左边的...

3755
来自专栏编程之旅

iOS开发——正则表达式验证手机号、密码

App的实际应用中,用户登陆功能基本是每个App都有需求的一个功能。而当前我们很常规的做法,就是让用户把手机号作为自己的用户名,而在注册获取短信验证码的过程中,...

4012
来自专栏菩提树下的杨过

objective-C 的内存管理之-引用计数

obj-c本质就是"改进过的c语言",大家都知道c语言是没有垃圾回收(GC)机制的(注:虽然obj-c2.0后来增加了GC功能,但是在iphone上不能用,因此...

20410
来自专栏IMWeb前端团队

Promise的简单实现

本篇文章通过构建一个简单的Promise对象来了解如何做到异步获得数据。 使用方法 const fetch = function(url) { return...

2219
来自专栏菩提树下的杨过

objective-C中的Class(类类型),Selector(选择器SEL),函数指针(IMP)

今天在园子里看到了一篇牛文“Objective-C 2.0 with Cocoa Foundation--- 5,Class类型,选择器Selector以及函数...

2355
来自专栏码生

ios 获取属性的类型

1712
来自专栏青玉伏案

Objective-C中把数组中字典中的数据转换成URL

        可能上面的标题有些拗口,学过PHP的小伙伴们都知道,PHP中的数组的下标是允许我们自定义的,PHP中的数组确切的说就是键值对。而在OC我们要用字...

25210

扫码关注云+社区

领取腾讯云代金券