iOS 类簇及越界处理

来源:互联网 发布:乐视mac码 编辑:程序博客网 时间:2024/06/06 18:25
 估计做iOS开发的朋友都遇到多一个问题,就是数组越界,

-[__NSArray0 objectAtIndex:]: index 1 beyond bounds for empty NSArray'

-[__NSArrayI objectAtIndex:]: index 1 beyond bounds [0 .. 0]'

-[__NSArrayM objectAtIndex:]: index 1 beyond bounds for empty array'

-[__NSArrayM objectAtIndex:]: index 1 beyond bounds [0 .. 0]'  

曾经也许你想过,那我是否可以重写objectAtIndex方法,然后判断下是否越界,大概就这样:

- (ObjectType)objectAtIndex:(NSUInteger)index
{
    if(self.count <= index) return nil;
    return [super objectAtIndex:index];
}
又或则这样:通过运行时的方法交换,来达到这个目的
- (ObjectType)new_objectAtIndex:(NSUInteger)index
{
    if(self.count <= index) return nil;
    return [self new_objectAtIndex:index];
}


继承重写的话,是可以达到目的的,但是我项目都写完了,你叫我一个个的去修改之前的数组初始化类方法为子类么?那得多麻烦啊。

方法交换的话,想想很不错哦,一劳永逸,但是,如果你真的去试试,你发现,天哪,这都什么鬼,根本没有用啊,


其实,NSArray只是的抽象的类罢了,其实具体是神马类,我们先看下下面这张图:


图片

从这张图应该能至少看出:
1、arr1和arr2类名叫_NSArray0,地址还相等;
2、未init的arr8,类名叫做_NSPlaceHolderArray;
3、初始化后的可变数组类名都叫_NSArrayM;
4、初始化后的不可变数组类名都叫_NSArrayI.

那现在我们回到方法交换那里,现在假设你对NSAarry进行方法交换。交换的方法就是 
objectAtIndex: 那么交换的就是NSAarry的方法,它本身就是抽象的父类,也就是说其实都是空的实现,而实际运行时,是执行实际子类的objectAtIndex方法,所以这个交换并没起到什么用处。那怎么办呢,那我们去交换子类的实现即可。


+ (void)load

{

    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

        @autoreleasepool

        {
    /*   这是错误的交换方法

            [objc_getClass("__NSArrayI"swizzleMethod:@selector(objectAtIndex:) withMethod:@selector(swizzleObjectAtIndex:) error:nil];//交换不可变数组

           [objc_getClass("__NSArrayM"swizzleMethod:@selector(objectAtIndex:) withMethod:@selector(swizzleObjectAtIndex:) error:nil];//交换不可变数组

错误原因:假设3个人各有1个球,序号分别为1、2、3,那么如果1和3交换后,2又和3交换,那么最终三个人拿到的球的序号就是:3、1、2;但是,在上面交换的时候,我们注意,其实人员1、和人2都想要一个球3,实际我们想要得到的结果是3、3、X;  因此运行结果肯定不符合我们的预期,甚至崩溃,当可变数组调用objextAtIndex的时候,就会进入死循环。

    */ 
  /*
    下面的是正确的做法,将球3复制一份,因此,人员3就有2个球,人员1和第三个人的第一个球换,人员2和人员3的第二个球交换,结果就是3 3 X 了
    */ 

            [objc_getClass("__NSArrayI"swizzleMethod:@selector(objectAtIndex:) withMethod:@selector(swizzleObjectAtIndexI:) error:nil];//交换不可变数组

           [objc_getClass("__NSArrayM"swizzleMethod:@selector(objectAtIndex:) withMethod:@selector(swizzleObjectAtIndexM:) error:nil];//交换不可变数组

        };

    });

}


然后。。。。我们貌似离题了,其实我们要讨论的是叫类簇啊。。。,其实在解决完上面的问题,我们就已经发现,类簇也就是抽象工厂模式。

什么叫工厂模式?下面看下百度百科里的描述:
抽象工厂模式可以向客户端提供一个接口,使客户端在不必指定产品的具体的情况下,创建多个产品族中的产品对象。,工厂类负责创建抽象产品的具体子类的实例。
 
具体干啥的我不知啊,我就负责生产出来,然后产品的性能是啥,你用了你就知道啊。。。。 

恩,如果不清楚的人,也许会问:这个模式貌似有啥用呢? 我继承不就行啦.....为啥要有个抽象父类


那我们来看下一个场景,就拿系统的NSNumber来说话吧,char、int、long、double、bool...,你的思路是,创建一个父类NSNumber,然后NSCharNumber、NSIntNumber.....,到这里倒没错,因为系统其实也有这些子类,但是:
你给NSCharNumber一个初始化方法叫,initWithChar:,给NSIntNumber初始化方法叫initWithInt:....
好吧,你一个一个的也写完了,提交给其他程序员用。。。一个类里,我得import所有的头,我得记住所有的子类名....到这里,你自己应该也烦了吧,相比,这个工厂模式就体现了优势了。



写到这里,就完了么?
还没有,我们最开始那里,说到所有空的不可变数组,地址是一样一样的,好奇怪啊。。。不是alloc了就分配了一个地址么。。。
看这里:

    NSArray * x =[NSArray alloc];

    NSArray * x2 = [NSArray alloc];

    NSMutableArray *y =[NSMutableArray alloc];

    NSMutableArray *y2=[NSMutableArray alloc];

    NSLog(@"%p %p %p %p",x,x2,y,y2);


猜猜怎么着?x==x2,y==y2
而且,不init的话,出来的数组是NSPlaceholderArray类型的啊,
那我们可以猜想下,里面的alloc里是不是静态方法实现的获取placeholderArray,然后再根据不同的类型进行init再生成对应的子类。

1 0