作者:梦春情初开 | 来源:互联网 | 2023-10-13 13:26
估计做iOS开发的朋友都遇到多一个问题,就是数组越界,-[__NSArray0objectAtIndex:]:index1beyondboundsforemptyNSArray’
估计做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];
}
又或则这样:通过运行时的方法交换,来达到这个目的
&#8211; (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(&#8220;__NSArrayI&#8221;) swizzleMethod:@selector(objectAtIndex:) withMethod:@selector(swizzleObjectAtIndex:) error:nil];//交换不可变数组
[objc_getClass(&#8220;__NSArrayM&#8221;) 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(&#8220;__NSArrayI&#8221;) swizzleMethod:@selector(objectAtIndex:) withMethod:@selector(swizzleObjectAtIndexI:) error:nil];//交换不可变数组
[objc_getClass(&#8220;__NSArrayM&#8221;) swizzleMethod:@selector(objectAtIndex:) withMethod:@selector(swizzleObjectAtIndexM:) error:nil];//交换不可变数组
};
});
}
然后。。。。我们貌似离题了,其实我们要讨论的是叫类簇啊。。。,其实在解决完上面的问题,我们就已经发现,类簇也就是抽象工厂模式。
什么叫工厂模式?下面看下百度百科里的描述:
抽象工厂模式可以向客户端提供一个接口,使客户端在不必指定产品的具体的情况下,创建多个产品族中的产品对象。,工厂类负责创建抽象产品的具体子类的实例。
具体干啥的我不知啊,我就负责生产出来,然后产品的性能是啥,你用了你就知道啊。。。。
恩,如果不清楚的人,也许会问:这个模式貌似有啥用呢? 我继承不就行啦&#8230;..为啥要有个抽象父类
那我们来看下一个场景,就拿系统的NSNumber来说话吧,char、int、long、double、bool&#8230;,你的思路是,创建一个父类NSNumber,然后NSCharNumber、NSIntNumber&#8230;..,到这里倒没错,因为系统其实也有这些子类,但是:
你给NSCharNumber一个初始化方法叫,initWithChar:,给NSIntNumber初始化方法叫initWithInt:&#8230;.
好吧,你一个一个的也写完了,提交给其他程序员用。。。一个类里,我得import所有的头,我得记住所有的子类名&#8230;.到这里,你自己应该也烦了吧,相比,这个工厂模式就体现了优势了。
写到这里,就完了么?
还没有,我们最开始那里,说到所有空的不可变数组,地址是一样一样的,好奇怪啊。。。不是alloc了就分配了一个地址么。。。
看这里:
NSArray * x =[NSArray alloc];
NSArray * x2 = [NSArray alloc];
NSMutableArray *y =[NSMutableArray alloc];
NSMutableArray *y2=[NSMutableArray alloc];
NSLog(@&#8221;%p %p %p %p&#8221;,x,x2,y,y2);
猜猜怎么着?x==x2,y==y2
而且,不init的话,出来的数组是NSPlaceholderArray类型的啊,
那我们可以猜想下,里面的alloc里是不是静态方法实现的获取placeholderArray,然后再根据不同的类型进行init再生成对应的子类。