美文网首页
从NSArray看类簇

从NSArray看类簇

作者: seeek | 来源:发表于2017-07-17 17:18 被阅读0次

Class Clusters

Class Clusters(类簇)是抽象工厂模式在iOS下的一种实现,众多常用类,如NSString,NSArray,NSDictionary,NSNumber都运作在这一模式下,它是接口简单性和扩展性的权衡体现,在我们完全不知情的情况下,偷偷隐藏了很多具体的实现类,只暴露出简单的接口。

NSArray的类簇

虽然官方文档中拿NSNumber说事儿,但Foundation并没有像图中描述的那样为每个number都弄一个子类,于是研究下NSArray类簇的实现方式。

__NSPlacehodlerArray

熟悉这个模式的同学很可能看过下面的测试代码,将原有的alloc+init拆开写:

idobj1 = [NSArrayalloc];// __NSPlacehodlerArray *

idobj2 = [NSMutableArrayalloc];// __NSPlacehodlerArray *

idobj3 = [obj1 init];// __NSArrayI *

idobj4 = [obj2 init];// __NSArrayM *

发现+ alloc后并非生成了我们期望的类实例,而是一个__NSPlacehodlerArray的中间对象,后面的- init或- initWithXXXXX消息都是发送给这个中间对象,再由它做工厂,生成真的对象。这里的__NSArrayI和__NSArrayM分别对应Immutable和Mutable(后面的I和M的意思)

于是顺着思路猜实现,__NSPlacehodlerArray必定用某种方式存储了它是由谁alloc出来的这个信息,才能在init的时候知道要创建的是可变数组还是不可变数组

于是乎很开心的去看了下*obj1的内存布局:

下面是32位模拟器中的内存布局(64位太长不好看就临时改32位了- -),第一个箭头是*obj1,第二个是*obj2

我们知道,对象的前4字节(32位下)为isa指针,指向类对象地址,上图所示的0x0051E768就是__NSPlacehodlerArray类对象地址,可以从lldb下po这个地址来验证。

那么问题来了,这个中间对象并没有储存任何信息诶(除了isa外就都是0了),那它init的时候咋知道该创建什么呢?

经过研究发现,Foundation用了一个很贱的比较静态实例地址方式来实现,伪代码如下:

static__NSPlacehodlerArray*GetPlaceholderForNSArray() {

static__NSPlacehodlerArray*instanceForNSArray;

if(!instanceForNSArray) {

instanceForNSArray= [[__NSPlacehodlerArrayalloc] init];

}

returninstanceForNSArray;

}

static__NSPlacehodlerArray*GetPlaceholderForNSMutableArray() {

static__NSPlacehodlerArray*instanceForNSMutableArray;

if(!instanceForNSMutableArray) {

instanceForNSMutableArray= [[__NSPlacehodlerArrayalloc] init];

}

returninstanceForNSMutableArray;

}

// NSArray实现

+ (id)alloc {

if(self== [NSArrayclass]) {

returnGetPlaceholderForNSArray()

}

}

// NSMutableArray实现

+ (id)alloc {

if(self== [NSMutableArrayclass]) {

returnGetPlaceholderForNSMutableArray()

}

}

// __NSPlacehodlerArray实现

- (id)init {

if(self== GetPlaceholderForNSArray()) {

self= [[__NSArrayIalloc] init];

}

elseif(self== GetPlaceholderForNSMutableArray()) {

self= [[__NSArrayMalloc] init];

}

returnself;

}

Foundation不是开源的,所以上面的代码是猜测的,思路大概就是这样,可以这样验证下:

idobj1 = [NSArrayalloc];

idobj2 = [NSArrayalloc];

idobj3 = [NSMutableArrayalloc];

idobj4 = [NSMutableArrayalloc];

// 1和2地址相同,3和4地址相同,无论多少次都相同,且地址相差16位

静态不可变空对象

除此之外,Foundation对不可变版本的空数组也做了个小优化:

NSArray*arr1 = [[NSArrayalloc] init];NSArray*arr2 = [[NSArrayalloc] init];NSArray*arr3 = @[];NSArray*arr4 = @[];NSArray*arr5 = @[@1];

上边1-4号都指向了同一个对象,而arr5指向了另一个对象。

若干个不可变的空数组间没有任何特异性,返回一个静态对象也理所应当。

不仅是NSArray,Foundation中如NSString,NSDictionary,NSSet等区分可变和不可变版本的类,空实例都是静态对象(NSString的空实例对象是常量区的@"")

所以也给用这些方法来测试对象内存管理的同学提个醒,很容易意料之外的。

References

https://developer.apple.com/library/ios/documentation/general/conceptual/CocoaEncyclopedia/ClassClusters/ClassClusters.html

http://iphonedevwiki.net/index.php/Foundation.framework/Inheritance_hierarchy

相关文章

  • 从NSArray看类簇

    Class Clusters Class Clusters(类簇)是抽象工厂模式在iOS下的一种实现,众多常用类,...

  • 类簇,从NSArray说起

    在iOS开发中,广泛运用了类蔟(Class clusters)的设计模式。如NSNumber、NSString、N...

  • 对NSArray或者NSMutableArray的方法交换的问题

    原因: NSArray/NSMutableArray 是抽象类,使用类簇创建,真正的实现类是__NSArrayI/...

  • 类簇

    类簇在iOS中是一种非常实用的模式,例如:NSArray, NSDictionary等。 那么类簇有哪些特点了: ...

  • iOS 设计模式

    工厂模式(类簇):uibutton nsnumber nsarray nsstring装饰器模式:Category...

  • 关于类簇

    在ios中,比如NSString,NSArray, NSNumber 这些都是类簇,大概就是说,他们在alloc的...

  • iOS中数组的优化

    先写些结论,有空了再分析 iOS使用了类簇模式,比如数据创建NSArray和NSMutableArray时都是通过...

  • 类簇

    类簇(Class Clusters)是抽象工厂模式在iOS下的一种实现,Function中,很多常用类,如NSSt...

  • 类簇

    // 类簇 // 在 OC 中, init开头的初始化方法所做的和其他方法并没有很大不同,只是将 self 进行了...

  • 类簇

    首先需要了解一下iOS的 类簇 ,https://www.jianshu.com/p/3ae2f9589fae 我...

网友评论

      本文标题:从NSArray看类簇

      本文链接:https://www.haomeiwen.com/subject/jhlqkxtx.html