【问题标题】:Is there any way to monkey-patch or swizzle an NSArray (or other Class Cluster)?有没有办法对 NSArray (或其他类集群)进行猴子补丁或调配?
【发布时间】:2012-08-01 01:38:33
【问题描述】:

今天我正在做一个项目,我想为NSArray 的所有实例“别名”一个替代方法,并且认为使用一些good old-fashioned method swizzling. 不会太困难

我爆发了JRSwizzle 并且……

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

为了清楚起见,我将它与 NSArray 上的适当类别配对,这是一个名为 objectAtIndex_accordingToMe: 的实例方法。

但是,我只是在相同的旧索引处获得相同的旧对象。叹。最终,我发现尽管没有抛出任何错误 - 我不会达到这些结果due to the fact that NSArray is a class cluster

我想我的问题更多是不愿意接受“这个”真的是试图覆盖NSArray 方法的尽头。我的意思是,来吧这是NSArray..人们一定想搞砸它,不是吗?有人会认为 Apple 的基础课程将成为各地调酒师的主要目标!

那么,有没有办法改变、别名、monkey-patch、覆盖或以其他方式使用......一个 NSArray 等(没有子类化)?

【问题讨论】:

  • 你想完成什么?
  • 只是一个想法,但我之前使用过目标 C 运行时来调动代码。在较低级别,您可以获取返回数组的类并直接对其进行调整,而不是尝试使用类别。那么实际的实现类应该无关紧要。
  • @NSResponder 我经常使用objectAtNormalizedIndex 方法,它的作用类似于 NSArray 上的一种循环队列——我想在这里实现它,但不需要对现有代码进行一百万次更改。我以前从来没有为 swizzling 烦恼过,所以我想看看我能不能做到这一点,但我想也许 Apple 似乎讨厌所有与 swizzle 相关的东西(SIMBL 等)是有原因的。
  • 混搭基本系统类会导致问题。框架依赖于特定的功能,混合 NSArray 将打破这些假设并导致极其难以重现的错误。即使你能做到,也请不要这样做。

标签: objective-c cocoa nsarray monkeypatching swizzling


【解决方案1】:

这不仅仅是一个类集群。 NSArrayCFArray 是免费的,你不能调配Core Foundation。所以这通常不太可能奏效。

但是您要解决什么问题?如果要添加新方法,请使用类别。他们在类集群上工作得很好。修改 NSArray 上某些内置函数的行为似乎是灾难的根源(这可能是一个有趣的练习)。

走得太远之前,您可能至少想看看CFArray.c 并了解一些底层内容是如何实现的。


编辑:虽然我永远不会在生产代码中这样做,但您可以通过使用 ISA-swizzling 劫持单个数组实例来获得您想要的一些东西。有关示例代码,请参阅ISASwizzle。代码解释在iOS:PTL的第20章。搜索“isa swizzle”,您应该可以在网上找到更多信息。这就是 KVO 的实现方式。但是有了 NSArray……哇,那肯定很脆弱。

【讨论】:

    【解决方案2】:

    大概你有一个你想要这种行为的特定数组。无论它是什么,您都可以获取该实例的类对象,并且非常容易地进行调整:

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

    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]);
    

    前两个得到__NSArrayI,第三个得到__NSArrayM,所以可能(这是非常脆弱)你可以使用运行时函数来获取类对象的名称:

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

    【讨论】:

    • 你能做到[objc_getClass(NSStringFromClass([myArray class])) jr_swizzleMethod…吗?
    • 不,因为objc_getClass() 采用C 字符串,而不是NSString;无论如何这是不必要的,因为[myArray class] 会给你同样的结果。
    • [objc_getClass([NSStringFromClass([myArray class])) UTF8String] … 怎么样,哈哈。不迂腐..我只是尽量不要在介绍脆弱点时变得漫不经心/愚蠢。哦等等,我忘记了这些在调用时不会被触发..而是预先“注册”的。没关系..我明白为什么那行不通了..
    • 是的,这行得通,但同样,您不需要转换为字符串并返回到类对象。您已经有了类对象:[myArray class] == objc_getClass([NSStringFromClass([myArray class])) UTFString]),所以只需使用第一个选项。我包含第二个的唯一原因是,如果您(或未来的读者)想尝试混合所有他们可以找到的具体子类。
    • 也许值得注意的是,一个类可以通过覆盖-[NSObject class] 来谎报其实例的类型,而KVO 在运行时创建的类确实会覆盖-[NSObject class] 以隐藏它们的存在。但是object_getClass(不同于objc_getClass!)总是说实话。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-08-19
    • 1970-01-01
    • 1970-01-01
    • 2011-04-15
    • 2016-08-25
    • 1970-01-01
    • 2012-01-24
    相关资源
    最近更新 更多