• 主页
  • 系列总集
  • OpenCV
  • CMake
  • iOS
  • Java
  • 前端
所有文章 关于我

  • 主页
  • 系列总集
  • OpenCV
  • CMake
  • iOS
  • Java
  • 前端

判断当前窗口是否显示

2015-11-07

判断当前窗口是否显示

假如一个UIView对象当前正在显示,那么它的window属性肯定为非空值。虽然官方文档未说明UIView未显示时window属性的取值,但是经过简单的测试,大部分情况下UIView未显示时,window的值为空,因此依据此判断当前UIViewController是否正在显示。但是访问UIViewController的view属性时,可能会引起view加载(假如此时还未加载),这是不必要的,而且还可能引起无法预期的问题。因此在访问view属性之前,最好先检查isViewLoaded属性来避免上述问题.

示例代码如下,在UIViewController的扩展中声明了一个isVisible方法,方便以后调用

注意:UIView的官方文档中仅注明当view还未添加到window时,window属性为空;但是并未提及当window为nil时,当前view未显示。通过简单的Demo检测上文的方法可满足大部分需求,因此使用时建议进行简单测试!

1
2
3
if(self.view.isViewLoaded && self.view.window) {
//code
}
  • iOS
  • ViewController
  • UI
  • Tips

展开全文 >>

HTTPS总结文稿

2015-11-01

HTTPS定义

  1. 全称:Hypertext Transfer Protocol over Secure Socket Layer
  2. HTTP的安全版,即HTTP之下TCP之上加入SSL层,与HTTP不同的是__不同的默认端口(http通常是80,https通常是443)__及一个加密/身份验证层
  3. HTTPS协议需要到CA申请证书,免费证书很少,需要交费
  4. HTTP的连接是无状态的;HTTPS协议包含一种类似握手协议的身份认证过程(认证是单向的/双向的)

有两种基本的加解密算法类型

  1. 对称加密:密钥只有一个,加密解密为同一个密码,且加解密速度快,典型的对称加密算法有DES、AES等;

    • 类似传统的账号密码概念,内容是账号密码是密钥
  1. 非对称加密:密钥成对出现,且根据公钥无法推知私钥,根据私钥也无法推知公钥;加密解密使用不同密钥,公钥加密需要私钥解密,私钥加密需要公钥解密,如RSA。

    • 密文 S = A×B,S是加密后的内容,A与B分别是两个大素数(仅可被自己与1整除,如13),这样使用A加密后的结果S只能被B除尽(解开),反之同理。

有两种认证方法(单向/双向)

  1. 单向认证:一般意义的https,如浏览器访问https网站

    • 主要保证服务器是它自己所声明的正确地址
    • 客户端和服务端的内容都是通过对称密钥加密的
    • 对称密钥是由客户端产生,通过服务器派发出的公钥加密后发送给服务器
  2. 双向认证:客户端也具有一个自己的证书,如银行的U盾

    • 客户端本身具备证书,保密的是传输过程本身而不一定能保证服务器本身是安全的
    • 如常见的钓鱼网站或DNS劫持,用户访问的域名并没有指向正确的地址,所以客户端证书本身并无法确保服务器是真实的
    • 双向认证是指客户端不仅要向CA验证服务器本身的真实性,服务器在响应数据时也要验证接收方的真实性
    • 详细链接 Https detail

证书格式以及转化

证书的标准

PKCS 全称是 Public-Key Cryptography Standards
是由 RSA 实验室与其它安全系统开发商为促进公钥密码的发展而制订的一系列标准
PKCS 目前共发布过 15 个标准。

常见的标准:

  1. PKCS#7 Cryptographic Message Syntax Standard
  2. PKCS#10 Certification Request Standard
  3. PKCS#12 Personal Information Exchange Syntax Standard
  4. X.509是常见通用的证书格式。所有的证书都符合为Public Key Infrastructure (PKI) 制定的 ITU-T X509 国际标准。

PKCS#7 常用的后缀是: .P7B .P7C .SPC

PKCS#12 常用的后缀有: .P12 .PFX

X.509 DER 编码(ASCII)的后缀是: .DER .CER .CRT

X.509 PAM 编码(Base64)的后缀是: .PEM .CER .CRT

编码与扩展名

