Is there any way to monkey-patch or swizzle an NSArray (or other Class Cluster)?

佐手、 提交于 2019-12-09 07:12:59

问题


Today I was working on a project in which I wanted to "alias" an alternative method for all instances of NSArray, and didn't think it would be too difficult with some good old-fashioned method swizzling.

I broke out JRSwizzle and…

[NSArray jr_swizzleMethod:@selector(objectAtIndex:) withMethod:@selector(objectAtIndex_accordingToMe:) error:nil];

To be clear, I paired this with the appropriate category on NSArray, an instance method called objectAtIndex_accordingToMe:.

However, I was just getting that same old object, at that same old index. Sigh. Ultimately, I figured out that despite not throwing any errors - I'm not going to achieve these results due to the fact that NSArray is a class cluster

I guess my question is more of an unwillingness to accept that "this" is really the end of the road trying to override NSArray methods. I mean, come on this is NSArray.. people must wanna muck around with it, no? One would think that Apple's foundation classes would be a prime target for swizzlers, everywhere!

So, is there a way to alter, alias, monkey-patch, override, or otherwise have your way with… an NSArray, etc. (without subclassing)?


回答1:


Presumably you have a particular array for which you'd like this behavior. You can get that instance's class object, no matter what it is, and swizzle that quite easily:

[[myArray class] jr_swizzleMethod:@selector(objectAtIndex:) withMethod:@selector(objectAtIndex_accordingToMe:) error:nil];

There's also only a few concrete subclasses of NSArray:

NSArray * trees = [NSArray array];
NSArray * birds = [NSArray arrayWithObjects:@"Albatross", @"Budgerigar", @"Cardinal", nil];
NSMutableArray * dogs = [NSMutableArray arrayWithObjects:@"Airedale", @"Beagle", @"Collie", nil];

NSLog(@"%@ %@ %@", [trees class], [birds class], [dogs class]);

We get __NSArrayI for the first two and __NSArrayM for the third, so potentially (this is very fragile) you could use a runtime function to grab the class object by name:

[objc_getClass("__NSArrayI") jr_swizzleMethod:@selector(objectAtIndex:) withMethod:@selector(objectAtIndex_accordingToMe:) error:nil];



回答2:


It's not just that it's a class cluster. NSArray is toll-free bridged to CFArray, and you can't swizzle Core Foundation. So this is very unlikely to work in general.

But what are you trying to solve? If you want to add a new method, use a category. They work on class clusters just fine. Modifying the behavior of some built-in on NSArray seems a recipe for disaster (entertaining as it might be as an exercise).

Before going too far, you probably want to at least take a look at CFArray.c and understand how some of the underlying stuff is implemented.


EDIT: While I would never do this in production code, you may get some of what you want by hijacking individual array instances with ISA-swizzling. See ISASwizzle for some example code. The code explanation is in Chapter 20 of iOS:PTL. Search out for "isa swizzle" and you should find more on the net. It's how KVO is implemented. But with NSArray... wow, that's gotta be fragile.



来源:https://stackoverflow.com/questions/11751473/is-there-any-way-to-monkey-patch-or-swizzle-an-nsarray-or-other-class-cluster

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!