@yang_yong_xin wrote:
前言
原文链接:http://www.lanvsblue.top/2016/10/08/how-to-use-private-api-in-ios/
在iOS的API中,几乎每一个类都会有一部分未公开的方法,而这些方法在一般情况下是不能被开发者调用的,可以说最广为人知的就是:
[UIView recursiveDescription]
了。这是一个UIView的私有方法,所以在API Reference中是找不到的。它是一个神奇的方法,可以打印出所有view层次直接的关系和view的description。如果正确调用会可以输出如下log信息:可以试着在代码中调用这个方法:
- (void)viewDidLoad { [super viewDidLoad]; //...... //...... [self.view recursiveDescription]; //...... //...... }
编译是会报错的:
no visible @interface for 'UIView' declares the selector 'recursiveDescription'
。那么应该如何调用像这样私有的API呢?我们先看一个简单的例子。PS:所有本文有关的代码可以在这边下载
PS:注意!!!调用了私有API的App是不能上架AppStore的,所以请斟酌以后再调用。
Objective-C调用私有方法
要想调用私有API首先我们得学会如何在Objective-C中调用私有方法。我们来看一个例子:
这个例子包含一个类和一个main方法,文件结构如下:
Image may be NSFW.
Clik here to view.ExampleClass.h文件:
/ * 这是ExampleClass类的头文件,ExampleClass类包含: * 一个公有的方法,方法名为 -(void)publicMethod; * 一个私有的方法,方法名为 -(void)privateMethod; 在.m文件中实现 * / #import <Foundation/Foundation.h> @interface ExampleClass : NSObject -(void)publicMethod; @end
ExampleClass.m文件:
/ * 这是ExampleClass类的实现文件,ExampleClass类包含: * 一个公有的方法,方法名为 -(void)publicMethod; * 一个私有的方法,方法名为 -(void)privateMethod; 在.m文件中实现 * / #import "ExampleClass.h" @implementation ExampleClass { } - (void)publicMethod { NSLog(@"This is PUBLIC method"); } -(void)privateMethod{ NSLog(@"This is PRIVATE method"); } @end
一个名为ExampleClass的类中,有一个公有方法和一个私有方法。公有方法可以直接用
[Class Method]
这样的形式调用,而私有方法应该怎么样调用呢?有下面两个方法:方法一:巧用Category欺骗编译器
如果直接使用
[Class Method]
这样的方法调用ExampleClass类的privateMethod
会报找不到此方法
的错误,刚刚已经尝试过UIView类的recursiveDescription
方法了。明明是存在这个方法的,但是编译不通过。那么我们要做的就是欺骗编译器,是存在这个方法的。我们可以构造一个匿名的Category来实现,还是刚刚的
ExampleClass
的例子,看看怎么样用Category欺骗编译器:main.h文件:
#import <Foundation/Foundation.h> #import "ExampleClass.h" @interface ExampleClass() -(void)privateMethod; @end int main(int argc, const char * argv[]) { @autoreleasepool { ExampleClass *example = [[ExampleClass alloc] init]; [example publicMethod]; [example privateMethod]; } return 0; }
这时候就可以编译了,查看结果:
Image may be NSFW.
Clik here to view.惊人的的发现竟然调用成功了
代码参见文件中的Example1。
方法二:使用OC的runtime特性:[NSObject performSelector:]
首先介绍两个NSObject的方法:
* [NSObject respondsToSelector:]
* [NSObject performSelector:]第一个方法返回一个布尔值用来查看receiver或receiver的父类是否实现了这个selector;第二个方法发送一个消息(oc里面讲消息,其他语言为方法)给receiver,这个方法返回的就是发送的消息所返回的值。
为了证明
[ExampleClass privateMethod]
这个方法确实是存在的,我首先来使用第一个方法:[NSObject respondsToSelector:]
:main.h文件:
#import <Foundation/Foundation.h> #import "ExampleClass.h" int main(int argc, const char * argv[]) { @autoreleasepool { ExampleClass *example = [[ExampleClass alloc] init]; [example publicMethod]; NSLog(@"%d",[example respondsToSelector:@selector(privateMethod)]); } return 0; }
运行以后发现程序输出了1,证明
ExampleClass
确实是包含privateMethod
方法的,这时候我们改一下main.h,尝试使用Objective-C的runtime特性,使用上文提到的[NSObject performSelector:]方法,调用privateMethod
:main.h:
#import <Foundation/Foundation.h> #import "ExampleClass.h" int main(int argc, const char * argv[]) { @autoreleasepool { ExampleClass *example = [[ExampleClass alloc] init]; [example publicMethod]; if([example respondsToSelector:@selector(privateMethod)]){ [example performSelector:@selector(privateMethod)]; } } return 0; }
这样也能看见相同的效果。
代码参见文件中的Example2。
调用私有API
如何调用私有API就可以举一反三出来了,这里就使用方法一举个例子,这个例子在iOS中运行:
#import "ViewController.h" @interface UIView() -(id)recursiveDescription; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; UIView *view1 = [[UIView alloc] initWithFrame:CGRectMake(20,50,200,300)]; view1.backgroundColor = [UIColor greenColor]; [self.view addSubview:view1]; UIView *view2 = [[UIView alloc] initWithFrame:CGRectMake(10,20,150,250)]; view2.backgroundColor = [UIColor redColor]; [view1 addSubview:view2]; UIView *view3 = [[UIView alloc] initWithFrame:CGRectMake(10,20,100,200)]; view3.backgroundColor = [UIColor blueColor]; [view2 addSubview:view3]; UIView *view4 = [[UIView alloc] initWithFrame:CGRectMake(170,20,20,100)]; view4.backgroundColor = [UIColor grayColor]; [view1 addSubview:view4]; UIView *view5 = [[UIView alloc] initWithFrame:CGRectMake(70,50,200,50)]; view5.backgroundColor = [UIColor brownColor]; [view2 addSubview:view5]; NSLog(@"%@",view5); //测试私有方法 [self runPrivateMethod]; } -(void)runPrivateMethod{ NSLog(@"-------------------------ON TEST-------------------------"); NSLog(@"%d",[self.view respondsToSelector:@selector(recursiveDescription)]); NSLog(@"%@",[self.view recursiveDescription]); NSLog(@"-------------------------END TEST-------------------------"); } @end
是不是打印出文首那要的log了呢?
Posts: 2
Participants: 2