X.509证书是一个数字文档,具有扩展名和编码两个属性
  • .DER = DER是一种编码,扩展名有三种。以二进制存放,常见于Windows系统
  • .PEM = PEM是一种编码,扩展名也是三种,文件开始由一行”—– BEGIN …“开始。
  • .CRT = CRT常见于Linux/Unix证书。证书可以是DER编码,也可以是PEM编码。
  • .CER = CRT证书的微软型式。也可以用两种编码。
  • .KEY = 扩展名KEY用于PCSK#8的公钥和私钥。可以是两种编码。
  • 扩展名CER和CRT几乎是同义词,CRT文件和CER文件只有在使用相同编码的时候才可以通过改后缀名代替。

证书之间的转换

只有编码相同可以通过改后缀名代替,正常情况下应该通过专业的编码工具来互相转换,如OpenSSL

1
"openssl x509 -in 你的证书.crt -out 你的证书.cer -outform der"

CA签名证书与自签名证书

CA签名证书如何验证

HTTPS连接建立过程大致是:

  1. 客户端和服务端建立一个连接
  2. 服务端返回一个证书
  3. 客户端里存有各个受信任的证书机构根证书**(IOS8 预装的根证书Apple Support**
    )
  4. 用这些根证书对服务端返回的证书进行验证,经验证如果证书是可信任的,就生成一个pre-master secret
  5. 用这个证书的公钥加密后发送给服务端,服务端用私钥解密后得到pre-master secret
  6. 再根据某种算法生成master secret
  7. 客户端也同样根据这种算法从pre-master secret生成master secret
  8. 随后双方的通信都用这个master secret对传输数据进行加密解密。

自签名的证书通过SSL Pinning使用

  1. 为什么要用SSL Pinning?

如果服务端的证书是从受信任的的CA机构颁发的,验证是没问题的,但CA机构颁发证书比较昂贵,小企业或个人用户 可能会选择自己颁发证书,这样就无法通过系统受信任的CA机构列表验证这个证书的真伪了,所以需要SSL Pinning这样的方式去验证。

  1. 什么是SSL Pinning?

可以理解为证书绑定,是指客户端直接保存服务端的证书(存入Xcode工程),建立https连接时直接对比服务端返回的和客户端保存的两个证书是否一样,一样就表明证书是真的,不再去系统的信任证书机构里寻找验证。

这适用于非浏览器应用,因为浏览器跟很多未知服务端打交道,无法把每个服务端的证书都保存到本地,但CS架构的手机APP事先已经知道要进行通信的服务端,可以直接在客户端保存这个服务端的证书用于校验。

  1. 为什么直接对比就能保证证书没问题?

如果中间人从客户端取出证书,再伪装成服务端跟其他客户端通信,它发送给客户端的这个证书不就能通过验证吗?
确实可以通过验证,但后续的流程走不下去,因为下一步客户端会用证书里的公钥加密,中间人没有这个证书的私钥就解不出内容,也就截获不到数据,这个证书的私钥只有真正的服务端有,中间人伪造证书主要伪造的是公钥。

如何使用AFNetworking发送HTTPS请求

建立自己的安全策略(Security Policy)

AFSecurityPolicy分三种验证模式:

  • AFSSLPinningModeNone

这个模式表示不做SSL pinning,只跟浏览器一样在系统的信任机构列表里验证服务端返回的证书。若证书是信任机构签发的就会通过,若是自己服务器生成的证书,这里是不会通过的。

  • AFSSLPinningModeCertificate

这个模式表示用证书绑定方式验证证书,需要客户端保存有服务端的证书拷贝,这里验证分两步,第一步验证证书的域名/有效期等信息,第二步是对比服务端返回的证书跟客户端返回的是否一致。

这里还没弄明白第一步的验证是怎么进行的,代码上跟去系统信任机构列表里验证一样调用了SecTrustEvaluate,只是这里的列表换成了客户端保存的那些证书列表。若要验证这个,是否应该把服务端证书的颁发机构根证书也放到客户端里?

  • AFSSLPinningModePublicKey

这个模式同样是用证书绑定方式验证,客户端要有服务端的证书拷贝,只是验证时只验证证书里的公钥,不验证证书的有效期等信息。只要公钥是正确的,就能保证通信不会被窃听,因为中间人没有私钥,无法解开通过公钥加密的数据。

