People Lack Willpower,Rather Than Strength!
1.MD5加密
- 背景: 传统的GET/POST请求提交用户的隐私数据,不能解决安全问题. 利用软件(例如:Charles)设置代理服务器,拦截查看手机数据. 因此,
提交用户的隐私数据时,一定不要明文提交,要加密处理后再提交
. - 常见的加密算法:
MD5 \ SHA \ DES \ 3DES \ RC2和RC4 \ RSA \ IDEA \ DSA \ AES
. - 加密算法的选择: 一般公司都会有一套自己的加密方案.
- 什么是MD5?
- Message Digest Algorithm 5,译为“消息摘要算法第5版”;
- 对输入信息生成唯一的128位散列值(32个字符).
- MD5特点:
- 输入两个不同的明文不会得到相同的输出值
- 根据输出值,不能得到原始的明文,即其过程
不可逆
.
-
MD5应用:
- 由于MD5加密算法具有较好的安全性,而且免费,因此该加密算法被广泛使用.
- 主要应用在:数字签名,文件完整性验证和口令加密方面.
-
在注册/登录上的应用.
使用前,需要先导入框架
-
为什么要导入框架?
- 由于Apple 提供给我们的是C 语言API, 并不好用.
-
主要内容
- 简单加密
- 加盐
- 双重加密
- 注意点: MD5加密是不可逆的.
- 代码示例:
// 先导入框架
#import "NSString+Hash.h"
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// extern unsigned char *CC_MD5(const void *data, CC_LONG len, unsigned char *md)
// c语言的,不好用,我们使用框架
// 1.常规输出
NSString *string = @"testString";
NSLog(@"%@",string);
/*2015-09-10 16:15:53.857 01-MD5加密[2435:144621] testString
*/
// 2.简单md5加密输出
NSLog(@"%@",string.md5String);
/*2015-09-10 16:16:41.657 01-MD5加密[2463:145280] 536788f4dbdffeecfbb8f350a941eea3
*/
// 3.加盐输出salt,在原pwd基础上自己额外添加字符串
string = @"testString+PJ";
NSLog(@"%@",string.md5String);
/*2015-09-10 16:21:04.357 01-MD5加密[2509:147089] 799ebd673b9ac12ff429aa66ef102eff
*/
// 4.双重加密
NSLog(@"%@",string.md5String.md5String);
/* 2015-09-10 16:22:20.985 01-MD5加密[2536:147930] 504650a386e5a98086271fe8e8a9f6c4
*/
}
- 网上随意一个网站, 我们可以看到MD5破解,其实质无外乎,就是收集足够多的md5值
-
加盐后,亦然可以破解
2.HTTPs
背景: 从安全考虑, 在iOS9 beta1中,苹果将原http协议改成了https协议, 所以访问https网站时, 我们需要对网站返回的证书做处理.
-
注意点: TLS1.2 SSL加密请求数据
- 访问加密个人服务器(即https://www.xxx.com网站), 可能访问不到,因为个人服务器使用证书版本低,我们在xcode中即使使用https方式访问也没用.需要做如下配置:
-
模拟器连不上自己服务器,因为个人服务器SSL加密方法还是TLS1.1版本.需要在Xcode的info.plist中添加对应的键值对.
-
原理
自己安装证书
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// 创建request
// NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.baidu.com"]];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.apple.com/"]];
// 1.创建session会话--监听请求过程
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
// 2.根据session创建请求任务
NSURLSessionDataTask *task = [session dataTaskWithRequest: request];
// 3.Task resume
[task resume];
}
#pragma mark - NSURLSessionDataDelegate
// 服务器响应时调用
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
// 表示接受服务器返回数据
completionHandler(NSURLSessionResponseAllow);
NSLog(@"didReceiveResponse");
}
// 服务器返回数据时调用
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
NSLog(@"didReceiveData");
}
// 完成数据下载时调用
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
NSLog(@"didCompleteWithError");
}
/*
对于常规网站,不需要我们按照证书,直接反馈请求:例如www.baidu.com
2015-09-10 16:43:12.992 02-HTTPs[2707:158966] didReceiveResponse
2015-09-10 16:43:12.992 02-HTTPs[2707:158966] didReceiveData
2015-09-10 16:43:12.992 02-HTTPs[2707:158966] didReceiveData
2015-09-10 16:43:12.992 02-HTTPs[2707:158966] didCompleteWithError
对于需要按照证书的网站,我们发送请求时,如果没有安装证书,则如下结果:12306
2015-09-10 16:45:20.908 02-HTTPs[2740:160452] NSURLConnection/CFURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9813)
2015-09-10 16:45:20.909 02-HTTPs[2740:160342] didCompleteWithError
*/
/*=========对于HTTPs网站,当需要我们安装证书时,我们要如下操作=========*/
// 只要访问的是HTTPS的路径就会调用
// 该方法的作用就是处理服务器返回的证书, 需要在该方法中告诉系统是否需要安装服务器返回的证书
// NSURLAuthenticationChallenge : 授权问询
// + 受保护空间
// + 服务器返回的证书类型
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler
{
// 1.判断服务器返回的受保护空间里的证书是否是服务器信任的;(服务器自己返回的证书,自己怎么会不信任那???😖)
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
// 2.根据服务器返回的受保护空间里的证书类型创建一个证书
NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
// 3.安装证书
completionHandler( NSURLSessionAuthChallengeUseCredential,credential);
/*
(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler
第一个参数:表示如何处理证书;
第二个参数:表示要处理哪个证书;
*/
}
}
- 使用AF提供方法
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler
{
// AFN框架中有现成...改改就好了
// 在框架中搜NSURLSessionAuthChallengeDisposition可以找到类似如下代码:
// 稍作改动就和上面自己写的一样了:
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
__block NSURLCredential *credential = nil;
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
{
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
if (credential) {
disposition = NSURLSessionAuthChallengeUseCredential;
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
} else
{
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
if (completionHandler) {
completionHandler(disposition, credential);
}
}
3.UIWebView
-
什么是UIWebView
- UIWebView是iOS内置的浏览器控件
-
系统自带的Safari浏览器就是通过UIWebView实现的
- 注意:webview 会内存泄露,这是苹果的问题,Safari就是使用webview做的!
-
UIWebView不但能加载远程的网页资源,还能加载绝大部分的常见文件
- html\htm
- pdf、doc、ppt、txt
- mp4
- … …
UIWebView常用的加载资源的方法
// 开发趋势,UI+ 网页,因为网页是跨平台的,一次开发一劳永逸!
- (void)viewDidLoad {
[super viewDidLoad];
// 2.监控请求过程,需要给webView添加代理
self.webView.delegate = self;
NSURL *url = [NSURL URLWithString:@"http://www.baidu.com"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//1. 使用webView发送请求,向服务器申请数据
// 注意:webview 会内存泄露,这是苹果的问题,Safari就是使用webview做的!
[self.webView loadRequest:request];
}
- 监听OC内部请求和webView内部请求: webView的代理方法
#pragma mark - UIWebViewDelegate
// 重点注意这个方法❤️
// 每次发送请求前,总会来这个方法:
// 如果return YES,表示允许请求;
// 如果return NO,表示不允许请求;
// 专门用来拦截请求的❤️
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
NSLog(@"%@",request.URL);
// 我们拦截hao123请求
NSString *urlStr = request.URL.absoluteString;
if ([urlStr containsString:@"hao123"]) {
return NO;
}
return YES;
/*结果可见:
请求百度首页时,发送了两次请求:
request1.url --> http://www.baidu.com/
request2.url --> bout:blank
2015-09-10 22:30:30.804 03-UIWebView[6747:323693] http://www.baidu.com/
2015-09-10 22:30:30.805 03-UIWebView[6747:323693] -[ViewController webViewDidStartLoad:]
2015-09-10 22:30:30.966 03-UIWebView[6747:323693] about:blank
2015-09-10 22:30:30.966 03-UIWebView[6747:323693] -[ViewController webViewDidStartLoad:]
2015-09-10 22:30:30.967 03-UIWebView[6747:323693] -[ViewController webViewDidFinishLoad:]
2015-09-10 22:30:31.131 03-UIWebView[6747:323693] -[ViewController webViewDidFinishLoad:]
*/
}
// 开始加载网页
// 注意:这里监控的不仅仅是我们指定的url请求,还有网页内部的其他请求
- (void)webViewDidStartLoad:(UIWebView *)webView
{
// webview提供了属性,可以及时监控是否可以前进/后退
self.goforwardBtn.enabled = self.webView.canGoForward;
self.backwordBtn.enabled = self.webView.canGoBack;
NSLog(@"%s",__func__);
}
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
NSLog(@"%s",__func__);
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
NSLog(@"%s",__func__);
}
/*例如这里:已进入百度首页,打印两次请求,说明除了我们发起的一次请求外,百度首页也做了请求.
2015-09-10 22:17:26.020 03-UIWebView[6651:317434] -[ViewController webViewDidStartLoad:]
2015-09-10 22:17:26.202 03-UIWebView[6651:317434] -[ViewController webViewDidStartLoad:]
2015-09-10 22:17:26.203 03-UIWebView[6651:317434] -[ViewController webViewDidFinishLoad:]
2015-09-10 22:17:26.384 03-UIWebView[6651:317434] -[ViewController webViewDidFinishLoad:]
*/
- webView已经提供了前进/回退的方法,直接使用即可.
#pragma mark - 内部控制方法
- (IBAction)goforward:(id)sender {
[self.webView goForward];
}
- (IBAction)backword:(id)sender {
[self.webView goBack];
}
- (IBAction)refresh:(id)sender {
[self.webView reload];
}
- webView常见功能和常见属性
-
展示图片
- 自适应
-
展示视频
- 自适应
-
展示HTML
- 自适应屏幕
- 特殊字符自动识别
实例代码
-
/* 3.展示本地html */
- (void)webViewDisplayMetaHTML
{
// 2.监控请求过程,需要给webView添加代理
self.webView.delegate = self;
// 1.1.url--->加载本地HTML请求
NSURL *url = [[NSBundle mainBundle] URLForResource:@"test.html" withExtension:nil];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// 1.2 设置webView属性
// 调整webView的contentSize,以适应屏幕
self.webView.scalesPageToFit = YES;
// 用于识别网页中特定的信息,例如网址...(号码会被默认是电话号码)
self.webView.dataDetectorTypes = UIDataDetectorTypeAll;
//1. 使用webView发送请求,向服务器申请数据
[self.webView loadRequest:request];
}
/* 2.视频展示*/
- (void)webViewDisplayVideo
{
// 2.监控请求过程,需要给webView添加代理
self.webView.delegate = self;
// 1.1.url--->加载本地HTML请求
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_02.mp4"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// 1.2 设置webView属性
// 调整webView的contentSize,以适应屏幕
self.webView.scalesPageToFit = YES;
//1. 使用webView发送请求,向服务器申请数据
[self.webView loadRequest:request];
}
/* 1.展示图片*/
- (void)webViewDisplayPicture
{
// 2.监控请求过程,需要给webView添加代理
self.webView.delegate = self;
// 1.1.url--->加载图片请求
NSURL *url = [NSURL fileURLWithPath:@"/Users/PlwNs/Desktop/Snip20150910_6.png"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// 1.2 设置webView属性
// 调整webView的contentSize,以适应屏幕
self.webView.scalesPageToFit = YES;
//1. 使用webView发送请求,向服务器申请数据
[self.webView loadRequest:request];
}
4.OC与JS互调
- 关键: 在OC中,通过拿到webView, 给其设置代理,在代理方法中拦截/执行webView的一些事件.
-
代理方法中只能用来拦截JS/OC中的加载/跳转网页
请求
(load请求).-
从代理方法的名字也可以看出:
....shouldStartLoadWithRequest: (void)webViewDidStartLoad:.. webViewDidFinishLoad:... didFailLoadWithError:... //都是和load相关,意味着只有OC/JS中发起加载/跳转网页时,才能来到代理方法.
-
同时可以
执行JS代码
.但是
不可以
监控HTML中的标签事件.调用OC中无参数的函数
-
// 这里OC调用JS函数!!!❤️❤️f
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
NSLog(@"%s",__func__);
// webView自己监听标签事件依然OK. 当然,我们还可以在OC中调用/拦截一些JS,在OC中执行JS
// 在该方法中,我们可以调用网页中的JS函数❤️
// 弹窗-->❤️分号可有可无❤️
[webView stringByEvaluatingJavaScriptFromString:@"ocShow();"];
// 打印数字
NSString *str = [webView stringByEvaluatingJavaScriptFromString:@"sum()"];
NSLog(@"%@",str);
}
// 但是,注意: ⚠️
网页上的button.onclick事件,OC代理方法中没办法代为执行.
// ======================网页代码==============
<head>
<metacharset="UTF-8">
<title>毛线网页</title>
<!--在script中设置调用函数,注意这里只是监控button点击事件,和请求不一样!!❤️-->
<script>
function show()
{
<!--alert(document.title);-->
alert(sum());
}
function sum()
{
return 2+3;
}
function ocShow()
{
alert(document.title);
}
</script>
</head>
<!-- 网页具体内容-->
<body>
<!-- 在网页上添加一个按钮-->
<button style="background:green; height:200px; width:200px" onclick="show()">按钮</button>
</body>
- JS调用OC方法
- 单个参数
// HTML中======================
<head>
<script>
function repost()
{
//在html中发送的请求,可以是下载资源,访问网站,调用某个方法...
// 前缀一定不能大写!系统会默认这里是网址,在请求中将大写转换为小写!❤️
// 这里,注意调用OC的方法名,因为:在HTML中有特殊含义,我们用"_"取代,多个参数用&隔开
location.href = "request://callWithNumber?1204774094";
}
</script>
</head>
<body>
<button style="background:green; height:200px; width:200px" onclick="repost()">按钮</button>
</body>
// OC中======================
// 被调用的某个OC方法
- (void)callWithNumber:(NSString *)number
{
NSLog(@"%@",number);
}
// 在OC中该方法拦截JS请求!❤️
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
// 1⃣️.单个参数
// 1.1 判断请求中是否有我的标记
NSString *schem = @"request://";
NSString *urlStr = request.URL.absoluteString;
if ([urlStr containsString:schem]) {
// 1.2 取出请求中标记后部分的字符串
NSString *methodStr = [urlStr substringFromIndex:schem.length];
// 1.3 取出方法名字符串和参数
// 注意: 如果指定的用于切割的字符串不存在, 那么就会返回整个字符串❤️
NSArray *arr = [methodStr componentsSeparatedByString:@"?"];
NSString *methodName = [arr firstObject];
methodName = [methodName stringByReplacingOccurrencesOfString:@"_" withString:@":"];
// 1.4 生成方法名
SEL aSelector = NSSelectorFromString(methodName);
// ❤️考虑到我们在JS中传入的方法可能带参数也可能不带,所以这里需要做个判断
NSString *para = nil;
if (arr.count == 2) {
para = [arr lastObject];
}
#pragma clang diagnostic push
// 下面这一行代码是用于指定需要忽略的警告信息
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
// 1.5 执行带参数方法
[self performSelector:aSelector withObject:para];
// 忽略警告信息的作用范围结束
#pragma clang diagnostic pop
// 1.6 不要忘记,这里拦截完JS请求后,不要让其继续执行,因为他根本就执行不了❤️
return NO;
// 2015-09-11 22:22:36.552 03-UIWebView[1485:41469] -[ViewController webViewDidStartLoad:]
// 2015-09-11 22:22:36.589 03-UIWebView[1485:41469] -[ViewController webViewDidFinishLoad:]
// 2015-09-11 22:22:38.265 03-UIWebView[1485:41469] 1204774094
}
}
- 多个参数 --核心代码
// 同样道理
// HTML===============================
.......
function repost()
{
location.href = "request://callWithNumber_content_?1204774094&myQQ";
}
// OC==============================
// 被调用方法
- (void)callWithNumber:(NSString *)number content:(NSString *)content
{
NSLog(@"number=%@,content=%@",number,content);
}
// 拦截
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
// 2.多个参数
// 2.1 判断请求中是否有个人标记
NSString *schem = @"request://";
NSString *urlStr = request.URL.absoluteString;
if ([urlStr hasPrefix:schem]) {
// 2.2 取出方法和参数字符串
NSString *subPath = [urlStr substringFromIndex:schem.length];
// 2.2 分割方法名和参数
NSArray *subPaths = [subPath componentsSeparatedByString:@"?"];
// 2.3 取出方法名,转换为方法
NSString *methodName = [subPaths firstObject];
methodName = [methodName stringByReplacingOccurrencesOfString:@"_" withString:@":"];
SEL aSelector = NSSelectorFromString(methodName);
// 2.4 取出参数
if (subPaths.count == 2) {
NSString *para = [subPaths lastObject];
NSArray *paras = [para componentsSeparatedByString:@"&"];
NSString *para1 = [paras firstObject];
NSString *para2 = [paras lastObject];
// 2.5执行方法----->可见系统提供的performSelector...方法最多有两个参数!!!需要我们自己写perform...方法
[self performSelector:aSelector withObject:para1 withObject:para2];
return NO;
}
}
}
- 问题:
- 从上面两种JS调用OC方法,我们发现
[self performSelector:aSelector withObject:para1 withObject:para2];
方法, 最多只能同时执行带两个参数的方法. 如果N多参数,怎么办? 下面我们引出NSInvocation 对象, 来解决该问题.
- 从上面两种JS调用OC方法,我们发现
5.NSInvocation
-
概念:
- NSInvocation 专门用来包装方法和对应的对象,他可以用来存储方法的名称和对应的对象,参数;❤️
- 那么,NSInvocation对象如何包装 方法那??
// 需要被包装的方法
- (void)sendMessageWithNumber:(NSString *)number andContent:(NSString *)content status:(NSString *)status
{
NSLog(@"发信息给%@, 内容是%@, 发送的状态是%@", number, content, status);
}
// 包装
- (void)invocationUsage
{
// 0.创建标签
// Signature签名: 在创建NSInvocation的时候, 必须传递一个签名对象
// 签名对象的作用 : 用于获取参数的个数和方法的返回值length/type
// 注意⚠️这里需要包装谁的方法,就让谁创建标签❤️
// 注意不要用方法methodSignatureForSelector:创建标签🈲
NSMethodSignature *signature = [[self class] instanceMethodSignatureForSelector:@selector(sendMessageWithNumber:andContent:status:)];
// 1.创建NSInvocation对象
// NSInvocation 专门用来包装方法和对应的对象,他可以用来存储方法的名称和对应的对象,参数;❤️
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
// 2.1 设置invocation中保存方法所属对象
invocation.target = self;
// 2.3 设置保存的方法
// 注意:这里的方法名必须和标签中保存的方法名一致!❤️
invocation.selector = @selector(sendMessageWithNumber:andContent:status:);
// 2.4 设置参数
// 第一个参数: 需要给指定方法传递的值
// + 第一个参数需要接收一个指针, 也就是传递值的时候需要传递地址
// 第二个参数: 需要给指定方法的第几个参数传值
// 注意:方法中的0/1位置分别被隐式变量self和_cmd占据
NSString *number = @"10086";
[invocation setArgument:&number atIndex:2];
NSString *content = @"love";
[invocation setArgument:&content atIndex:3];
NSString *status = @"success";
[invocation setArgument:&status atIndex:4];
// 3.执行
// 只要调用invocation的invoke方法, 就代表需要执行 NSInvocation对象中指定对象的指定方法, 并且传递指定的参数
[invocation invoke];
}
以上,可见执行一个方法甚是麻烦,因此我们需要对整个NSInvocation包装/执行过程封装,以期达到复用效果❤️
-
NSInvocation 的封装
- 如何封装?
- 考虑到任何一个对象都应该具有调用多参数方法的能力, 所以我们应该将NSInvocation对象封装到NSObject分类的一个方法中 : performSelector:(SEL)aSelector withArguments:(NSArray *)paras ;
// .h文件
#import <Foundation/Foundation.h>
@interface NSObject (performSelector)
- (id)performSelector:(SEL)aSelector withObjects:(NSArray *)objects;
@end
// .m 文件
#import "NSObject+performSelector.h"
@implementation NSObject (performSelector)
- (id)performSelector:(SEL)aSelector withObjects:(NSArray *)objects
{
// 0.创建标签
NSMethodSignature *signature = [[self class] instanceMethodSignatureForSelector:aSelector];
// 判断传入方法名是否存在
// 这里标签根据传入的方法,存储方法中的参数个数和返回值长度/类型,所以如果方法不存在,就得不到标签
if (signature == nil) {
// 传入的方法不存在,抛异常
NSString *info = [NSString stringWithFormat:@"-[%@ %@]:unrecognized selector sent to instance",[self class],NSStringFromSelector(aSelector)];
NSException *exception = [NSException exceptionWithName:@"方法不存在" reason:info userInfo:nil];
@throw exception;
}
// 1.创建NSInvocation对象
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
// 2.设置方法所属对象/参数/方法名
invocation.target = self;
invocation.selector = aSelector;
/*
当前如果直接遍历参数数组来设置参数, 会存在问题
如果参数数组元素多余参数个数, 那么就会报错
*/
NSUInteger argCount = signature.numberOfArguments - 2; // 这里记得减去self/_cmd两个参数
//如果参数的个数大于了参数值的个数, 那么数组会越界
NSUInteger paraCount = objects.count;
/*参数和参数值, 谁少就遍历谁*/
NSUInteger count = MIN(argCount, paraCount);
for (int i=0; i < count; i++) {
NSObject *para = objects[i];
if ([para isKindOfClass:[NSNull class]]) {
para = nil;
}
[invocation setArgument:¶ atIndex:2+i];
}
// 3.执行invocation
[invocation invoke];
// 4.获得返回值
id res = nil;
// 为了避免方法没有返回值情况,这里需要判断当前方法是否有返回值
if (signature.methodReturnLength) {
[invocation getReturnValue:&res];
}
return res;
}
@end
6.捕获异常
- 对于异常,我们除了可以@throw 之外, 我们还可以使用NSSetUncatchExeptionHandler捕获异常.
- 注意: webView不行,因为默认情况下,webView是不接受点击事件的.也就是说,我们不能往webView上加控件,然后点击这个控件. 但是webView上的标签控件,还是可以点击的!❤️.
- 在AppDelegate.m中实现,异常捕获代码
// 异常提示
void handle()
{
NSLog(@"我捕获异常了");
[[UIApplication sharedApplication].delegate performSelector:@selector(showError)];
}
- (void)showError
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"对不起" message:@"我自身问题" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"确定", nil];
[alert show];
// 必须要开启一个循环,不然程序崩掉时,主循环是死掉了,那么这个弹窗就做不了了!❤️
[[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
// 1表示非正常退出
exit(1);
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSSetUncaughtExceptionHandler(handle);
return YES;
}