@interface TestView: UIView
@end
出于某些特殊原因,我想替换TestView
(继承自UIView)layoutSubviews
方法。但是,不想影响原始UIView
的生命周期,只是希望它在课程TestView
内工作。
我只是这样实现它:
@implementation TestView
+(void)load {
Method a = class_getInstanceMethod(TestView.class, @selector(layoutSubviews));
Method b = class_getInstanceMethod(TestView.class, @selector(customLayout));
method_exchangeImplementations(a, b);
}
但出了点问题。 UIWindow似乎也调用了customLayout
。我猜它已经被污染了。我怎么能避免这种情况?
IDE:xCode 8.2.1
操作系统:10.12.4
这是测试代码:
#import "ViewController.h"
#import <objc/runtime.h>
@interface TestView: UIView
@end
@implementation TestView
+(void)load {
Method a = class_getInstanceMethod(TestView.class, @selector(layoutSubviews));
Method b = class_getInstanceMethod(TestView.class, @selector(customLayout));
method_exchangeImplementations(a, b);
}
-(void) customLayout {
[self customLayout];
}
-(instancetype) initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
self.backgroundColor = UIColor.grayColor;
return self;
}
@end
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
TestView* view = [[TestView alloc] initWithFrame: CGRectMake(0, 0, 300, 300)];
[self.view addSubview:view];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
这是运行时日志:
2017-06-07 14:51:38.240 swizzleTest[37908:28470016] -[UIWindow customLayout]: unrecognized selector sent to instance 0x7fc3d7009d00
2017-06-07 14:51:38.242 swizzleTest[37908:28470016] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UIWindow customLayout]: unrecognized selector sent to instance 0x7fc3d7009d00'
*** First throw call stack:
(
0 CoreFoundation 0x0000000111450d4b __exceptionPreprocess + 171
1 libobjc.A.dylib 0x000000010f00021e objc_exception_throw + 48
2 CoreFoundation 0x00000001114c0f04 -[NSObject(NSObject) doesNotRecognizeSelector:] + 132
3 CoreFoundation 0x00000001113d6005 ___forwarding___ + 1013
4 CoreFoundation 0x00000001113d5b88 _CF_forwarding_prep_0 + 120
5 swizzleTest 0x000000010ea2b3fb -[TestView customLayout] + 43
6 UIKit 0x000000010f594ab8 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1237
7 QuartzCore 0x0000000114296bf8 -[CALayer layoutSublayers] + 146
8 QuartzCore 0x000000011428a440 _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 366
9 QuartzCore 0x000000011428a2be _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 24
10 QuartzCore 0x0000000114218318 _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 280
11 QuartzCore 0x00000001142453ff _ZN2CA11Transaction6commitEv + 475
12 QuartzCore 0x0000000114245d6f _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv + 113
13 CoreFoundation 0x00000001113f5267 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 23
14 CoreFoundation 0x00000001113f51d7 __CFRunLoopDoObservers + 391
15 CoreFoundation 0x00000001113d98a6 CFRunLoopRunSpecific + 454
16 UIKit 0x000000010f4c9aea -[UIApplication _run] + 434
17 UIKit 0x000000010f4cfc68 UIApplicationMain + 159
18 swizzleTest 0x000000010ea2b9af main + 111
19 libdyld.dylib 0x000000011240068d start + 1
20 ??? 0x0000000000000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
答案 0 :(得分:0)
我在layoutSubviews
课程中明确覆盖TestView
之后就可以了。
否则,class_getInstanceMethod(TestView.class, @selector(layoutSubviews));
只能获得-[UIView layoutSubviews]
而不是-[TestView layoutSubviews]
。
修改后的代码如下:
@implementation TestView
+(void)load {
Method a = class_getInstanceMethod(TestView.class, @selector(layoutSubviews));
Method b = class_getInstanceMethod(TestView.class, @selector(customLayout));
method_exchangeImplementations(a, b);
}
-(void) customLayout {
[self customLayout];
}
-(void) layoutSubviews {
[super layoutSubviews];
}
-(instancetype) initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
self.backgroundColor = UIColor.grayColor;
return self;
}
@end
答案 1 :(得分:0)
在文档中发现的常见模式是:
2a上。如果方法存在,则调动
2B。如果子类中不存在该方法(从超类继承),则将该方法添加为新方法。