整个AFSecurityPolicy就是实现这这几种验证方式,剩下的就是实现细节了,详见源码。

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
- (void)testClientCertificate {
NSString *url = @"https://218.244.131.231/ManicureShop/api/order/pay/%@";
NSDictionary *dic = @{@"request" : @{
@"orderNo" : @"1409282102222110030643",
@"type" : @(2)
}
};

NSData *postData = [NSJSONSerialization dataWithJSONObject:dic options:NSJSONWritingPrettyPrinted error:nil];
//stringWithFormat 自定义文字混淆函数
NSString *sign = [self signWithSignKey:@"test" params:dic];
NSMutableData *body = [postData mutableCopy];
NSLog(@"%@", [[NSString alloc] initWithData:body encoding:NSUTF8StringEncoding]);
url = [NSString stringWithFormat:url, sign];

//设置可以解析的文本
//code =1016错误因为这里
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.requestSerializer = [AFJSONRequestSerializer serializer];
manager.responseSerializer = [AFJSONResponseSerializer serializer];
[manager.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Accept"];
[manager.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
manager.responseSerializer.acceptableContentTypes = [NSSet setWithArray:@[@"application/json", @"text/plain",@"text/html"]];
//自定义安全策略
manager.securityPolicy = [self customSecurityPolicy];

[manager POST:url parameters:dic success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(@"JSON: %@", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {

NSLog(@"Error: %@", error);
}];
}

//某种和服务器约定的混淆方式 默认使用不混淆
-(id) signWithSingleKey:(NSString *)inString params: (NSDictionary *)inDictionnary {

return inString;
}

//AFNetworking的自定义安全措施
-(AFSecurityPolicy *) customSecurityPolicy {
NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"工程根目录中证书的名字" ofType:@"cer"];
NSData *certData = [NSData dataWithContentsOfFile:cerPath];
//初始化时使用证书绑定加密 也可以使用公钥绑定加密 可选有3种模式
AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
//允许使用非CA签名的证书
//这里有个问题,如果使用的是IP测试模式 不通过DNS 需要去AFNet的安全.m文件里注销掉验证域名的代码 code=1012因为这里
[securityPolicy setAllowInvalidCertificates:YES];
[securityPolicy setPinnedCertificates:@[certData]];


return securityPolicy;
}

注销域名验证

前面说过,验证站点证书,是通过域名的,如果服务器端站点没有绑定域名(万恶的备案),仅靠IP地址上面的方法是绝对不行的。
需要修改AFNetworking2的源代码!打开AFSecurityPolicy.m文件,找到方法:

1
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain

将下面这部分注释掉

1
2
3
4
5
6
7
8
9
10
//            SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);
//
// if (!AFServerTrustIsValid(serverTrust)) {
// return NO;
// }
//
// if (!self.validatesCertificateChain) {
// return YES;
// }

这样,AFSecurityPolicy就只会比对服务器证书和内嵌证书是否一致,不会再验证证书是否和站点域名一致了。

  • Security
  • Net
  • HTTP
  • Tutorial

展开全文 >>

Xcode相关的各种路径

2015-11-01

NSBundle

  1. 一个NSBundle代表一个文件夹,利用NSBundle能访问对应的文件夹
  2. 利用mainBundle就可以访问软件资源包中的任何资源
  3. 模拟器应用程序的安装路径
    1
    /Users/aplle/资源库/Application Support/iPhone Simulator/7.1/Applications

Xcode文档安装路径

1
/Applications/Xcode.app/Contents/Developer/Documentation/DocSets

Xcode模拟器安装路径

1
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs

Xcode自带头文件的路径

1
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator7.1.sdk/System/Library/Frameworks/UIKit.framework/Headers

修改了系统自带头文件后,Xcode会报错

解决方案:删掉下面文件夹的缓存即可(aplle是电脑的用户名)

1
/Users/aplle/资源库/Developer/Xcode/DerivedData

或者

1
/Users/aplle/Library/Developer/Xcode/DerivedData

Xcode插件的安装路径

可以在这里删除内容来卸载插件

1
2
3
4
5
aplle是用户名
/Users/aplle/Library/Application Support/Developer/Shared/Xcode/Plug-ins```

## 沙盒路径

/Users/apple/Library/Application Support/iPhone Simulator/ 7.1/Applications/ 246D511D-E377-492F-8D02-B7AB3589758E/Documents

1
2
3

## Codesnippets代码段储存位置

~/Library/Developer/Xcode/UserData/CodeSnippets

1
2
3

## 合并手机和模拟器静态库

lipo -create Debug-iphoneos/libBestpayLogin.a Debug-iphonesimulator/libBestpayLogin.a -output libBestpayLogin.a


  • iOS
  • IDE
  • Tips

展开全文 >>

Hexo常用指令

2015-10-29
bashnpm install hexo -g #安装  
npm update hexo -g #升级  
hexo init #初始化

简写

hexo n "我的博客" == hexo new "我的博客" #新建文章
hexo p == hexo publish
hexo g == hexo generate#生成
hexo s == hexo server #启动服务预览
hexo d == hexo deploy#部署

服务器

hexo server #Hexo 会监视文件变动并自动更新,您无须重启服务器。
hexo server -s #静态模式
hexo server -p 5000 #更改端口
hexo server -i 192.168.1.1 #自定义 IP

hexo clean #清除缓存 网页正常情况下可以忽略此条命令
hexo g #生成静态网页
hexo d #开始部署

监视文件变动

hexo generate #使用 Hexo 生成静态文件快速而且简单
hexo generate --watch #监视文件变动

完成后部署

两个命令的作用是相同的
hexo generate --deploy
hexo deploy --generate

hexo deploy -g
hexo server -g

草稿

hexo publish [layout] <title>

模版

hexo new "postName" #新建文章
hexo new page "pageName" #新建页面
hexo generate #生成静态页面至public目录
hexo server #开启预览访问端口(默认端口4000,'ctrl + c'关闭server)
hexo deploy #将.deploy目录部署到GitHub

hexo new [layout] <title>
hexo new photo "My Gallery"
hexo new "Hello World" --lang tw

变量 描述
layout 布局
title 标题
date 文件建立日期
title: 使用Hexo搭建个人博客
layout: post
date: 2014-03-03 19:07:43
comments: true
categories: Blog
tags: [Hexo]
keywords: Hexo, Blog
description: 生命在于折腾,又把博客折腾到Hexo了。给Hexo点赞。

模版(Scaffold)

hexo new photo "My Gallery"

变量 描述
layout 布局
title 标题
date 文件建立日期

设置文章摘要

以上是文章摘要 <!--more--> 以下是余下全文

写作

hexo new page <title>
hexo new post <title>

变量 描述
:title 标题
:year 建立的年份(4 位数)
:month 建立的月份(2 位数)
:i_month 建立的月份(去掉开头的零)
:day 建立的日期(2 位数)
:i_day 建立的日期(去掉开头的零)

推送到服务器上

hexo n #写文章
hexo g #生成
hexo d #部署 #可与hexo g合并为 hexo d -g

报错

1.找不到git部署

ERROR Deployer not found: git

解决方法

npm install hexo-deployer-git --save

3.部署类型设置git

hexo 3.0 部署类型不再是github,_config.yml 中修改

bash# Deployment
## Docs: http://hexo.io/docs/deployment.html
deploy:
  type: git
  repository: git@***.github.com:***/***.github.io.git
  branch: master

4. xcodebuild

xcode-select: error: tool 'xcodebuild' requires Xcode, but active developer directory '/Library/Developer/CommandLineTools' is a command line tools instance

npm install bcrypt

5. RSS不显示

安装RSS插件

npm install hexo-generator-feed --save

开启RSS功能

编辑hexo/_config.yml,添加如下代码:

rss: /atom.xml #rss地址  默认即可

开启评论

1.我使用多说代替自带的评论,在多说 网站注册 > 后台管理 > 添加新站点 > 工具 === 复制通用代码 里面有 short_name

  1. 在根目录 _config.yml 添加一行 disqus_shortname: jslite 是在多说注册时产生的

  2. 复制到 themes\landscape\layout\_partial\article.ejs
    把

<% if (!index && post.comments && config.disqus_shortname){ %>
<section id="comments">
<div id="disqus_thread">
  <noscript>Please enable JavaScript to view the <lt;a href="//disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
</div>
</section>
<% } %>

改为

html<% if (!index && post.comments && config.disqus_shortname){ %>
  <section id="comments">
    <!-- 多说评论框 start -->
    <div class="ds-thread" data-thread-key="<%= post.layout %>-<%= post.slug %>" data-title="<%= post.title %>" data-url="<%= page.permalink %>"></div>
    <!-- 多说评论框 end -->
    <!-- 多说公共JS代码 start (一个网页只需插入一次) -->
    <script type="text/javascript">
    var duoshuoQuery = {short_name:'<%= config.disqus_shortname %>'};
      (function() {
        var ds = document.createElement('script');
        ds.type = 'text/javascript';ds.async = true;
        ds.src = (document.location.protocol == 'https:' ? 'https:' : 'http:') + '//static.duoshuo.com/embed.js';
        ds.charset = 'UTF-8';
        (document.getElementsByTagName('head')[0]
         || document.getElementsByTagName('body')[0]).appendChild(ds);
      })();
      </script>
    <!-- 多说公共JS代码 end -->
  </section>
<% } %>
  • Hexo
  • Terminal
  • Tools

展开全文 >>

设置Button的标题

2015-10-27

设置Button的标题

在定义UIButton的时候,经常会使用titleLabel.text设置UIButton的值,但是Run出来没有任何显示,原因在于:

  1. 正常使用UIButton的时候设置Title是要对应Button的ControlState,因为UIButton继承于UIControl,在设置值得时候需要对象状态
  2. setAttributedTitle可以正确设置title,是设置UIButton里的Attribute,而不是组成UIButton里的titlelabel的text
    [uibutton setAttributedTitle:[[NSAttributedString alloc]initWithString:@"titleText"] forState:UIControlStateNormal]
  3. 对应的currentTitle 也就是/normal/highlighted/selected/disabled状态下的title值,属性为readOnly
  4. 至于通过titleLabel的text不显示的原因是默认UIButton的titleLable是没设置frame的,而且hidden=YES;只要你设置这2个值就可以正常显示
    UIButton *uibtn = [[UIButton alloc]initWithFrame:CGRectMake(0, 100, 100, 30)];

    或者
    UIButton *uibtn = [UIButton buttonWithType:UIButtonTypeCustom];[uibtn setFrame:CGRectMake(0, 100, 100, 30)];
1
2
- (void)setTitle:(NSString *)title forState:(UIControlState)state;
- (void)setAttributedTitle:(NSAttributedString *)title forState:(UIControlState)state
  • iOS
  • Button
  • Tips

展开全文 >>

通讯录读取

2015-10-27

通讯录读取

ContactTool.h

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
typedef void(^AddressBook)(BOOL successed, NSArray<__kindof NSDictionary*> *addressBookArray);

UIKIT_EXTERN NSString *const Contact_Name;
UIKIT_EXTERN NSString *const Contact_Phone;
UIKIT_EXTERN NSString *const Contact_Lowercase_Name;


@interface ContactTool : NSObject
//读取通讯录,保存为姓名,电话,姓名拼音小写字典
+ (void)pickAddressBookCompletion:(AddressBook)completion;
//标准化为大陆号码,去除+86和"-"还有" "
+ (NSString *)phoneNumberFormat:(NSString *)phone;
//转换汉字成拼音
+ (NSString *)transformToPinyin:(NSString *)string;

@end

###ContactTool.m

NSString *const Contact_Name = @"name";
NSString *const Contact_Phone = @"phone";
NSString *const Contact_Lowercase_Name = @"lowercaseName";

@implementation ContactTool

#pragma mark - AdressBook DataList读取通讯录

+ (void)pickAddressBookCompletion:(AddressBook)completion {
    //创建一个电话本的参考
    ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions(NULL, NULL);
    //创建一个信号
    dispatch_semaphore_t sema = dispatch_semaphore_create(0);
    //申请接入闭包
    ABAddressBookRequestAccessWithCompletion(addressBookRef, ^(bool granted, CFErrorRef error) {
        //接入成功发送信号
        dispatch_semaphore_signal(sema);
    });
    //信号开始是0 发送出一个信号变成-1 信号值小于0时继续执行,如果没有得到信号,等待FOREVER
    dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
    //检查权限
    if (ABAddressBookGetAuthorizationStatus() != kABAuthorizationStatusAuthorized) {
        completion(NO,nil);
        return;
    }
    //检查是否成功创建
    if (addressBookRef == nil) {
        return;
    }
    //成功则拷贝电话本
    [self copyAddressBook:addressBookRef completion:completion];

}

+ (void)copyAddressBook:(ABAddressBookRef)addressBook completion:(AddressBook)completion {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //C Float Index 使用有符号long的数目作为索引 获得总人数
        CFIndex numberOfPeople = ABAddressBookGetPersonCount(addressBook);
        //
        CFArrayRef people = ABAddressBookCopyArrayOfAllPeople(addressBook);
        //
        NSMutableArray *addressBookArray = [NSMutableArray array];
        for (int i = 0; i < numberOfPeople; ++i) {
            ABRecordRef person = CFArrayGetValueAtIndex(people, i);
            NSString *firstName = (__bridge NSString *)(ABRecordCopyValue(person, kABPersonFirstNameProperty));
            NSString *lastName = (__bridge NSString *)(ABRecordCopyValue(person, kABPersonLastNameProperty));
            NSMutableString *compositeName = nil;

            //判断是否存在空
            if (firstName == nil && lastName == nil) {
                compositeName = [NSMutableString stringWithString:@"#"];
            }else if (firstName == nil){
                compositeName = [NSMutableString stringWithFormat:@"%@",lastName];
            }else if (lastName == nil){
                compositeName = [NSMutableString stringWithFormat:@"%@",firstName];
            }else {
                compositeName = [NSMutableString stringWithFormat:@"%@%@",lastName,firstName];
            }
            //取出电话号码
            ABMultiValueRef phone = ABRecordCopyValue(person, kABPersonPhoneProperty);
            NSInteger phoneCount = ABMultiValueGetCount(phone);
            //
            for (int i = 0; i < phoneCount; ++i) {
                NSString *phoneNumber = (__bridge NSString *)(ABMultiValueCopyValueAtIndex(phone, i));
                phoneNumber = [self phoneNumberFormat:phoneNumber];
                //模拟器测试需要注销掉if
                //RegxTool是个自己写的正则校验函数
                if ([RegxTool validPhoneNumber:phoneNumber]) {
                    NSString *lowercaseName = [[self transformToPinyin:compositeName] lowercaseString];
                    NSDictionary *dict = @{Contact_Name:compositeName,Contact_Phone:phoneNumber,Contact_Lowercase_Name:lowercaseName};
                    [addressBookArray addObject:dict];
                }
            }
        }
        //
        if (addressBookArray.count > 0) {
            //字典数组按KEY排序
            NSArray *sortedArray = [self sortArray:addressBookArray withKey:(NSString *)Contact_Lowercase_Name];
            [addressBookArray removeAllObjects];
            [addressBookArray addObjectsFromArray:sortedArray];
            //返回结果
            dispatch_async(dispatch_get_main_queue(), ^{
                completion(YES, addressBookArray);
            });
        }
    });
}

+ (NSString *)phoneNumberFormat:(NSString *)phone {
    NSMutableString *tempPhone = [NSMutableString stringWithString:phone];
    NSRange prefixRange = [tempPhone rangeOfString:@"+86"];
    if (prefixRange.location != NSNotFound) {
        [tempPhone deleteCharactersInRange:prefixRange];
    }
    NSRange spaceRange = [tempPhone rangeOfString:@" "];
    while (spaceRange.location != NSNotFound) {
        [tempPhone deleteCharactersInRange:spaceRange];
        spaceRange = [tempPhone rangeOfString:@" "];
    }
    NSRange linkRange = [tempPhone rangeOfString:@"-"];
    while (linkRange.location != NSNotFound) {
        [tempPhone deleteCharactersInRange:linkRange];
        linkRange = [tempPhone rangeOfString:@" "];
    }
    return tempPhone;
}

+ (NSString *)transformToPinyin:(NSString *)string {
    NSString *regularString = @"^[A-Za-z0-9\\p{Z}\\p{P}]+$";
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regularString];
    BOOL needTransform = ![predicate evaluateWithObject:string];
    //
    NSMutableString *mutableString = [NSMutableString stringWithString:string];
    if (needTransform) {
        CFStringTransform((CFMutableStringRef)mutableString, NULL, kCFStringTransformToLatin, false);
        CFStringTransform((CFMutableStringRef)mutableString, NULL, kCFStringTransformStripCombiningMarks, false);
    }
    return mutableString;
}

+ (NSArray *)sortArray:(NSArray *)array withKey:(NSString *)key {
    NSSortDescriptor *sortDesc = [NSSortDescriptor sortDescriptorWithKey:key ascending:YES];
    NSArray *sortDescArr = [NSArray arrayWithObject:sortDesc];
    NSArray *sortedArr = [array sortedArrayUsingDescriptors:sortDescArr];
    return sortedArr;
}

@end

权限检查代码

由于在读取通讯录的过程中,并不一定有读取的权限,如果使用以下代码,在真机调试中可以顺利在获得权限后读取,模拟器中不行,原因:

由于闭包还未返回,代码已经顺序执行,addressBook在顺序执行的过程中始终为nil

- (void)pickAddressBook{    
        //读取通讯录权限
        if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined) {
        //请求完成闭包
ABAddressBookRequestAccessWithCompletion(addressBookRef, ^(bool granted, CFErrorRef error) {
                CFErrorRef *error = NULL;
                ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, error);
                //自定义的拷贝函数
                [self copyAddressBook:addressBook];
            });
        }else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized){
            CFErrorRef *error = NULL;
            ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, error);
            //自定义的拷贝函数
            [self copyAddressBook:addressBook];
        }else{
        //读取失败提示
            dispatch_async(dispatch_get_main_queue(), ^{
                UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"翼支付" message:@"通讯录读取"  cancelButtonTitle:@"确定" otherButtonTitles:nil, nil];
                [alert show];

            });
        }

}

通讯录校验代码

电话号码去+86和-

+ (NSString *)phoneNumberFormat:(NSString *)phone;

电话号码正则校验

+ (BOOL)validPhoneNumber:(NSString *)phone{
    NSString *regx = @"^[0-9]{11}$";
        NSPredicate *pred = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regx];
    return [pred evaluateWithObject:phone];
    return NO;
}

[正则表达式常用校验][^1]

[^1]:

  • iOS
  • Contact
  • Tips

展开全文 >>

Hello World

2015-10-01

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment

展开全文 >>

&laquo; Prev1…434445 Next »
© 2021 Alan Li
Hexo Theme Yilia by Litten
  • 所有文章
  • 关于我

tag:

  • iOS
  • Java
  • Collection
  • Python
  • Shell
  • CMake
  • Memory
  • JavaScript
  • Architecture
  • AnchorPoint
  • Android
  • Web
  • Annotation
  • AFNetworking
  • Window
  • ViewController
  • AutoLayout
  • Dozer
  • CoreAnimation
  • Cycle Retain
  • Block
  • UI
  • IDE
  • FrontEnd
  • CSS
  • Category
  • TableViewCell
  • Security
  • Net
  • JSP
  • Spring
  • C
  • MyBatis
  • Date
  • React
  • GCD
  • UITouch
  • Gesture
  • UIControl
  • Git
  • HTML
  • HTTPS
  • HTTP
  • Servlet
  • Server
  • DataBase
  • MySQL
  • Linux
  • Tutorial
  • Ajax
  • Type
  • JQuery
  • JSON
  • Exception
  • Parameter
  • Reflect
  • Thread
  • Sort
  • KVO
  • MKMapKit
  • Overlay
  • Maven
  • Configure
  • Tips
  • Transaction
  • Swift
  • NavigationBar
  • Nginx
  • Runtime
  • OpenCV
  • Property
  • Playground
  • Protocol
  • Redux
  • ScrollView
  • Session
  • Cookie
  • Shiro
  • Error
  • Singleton
  • RegEx
  • StackView
  • StatusBar
  • Base64
  • Socket
  • TCP
  • IP
  • TextField
  • CALayer
  • UILabel
  • View
  • Animation
  • Xcode
  • Hexo
  • Terminal
  • OC
  • Device
  • Log
  • Image
  • JUnit
  • Oval
  • Archive
  • XSS
  • Compiler
  • Aspect
  • Responder
  • Class
  • FireWall
  • RetainCount
  • Const
  • Frame
  • String
  • Symbols
  • Framework
  • CocoaPods
  • Unity
  • Message
  • Button
  • AuthorizationStatus
  • Struct
  • XCTest
  • NSNotification
  • Contact

    缺失模块。
    1、请确保node版本大于6.2
    2、在博客根目录(注意不是yilia根目录)执行以下命令:
    npm i hexo-generator-json-content --save

    3、在根目录_config.yml里添加配置:

      jsonContent:
        meta: false
        pages: false
        posts:
          title: true
          date: true
          path: true
          text: false
          raw: false
          content: false
          slug: false
          updated: false
          comments: false
          link: false
          permalink: false
          excerpt: false
          categories: false
          tags: true
    

我写的,大概率是错的。。。。。