gpt4 book ai didi

ios - 如何在运行时 objective-c 中覆盖/调配私有(private)类的方法?

转载 作者:塔克拉玛干 更新时间:2023-11-02 10:03:37 26 4
gpt4 key购买 nike

为了说明我问这个问题的原因:基本上我想更改 iOS 上谷歌地图的 myLocationButton 的位置。所以我首先像这样获取实际的按钮:

@implementation GMSMapView (UIChanges)

- (UIButton *)myLocationButton
{
UIButton *myLocationButton;
for (myLocationButton in [settingView subviews])
{
if ([myLocationButton isMemberOfClass:[UIButton class]])
break;
}
return myLocationButton;
}

然后我尝试使用 NSLayoutConstraints 更改它在屏幕中的位置(直接更改按钮的 frame 属性的值对 google maps SDK 1.8+ 没有任何作用):

UIButton *myLocationButton = [mapView_ myLocationButton];
[myLocationButton setTranslatesAutoresizingMaskIntoConstraints:NO];
[myLocationButton constraintRightEqualTo:[myLocationButton superview] constant:0];
[myLocationButton constraintTopEqualTo:[myLocationButton superview] constant:50];

其中 constraintRightEqualTo 在类别中定义为:

- (void)constraintRightEqualTo:(UIView *)toView constant:(CGFloat)constant
{
NSLayoutConstraint *cn = [NSLayoutConstraint constraintWithItem:self
attribute:NSLayoutAttributeRight

relatedBy:NSLayoutRelationEqual
toItem:toView
attribute:NSLayoutAttributeRight
multiplier:1 constant:constant];

[toView addConstraint:cn];
}

到目前为止还好吗?好的。

现在这在 iOS8 中工作得很好.. 但是当我在 iOS 7 中运行它时它崩溃并出现这个著名的错误:

-[TPMURequestStatusNotificationManager makeActionButtonResponsive]:810 - makeActionButtonResponsive 2014-10-08 16:03:20.775 SmartTaxi[13009:60b] * Assertion failure in -[GMSUISettingsView layoutSublayersOfLayer:], /SourceCache/UIKit_Sim/UIKit-2935.137/UIView.m:8794 2014-10-08 16:03:20.779 SmartTaxi[13009:60b] * Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Auto Layout still required after executing -layoutSubviews. GMSUISettingsView's implementation of -layoutSubviews needs to call super.'

问题是 GMSUISettingsView 没有调用 [super layoutSubviews]..

我以前见过这种错误。问题是它发生在公共(public)类中,例如 UITableViewCell,而不是这个私有(private)类 GMSUISettingsView隐藏在适用于 iOS 的谷歌地图 SDK 中。如果它是公开的……我可以很容易地swizzled其中的方法 layoutsubviews 使用类似于 this 的方法回答。但这不是公共(public)方法。我如何在运行时更改它的 layoutsubviews 的定义来解决这个问题? (也欢迎提出使用更简单方法实现相同目标的建议)


更新

因此,根据反馈和更多研究,我做了以下工作:

@interface AttackerClass : UIView @end
@implementation AttackerClass

- (void)_autolayout_replacementLayoutSubviews
{
struct objc_super superTarget;
superTarget.receiver = self;
superTarget.super_class = class_getSuperclass(object_getClass(self));

objc_msgSendSuper(&superTarget, @selector(layoutSubviews));
NSLog(@":: calling send super")
// PROBLEM: recursive call.. how do I call the *original*
// GMSUISettingsView implementation of layoutSubivews here?
// replacing this with _autolayout_replacementLayoutSubviews will
// throw an error b/c GMSUISettingsView doesn't have that method defined
objc_msgSend(self, @selector(layoutSubviews));
objc_msgSendSuper(&superTarget, @selector(layoutSubviews));
}
@end


Method attackerMethod = class_getInstanceMethod([AttackerClass class], @selector(_autolayout_replacementLayoutSubviews));
Method victimMethod = class_getInstanceMethod(NSClassFromString(@"GMSUISettingsView"), @selector(layoutSubviews));

method_exchangeImplementations(victimMethod, attackerMethod);

这种方法的问题是任何时候 GMSUISettingsView 调用 layoutSubviews.. 它实际上调用 _autolayout_replacementLayoutSubviews.. 然后递归调用 GMSUISettingsView layoutsubviews.. 所以我的应用程序进入无限递归循环。 this answer通过使用类别解决了这个问题..但在这种情况下我不能 b/c GMSUISettingsView 是一个私有(private)类..

问同样问题的另一种方式:我如何保留对 GMSUISettingsView 的 layoutSubviews 未更改版本的引用,并在 _autolayout_replacementLayoutSubviews 中使用它,这样我就不会摔倒进入这个递归调用问题。

想法?

最佳答案

这样做了..我不确定这是否算作一个实际答案,因为我只是通过简单地调用 [self layoutIfNeeded] 而不是 [self layoutSubviews] 来解决这个问题

void _autolayout_replacementLayoutSubviews(id self, SEL _cmd)
{
// calling super
struct objc_super superTarget;
superTarget.receiver = self;
superTarget.super_class = class_getSuperclass(object_getClass(self));
objc_msgSendSuper(&superTarget, @selector(layoutSubviews));

// to get around calling layoutSubviews and having
// a recursive call
[self layoutIfNeeded];

objc_msgSendSuper(&superTarget, @selector(layoutSubviews));
}

- (void)replaceGMSUISettingsViewImplementation
{
class_addMethod(NSClassFromString(@"GMSUISettingsView"), @selector(_autolayout_replacementLayoutSubviews), (IMP)_autolayout_replacementLayoutSubviews, "v@:");

Method existing = class_getInstanceMethod(NSClassFromString(@"GMSUISettingsView"), @selector(layoutSubviews));
Method new = class_getInstanceMethod(NSClassFromString(@"GMSUISettingsView"), @selector(_autolayout_replacementLayoutSubviews));

method_exchangeImplementations(existing, new);
}

关于ios - 如何在运行时 objective-c 中覆盖/调配私有(private)类的方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26258071/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com