正则表达式在iOS中的应用总结

一、什么是正则表达式

  正则表达式,又称正规表示法,是对字符串操作的一种逻辑公式。正则表达式可以检测给定的字符串是否符合我们定义的逻辑,也可以从字符串中获取我们想要的特定部分。它可以迅速地用极简单的方式达到字符串的复杂控制。

二、正则表达式的语法

先看一个过滤纯数字的例子

- (BOOL)validateNumber:(NSString *) textString
{ NSString* number=@"^[0-9]+$";
NSPredicate *numberPre = [NSPredicate predicateWithFormat:@"SELF MATCHES %@",number];
return [numberPre evaluateWithObject:textString];
}

其中下述语句就是一个正则表达式

@"^[0-9]+$"
它代表了字符串中只能包含>=10-9的数字,语法是不是有一些怪异?
下面我们先撇开iOS中的正则表达式的语法,用通俗的正则表达式语法来为介绍一下。(iOS语法与通俗的正则表达式语法相同,不同在于对转义字符的处理上(语言类的都相同))

正则表达式语法:

首先,特殊符号'^'和'$'。他们的作用是分别指出一个字符串的开始和结束。eg:
“^one”:表示所有以”one”开始的字符串(”one cat”,”one123″,·····);
类似于:- (BOOL)hasPrefix:(NSString *)aString;
“a dog$”:表示所以以”a dog”结尾的字符串(”it is a dog”,·····);
类似于:- (BOOL)hasSuffix:(NSString *)aString;
“^apple$”:表示开始和结尾都是”apple”的字符串,这个是唯一的~;
“banana”:表示任何包含”banana”的字符串。
类似于 iOS8的新方法- (BOOL)containsString:(NSString *)aString,搜索子串用的。
‘*','+'和'?'这三个符号,表示一个或N个字符重复出现的次数。它们分别表示“没有或更多”([0,+]取整),“一次或更多”([1,+]取整),“没有或一次”([0,1]取整)。下面是几个例子:
“ab*”:表示一个字符串有一个a后面跟着零个或若干个b(”a”, “ab”, “abbb”,……);
“ab+”:表示一个字符串有一个a后面跟着至少一个b或者更多( ”ab”, “abbb”,……);
“ab?”:表示一个字符串有一个a后面跟着零个或者一个b( ”a”, “ab”);
“a?b+$”:表示在字符串的末尾有零个或一个a跟着一个或几个b( ”b”, “ab”,”bb”,”abb”,……)。
可以用大括号括起来({}),表示一个重复的具体范围。例如
“ab{4}”:表示一个字符串有一个a跟着4个b(”abbbb”);
“ab{1,}”:表示一个字符串有一个a跟着至少1个b(”ab”,”abb”,”abbb”,……);
“ab{3,4}”:表示一个字符串有一个a跟着3到4个b(”abbb”,”abbbb”)。
那么,“*”可以用{0,}表示,“+”可以用{1,}表示,“?”可以用{0,1}表示
注意:可以没有下限,但是不能没有上限!例如“ab{,5}”是错误的写法
 | ”表示“或”操作:
“a|b”:表示一个字符串里有”a”或者”b”;
“(a|bcd)ef”:表示”aef”或”bcdef”;
“(a|b)*c”:表示一串”a”"b”混合的字符串后面跟一个”c”;
方括号”[ ]“表示在括号内的众多字符中,选择1-N个括号内的符合语法的字符作为结果,例如
[ab]“:表示一个字符串有一个”a”或”b”(相当于”a|b”);
[a-d]“:表示一个字符串包含小写的'a'到'd'中的一个(相当于”a|b|c|d”或者”[abcd]“);
“^[a-zA-Z]“:表示一个以字母开头的字符串;
[0-9]a”:表示a前有一位的数字;
[a-zA-Z0-9]$”:表示一个字符串以一个字母或数字结束。
.”匹配除“\r\n”之外的任何单个字符:
“a.[a-z]“:表示一个字符串有一个”a”后面跟着一个任意字符和一个小写字母;
“^.{5}$”:表示任意1个长度为5的字符串;
“\num” 其中num是一个正整数。表示”\num”之前的字符出现相同的个数,例如
“(.)\1″:表示两个连续的相同字符。
“10\{1,2\}” : 表示数字1后面跟着1或者2个0 (“10″,”100″)。
 0\{3,\}  表示数字为至少3个连续的0 (“000”,“0000”,······)。
在方括号里用'^'表示不希望出现的字符,'^'应在方括号里的第一位。
“@[^a-zA-Z]4@”表示两个”@”中不应该出现字母)。
常用的还有:
 \d ”匹配一个数字字符。等价于[0-9]
 \D”匹配一个非数字字符。等价于[^0-9]
 \w ”匹配包括下划线的任何单词字符。等价于“[A-Za-z0-9_]”。
 \W ”匹配任何非单词字符。等价于“[^A-Za-z0-9_]”。
iOS中书写正则表达式,碰到转义字符,多加一个“\”,例如:
全数字字符:@”^\\d\+$”

  
三、iOS中正则表达式的3种使用

1.正则表达式与NSPredicate连用,eg:

- (BOOL)validateNumber:(NSString *) textString
{
NSString* number=@"^[0-9]+$";
NSPredicate *numberPre = [NSPredicate predicateWithFormat:@"SELF MATCHES %@",number];
return [numberPre evaluateWithObject:textString];
}

2.NSString方法

- (NSRange)rangeOfString:(NSString *)aString options:
(NSStringCompareOptions)mask;
NSString *searchText = @"rangeOfString";
NSRange range = [searchText rangeOfString:@"^[0-9]+$" 
options:NSRegularExpressionSearch];
if (range.location != NSNotFound) {
NSLog(@"range :%@", [searchText substringWithRange:range]);
}

3.正则表达式类(NSRegularExpression)

NSString *searchText = @"you want to match"; 
NSError *error = NULL;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"^[0-9]+$" options:NSRegularExpressionCaseInsensitive error:&error];
NSTextCheckingResult *result = [regex firstMatchInString:searchText options:0 range:NSMakeRange(0, [searchText length])];
if (result) {
 NSLog(@"%@", [searchText substringWithRange:result.range]);
}

四、常用的正则表达式

1.验证用户名和密码:”^[a-zA-Z]\w{5,15}$”
2.验证电话号码:(”^(\\d{3,4}-)\\d{7,8}$”)
eg:021-68686868  0511-6868686;
3.验证手机号码:”^1[3|4|5|7|8][0-9]\\d{8}$”;
4.验证身份证号(15位或18位数字):”\\d{14}[[0-9],0-9xX]”;
5.验证Email地址:(“^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\.\\w+([-.]\\w+)*$”);
6.只能输入由数字和26个英文字母组成的字符串:(“^[A-Za-z0-9]+$”) ;
7.整数或者小数:^[0-9]+([.]{0,1}[0-9]+){0,1}$
8.只能输入数字:”^[0-9]*$”。
9.只能输入n位的数字:”^\\d{n}$”。
10.只能输入至少n位的数字:”^\\d{n,}$”。
11.只能输入m~n位的数字:”^\\d{m,n}$”。
12.只能输入零和非零开头的数字:”^(0|[1-9][0-9]*)$”。
13.只能输入有两位小数的正实数:”^[0-9]+(.[0-9]{2})?$”。
14.只能输入有1~3位小数的正实数:”^[0-9]+(\.[0-9]{1,3})?$”。
15.只能输入非零的正整数:”^\+?[1-9][0-9]*$”。
16.只能输入非零的负整数:”^\-[1-9][]0-9″*$。
17.只能输入长度为3的字符:”^.{3}$”。
18.只能输入由26个英文字母组成的字符串:”^[A-Za-z]+$”。
19.只能输入由26个大写英文字母组成的字符串:”^[A-Z]+$”。
20.只能输入由26个小写英文字母组成的字符串:”^[a-z]+$”。
21.验证是否含有^%&',;=?$\”等字符:”[^%&',;=?$\x22]+”。
22.只能输入汉字:”^[\u4e00-\u9fa5]{0,}$”。
23.验证URL:”^http://([\\w-]+\.)+[\\w-]+(/[\\w-./?%&=]*)?$”。
24.验证一年的12个月:”^(0?[1-9]|1[0-2])$”正确格式为:”01″~”09″和”10″~”12″。
25.验证一个月的31天:”^((0?[1-9])|((1|2)[0-9])|30|31)$”正确格式为;”01″~”09″、”10″~”29″和“30”~“31”。
26.获取日期正则表达式:\\d{4}[年|\-|\.]\\d{\1-\12}[月|\-|\.]\\d{\1-\31}日?
评注:可用来匹配大多数年月日信息。
27.匹配双字节字符(包括汉字在内):[^\x00-\xff]
评注:可以用来计算字符串的长度(一个双字节字符长度计2,ASCII字符计1)
28.匹配空白行的正则表达式:\n\s*\r
评注:可以用来删除空白行
29.匹配HTML标记的正则表达式:<(\S*?)[^>]*>.*?</>|<.*? />
评注:网上流传的版本太糟糕,上面这个也仅仅能匹配部分,对于复杂的嵌套标记依旧无能为力
30.匹配首尾空白字符的正则表达式:^\s*|\s*$
评注:可以用来删除行首行尾的空白字符(包括空格、制表符、换页符等等),非常有用的表达式
31.匹配网址URL的正则表达式:[a-zA-z]+://[^\s]*
评注:网上流传的版本功能很有限,上面这个基本可以满足需求
32.匹配帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线):^[a-zA-Z][a-zA-Z0-9_]{4,15}$
评注:表单验证时很实用
33.匹配腾讯QQ号:[1-9][0-9]\{4,\}
评注:腾讯QQ号从10 000 开始
34.匹配中国邮政编码:[1-9]\\d{5}(?!\d)
评注:中国邮政编码为6位数字
35.匹配ip地址:((2[0-4]\\d|25[0-5]|[01]?\\d\\d?)\.){3}(2[0-4]\\d|25[0-5]|[01]?\\d\\d?)。

iOS开发中webView与JS交互的使用

UIWebView是iOS sdk中一个最常用的控件。是内置的浏览器控件,我们可以用它来浏览网页、打开文档等等,UIWebView能够加载html/htm、pdf、docx、txt等格式的文件。系统自带的Safari浏览器就是通过UIWebView实现的。下面我的一起来开开如何简单使用webView。

下面直接上代码:

搭建页面

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.url = @"https://www.baidu.com/";
    NSURLRequest *urlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:self.url]];
       [self.webView loadRequest:urlRequest];
}

返回按钮点击事件

- (void)goBack
{
    if ([self.webView canGoBack])
    {
        [self.webView goBack];//当有H5二级页面在本控制器上加载出现时,调这个
    }else {
        //当前控制器pop/dismiss返回上一级控制器
    }
}

webview的三个代理方法:

//利用URL来调用我们iOSnative的操作
-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    NSString *url = request.URL.absoluteString;
    //拦截URL跳转
    if ([url hasPrefix:@"事先规定好的协议URL"])
    {        
        //在这里进行跳转
        return NO;
    }
    NSString *pattern = nil;
    NSRange r;
    //拦截URL跳转
    pattern = @"事先规定好的协议URL";
    r = [url rangeOfString:pattern options:NSRegularExpressionSearch];
    if (r.location != NSNotFound) {  
        //在这里进行跳转      
        return NO;
    }
    //调用父类的方法
    return  [super webView:webView shouldStartLoadWithRequest:request navigationType:navigationType];

}

-(void)webViewDidStartLoad:(UIWebView *)webView
{
    //开始加载
}

//iOS调用JS的方法
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
    //加载结束
    [super webViewDidFinishLoad:webView];
    //    NSString *docStr=[webView stringByEvaluatingJavaScriptFromString:@"document.documentElement.textContent"];//获取web页面内容信息,此处获取的是个json字符串

    //    NSString *docStr=[webView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"alert(1);"]];//显示并获取一个弹窗

    //    NSString *docStr=[webView stringByEvaluatingJavaScriptFromString:@"document.location.href"];//获取当前页面的url

    //    NSString *docStr=[webView stringByEvaluatingJavaScriptFromString:@"document.title"];//获取当前页面的title

    //    NSString *docStr=[webView stringByEvaluatingJavaScriptFromString:@"document.getElementsByName('q')[0].value='http://www.cnblogs.com/Twisted-Fate/p/Mr.林';"];//修改当前页面的元素的值

    //    NSString *docStr=[webView stringByEvaluatingJavaScriptFromString:@"document.forms[0].submit();"];//表单提交

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
    [webView stringByEvaluatingJavaScriptFromString:@"var script = document.createElement('script');"
     "script.type = 'text/javascript';"
     "script.text = \"function myFunction() { "
     "var field = document.getElementsByName('q')[0];"
     "field.value='http://www.cnblogs.com/Twisted-Fate/p/Mr.林';"
     "document.forms[0].submit();"
     "}\";"
     "document.getElementsByTagName('head')[0].appendChild(script);"];
    });
    NSString *docStr=[webView stringByEvaluatingJavaScriptFromString:@"myFunction();"];

    NSLog(@"------>%@",docStr);
}

WebView自带的属性是不可以改变网页中字体大小颜色这些需求的,笔者给出几种解决方案如下:

  1. 第一种方式:在WebView的代理方法webViewDidFinishLoad中:

    字体大小: [webView stringByEvaluatingJavaScriptFromString:@"document.getElementsByTagName('body')[0].style.webkitTextSizeAdjust= '12%'"];
    
    字体颜色:[webView stringByEvaluatingJavaScriptFromString:@"document.getElementsByTagName('body')[0].style.webkitTextFillColor= 'green'"];
    
    背景颜色:[webView stringByEvaluatingJavaScriptFromString:@"document.getElementsByTagName('body')[0].style.background='#F6F7F3'"];
    
  2. 第二种方式:在下面可以通过改变margin大小来改变webview文字距离边距大小,13代表字体大小,self.strBookTips是要被操作的字符串。

    NSString *BookStr = [NSString stringWithFormat:@"<html> \n"
                          "<head> \n"
                          "<style type=\"text/css\"> \n"
                          "body {margin:10;font-size: %d;}\n"
                          "</style> \n"
                          "</head> \n"
                          "<body>%@</body> \n"
                          "</html>",13,self.strBookTips];
    [self.webView loadHTMLString:BookStr baseURL:nil];
    
  3. 第三种方式,这种缺少了改变背景颜色

    NSString * formatString = @"<span style=\"font-size:13px;color:#7E7C8A\">%@</span>";
    NSString * htmlString = [NSString stringWithFormat:formatString,self.strBookTips];
    [self.webView loadHTMLString:BookStr baseURL:nil];
    

http://www.cnblogs.com/gcb999/p/3178728.html

http://www.kwstu.com/ArticleView/guandebao_20139618222689

UIViewController中各方法调用顺序详解

UIViewController中loadView, viewDidLoad, viewWillUnload, viewDidUnload, viewWillAppear, viewDidAppear, viewWillLayoutSubviews,viewDidLayoutSubviews,viewWillDisappear, viewDidDisappear方法,按照调用顺序说明如下

  1. initWithNibName:bundle:

初始化UIViewController,执行关键数据初始化操作,注意这里不要做view相关操作,view在loadView方法中才初始化,这时loadView还未调用。如果使用StoryBoard进行视图管理,程序不会直接初始化一个UIViewController,StoryBoard会自动初始化或在segue被触发时自动初始化,因此方法initWithNibName:bundle:不会被调用。如果在代码里面使用instantiateViewControllerWithIdentifier:方法显示初始化一个UIViewController,则initWithCoder方法会被调用。

如果是通过调用initWithNibName:bundle指定nib文件名初始化的话,ViewController会根据此nib来创建View。如果name参数为nil,则ViewController会通过以下两个步骤找到与其关联的nib:
1)如果ViewController的类名以“Controller”结尾,例如ViewController的类名是MyViewController,则查找是否存在MyView.nib;
2)找跟ViewController类名一样的文件,例如MyViewController,则查找是否存在MyViewController.nib

  1. loadView

当访问UIViewController的view属性时,view如果此时是nil,那么VC会自动调用loadView方法来初始化一个UIView并赋值给view属性。此方法用在初始化关键view,需要注意的是,在view初始化之前,不能先调用view的getter方法,否则将导致死循环(除非先调用了[supper
loadView];)。

-(void)loadView{
NSLog(@”loadView”);
//错误,将导致死循环,因此此时view=nil,VC会再次调用loadView来初始化view
self.view.backgroundColor = [UIColor greenColor];
}

-(void)loadView{
NSLog(@”loadView”);
//正确,先初始化view
self.view = [[MyView alloc] init];
self.view.backgroundColor = [UIColor greenColor];
}

如果没有重载loadView方法,则UIViewController会从nib或StoryBoard中查找默认的loadView,默认的loadView会返回一个空白的UIView对象。

  1. viewDidLoad

当VC的view对象载入内存后调用,用于对view进行额外的初始化操作

  1. viewWillAppear

在view即将添加到视图层级中(显示给用户)且任意显示动画切换之前调用(这个时候supperView还是nil)。这个方法中完成任何与视图显示相关的任务,例如改变视图方向、状态栏方向、视图显示样式等

  1. viewDidAppear

在view被添加到视图层级中,显示动画切换之后调用(这时view已经添加到supperView中)。在这个方法中执行视图显示相关附件任务,如果重载了这个方法,必须在方法中调用[supper
viewDidAppear];

  1. viewWillLayoutSubviews

view即将布局其Subviews。比如view的bounds改变了(例如状态栏从不显示到显示,视图方向变化),要调整Subviews的位置,在调整之前要做的一些工作就可以在该方法中实现。

  1. viewDidLayoutSubviews

view已经布局其Subviews。比如view的bounds改变了(例如状态栏从不显示到显示,视图方向变化),已经调整Subviews的位置,在调整完成之后要做的一些工作就可以在该方法中实现。

  1. viewWillDisappear

view即将从superView中移除且移除动画切换之前,此时还没有调用removeFromSuperview。

  1. viewDidDisappear

view从superView中移除,移除动画切换之后调用,此时已调用removeFromSuperview。

  1. viewWillUnload

在VC的view对象从内存中释放之前调用,可以在view被释放前做一些资源清理操作。在iOS6.0开始就废弃了,该方法不再会调用

  1. viewDidUnload

在VC的view对象从内存中释放之后调用,可以在view被释放后做一些view相关的引用清理操作,此时view为nil。在iOS6.0开始就废弃了,该方法不再会调用

当一个视图被移除屏幕并且销毁的时候的执行顺序,这个顺序差不多和上面的相反
1、viewWillDisappear 视图将被从屏幕上移除之前执行
2、viewDidDisappear 视图已经被从屏幕上移除,用户看不到这个视图了
3、dealloc 视图被销毁,此处需要对你在init和viewDidLoad中创建的对象进行释放

UITableView使用中的一些刁专问题总结

  1. tableview中cell的系统分隔线问题(分隔线顶满或者缩短)

    //tableview代理方法,设置系统cell的分隔线
    -(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
    {
        if (0 == indexPath.section) {
            if (0 == indexPath.row) {
                //cell分隔线缩短50
                if ([cell respondsToSelector:@selector(setSeparatorInset:)]) {
                    [cell setSeparatorInset:UIEdgeInsetsMake(0, 50, 0, 0)];
                }
            }else {
                //cell分割线顶满(分隔线加长)
                if ([cell respondsToSelector:@selector(setSeparatorInset:)]) {
                    [cell setSeparatorInset:UIEdgeInsetsZero];
                }
                if ([cell respondsToSelector:@selector(setLayoutMargins:)]) {
                    [cell setLayoutMargins:UIEdgeInsetsZero];
                }
            }
        }else if (1 == indexPath.section) {
            if (0 == indexPath.row) {
                if ([cell respondsToSelector:@selector(setSeparatorInset:)]) {
                    [cell setSeparatorInset:UIEdgeInsetsZero];
                }
                if ([cell respondsToSelector:@selector(setLayoutMargins:)]) {
                    [cell setLayoutMargins:UIEdgeInsetsZero];
                }
            }
        }else if (2 == indexPath.section){
            if (0 == indexPath.row) {
                if ([cell respondsToSelector:@selector(setSeparatorInset:)]) {
                    [cell setSeparatorInset:UIEdgeInsetsMake(0, 50, 0, 0)];
                }
            }
        }
    }
    
  2. tableview下面出现多余的cell(有分割线影响界面)

    //隐藏多余cell
    -(void)setExtraCellLineHidden: (UITableView *)tableView
    {
        UIView *view = [UIView new];
        view.backgroundColor = [UIColor clearColor];
        [tableView setTableFooterView:view];
    }
    

    例如:
    你要隐藏某个UITableView里面多余的cell
    UITableView *tabelDemo = [[UITableView alloc] init];
    [self.view addSubview:tabelDemo];
    然后直接调用就可以了
    [self setExtraCellLineHidden:tabelDemo];

  3. tableview顶部空出的一部分高度(原因我猜有可能是因为系统自动布局计算了状态栏的高度)

    //去掉tableview顶部高度多出的一部分
    self.edgesForExtendedLayout = UIRectEdgeNone;(这里self指当前控制器,只需要在viewDidl
    Load调用)

    效果图:
    Mou icon

    Mou icon

iOS二维码扫描总结

在 iOS7 以前,在iOS中实现二维码和条形码扫描,我们所知的有两大开源组件ZBar与ZXing. 这两大组件我们都有用过,这里总结下各自的缺点:

  1. ZBar
    ZBar在扫描的灵敏度上,和内存的使用上相对于ZXing上都是较优的,但是对于 “圆角二维码” 的扫描确很困难。

  2. ZXing
    ZXing 是 Google Code上的一个开源的条形码扫描库,是用java设计的,连Google Glass 都在使用的。但有人为了追求更高效率以及可移植性,出现了c++ port. Github上的Objectivc-C port,其实就是用OC代码封装了一下而已,而且已经停止维护。这样效率非常低,在instrument下面可以看到CPU和内存疯涨,在内存小的机器上很容易崩溃。

  3. AVFoundation
    AVFoundation无论在扫描灵敏度和性能上来说都是最优的,所以毫无疑问我们应该切换到AVFoundation,需要兼容iOS 6或之前的版本可以用zbar或zxing代替。

原生二维码扫描具体怎么使用系统框架AVFoundation来实现,下面本人收藏的简书文章,写的灰常清楚

http://www.jianshu.com/p/6b7d54b3f88b

iOS宏定义的使用与规范

宏定义在很多方面都会使用,例如定义高度、判断iOS系统、工具类,还有诸如文件路径、服务端api接口文档。为了对宏能够快速定位和了解其功能,我们最好在定义的时候将其放入特定的头文件中,下面我抛砖引玉,对一些常用的宏进行分类、分文件定义,希望对大家有所帮助。

1.定义尺寸类的宏

DimensMacros.h

//状态栏高度
#define STATUS_BAR_HEIGHT 20
//NavBar高度
#define NAVIGATION_BAR_HEIGHT 44
//状态栏 + 导航栏 高度
#define STATUS_AND_NAVIGATION_HEIGHT ((STATUS_BAR_HEIGHT) + (NAVIGATION_BAR_HEIGHT))

//屏幕 rect
#define SCREEN_RECT ([UIScreen mainScreen].bounds)

#define SCREEN_WIDTH ([UIScreen mainScreen].bounds.size.width)

#define SCREEN_HEIGHT ([UIScreen mainScreen].bounds.size.height)

#define CONTENT_HEIGHT (SCREEN_HEIGHT - NAVIGATION_BAR_HEIGHT - STATUS_BAR_HEIGHT)

//屏幕分辨率
#define SCREEN_RESOLUTION (SCREEN_WIDTH * SCREEN_HEIGHT * ([UIScreen mainScreen].scale))


//广告栏高度
#define BANNER_HEIGHT 215

#define STYLEPAGE_HEIGHT 21

#define SMALLTV_HEIGHT 77

#define SMALLTV_WIDTH 110

#define FOLLOW_HEIGHT 220

#define SUBCHANNEL_HEIGHT 62

2.定义沙盒目录文件的宏

PathMacros.h

//文件目录
#define kPathTemp                   NSTemporaryDirectory()
#define kPathDocument               [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]
#define kPathCache                  [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0]
#define kPathSearch                 [kPathDocument stringByAppendingPathComponent:@"Search.plist"]

#define kPathMagazine               [kPathDocument stringByAppendingPathComponent:@"Magazine"]
#define kPathDownloadedMgzs         [kPathMagazine stringByAppendingPathComponent:@"DownloadedMgz.plist"]
#define kPathDownloadURLs           [kPathMagazine stringByAppendingPathComponent:@"DownloadURLs.plist"]
#define kPathOperation              [kPathMagazine stringByAppendingPathComponent:@"Operation.plist"]

#define kPathSplashScreen           [kPathCache stringByAppendingPathComponent:@"splashScreen"]
#endif

3.工具类的宏

UtilsMacros.h

//Log utils marco

#define ALog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);

#ifdef DEBUG
#define DLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);
#else
#define DLog(...)
#endif

#ifdef DEBUG
#define ULog(...)
//#define ULog(fmt, ...)  { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:[NSString stringWithFormat:@"%s\n [Line %d] ", __PRETTY_FUNCTION__, __LINE__] message:[NSString stringWithFormat:fmt, ##__VA_ARGS__]  delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil]; [alert show]; }
#else
#define ULog(...)
#endif


//System version utils

#define SYSTEM_VERSION_EQUAL_TO(v)                  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedSame)
#define SYSTEM_VERSION_GREATER_THAN(v)              ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending)
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v)  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN(v)                 ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v)     ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedDescending)


// 获取RGB颜色
#define RGBA(r,g,b,a) [UIColor colorWithRed:r/255.0f green:g/255.0f blue:b/255.0f alpha:a]
#define RGB(r,g,b) RGBA(r,g,b,1.0f)


#define IsPortrait ([UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationPortrait || [UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationPortraitUpsideDown)


#define IsNilOrNull(_ref)   (((_ref) == nil) || ([(_ref) isEqual:[NSNull null]]))


//角度转弧度
#define DEGREES_TO_RADIANS(d) (d * M_PI / 180)

//大于等于7.0的ios版本
#define iOS7_OR_LATER SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0")

//大于等于8.0的ios版本
#define iOS8_OR_LATER SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0")

//iOS6时,导航VC中view的起始高度
#define YH_HEIGHT (iOS7_OR_LATER ? 64:0)

//获取系统时间戳
#define getCurentTime [NSString stringWithFormat:@"%ld", (long)[[NSDate date] timeIntervalSince1970]]

4.通知Notification相关的宏

NotificationMacros.h
//系统Notification定义

#define TNCancelFavoriteProductNotification     @"TNCancelFavoriteProductNotification"      //取消收藏时
#define TNMarkFavoriteProductNotification       @"TNMarkFavoriteProductNotification"        //标记收藏时

#define kNotficationDownloadProgressChanged     @"kNotficationDownloadProgressChanged"      //下载进度变化
#define kNotificationPauseDownload              @"kNotificationPauseDownload"               //暂停下载
#define kNotificationStartDownload              @"kNotificationStartDownload"               //开始下载

#define kNotificationDownloadSuccess            @"kNotificationDownloadSuccess"             //下载成功
#define kNotificationDownloadFailed             @"kNotificationDownloadFailed"              //下载失败
#define kNotificationDownloadNewMagazine        @"kNotificationDownloadNewMagazine"

服务端API接口的宏

APIStringMacros.h
//////////////////////////////////////////////////////////////////////////////////////////////////
//接口名称相关

#ifdef DEBUG
//Debug状态下的测试API
#define API_BASE_URL_STRING     @"http://boys.test.companydomain.com/api/"

#else
//Release状态下的线上API
#define API_BASE_URL_STRING     @"http://www.companydomain.com/api/"

#endif

//接口
#define GET_CONTENT_DETAIL      @"channel/getContentDetail" //获取内容详情(含上一个和下一个)

#define GET_COMMENT_LIST        @"comment/getCommentList"   //获取评论列表

#define COMMENT_LOGIN           @"comment/login"            //获取评论列表

#define COMMENT_PUBLISH         @"comment/publish"          //发布评论

#define COMMENT_DELETE          @"comment/delComment"       //删除评论

#define LOGINOUT                @"common/logout"            //登出

还有很多其他类型的宏,此处不一一列举

创建一个import所有宏相关的文件Macros.h

Macros.h
#import "UtilsMacros.h"
#import "APIStringMacros.h"
#import "DimensMacros.h"
#import "NotificationMacros.h"
#import "SharePlatformMacros.h"
#import "StringMacros.h"
#import "UserBehaviorMacros.h"
#import "PathMacros.h"

在xcode项目的pch文件中,导入Macros.h文件

XcodeProjectName-Prefix.pch
#ifdef __OBJC__
    #import <UIKit/UIKit.h>
    #import <Foundation/Foundation.h>
    #import "Macros.h"
#endif

iOS开发之block使用详解

苹果自从iOS4.0开始推出block,方便开发者使用,现代ios应用的开发,不使用GCD和block,效率会降低很多。在编程过程中,blocks被Obj-C看成是对象,它封装了一段代码,这段代码可以在任何时候执行。Blocks可以作为函数参数或者函数的返回值,而其本身又可以带输入参数或返回值。它和传统的函数指针很类似,但是有区别:blocks是inline的,并且它对局部变量是只读的。

下面来看看block到底是什么鬼:

  1. block的定义

    // 声明和实现写在一起,就像变量的声明实现 int a = 10;
    int (^aBlock)(int, int) = ^(int num1, int num2) {
            return num1 * num2;
           };
    // 声明和实现分开,就像变量先声明后实现 int a;a = 10;
    int (^cBlock)(int,int);
     cBlock = ^(int num1,int num2)
    {
        return num1 * num2;
    };
    //其中,定义了一个名字为aBlock的blocks对象,并携带了相关信息:
    
      1. aBlock 有两个形式参数,分别为int类型;
    
      2. aBlock 的返回值为int 类型;
    
      3. 等式右边就是blocks的具体实现;
    
      4. ^ 带边blocks声明和实现的标示(关键字);
    
    //当然,你可以定义其他形式的block:无返回值,无形式参数等;
    
    void (^bBlock)() = ^()
    {
        int a = 10;
        printf(num = %d,a);
    };
    
  2. blocks 访问权限

    //1.blocks可以访问局部变量,但是不能修改。
    int a = 10;
    int (^dBlock)(int) = ^(int num)
    {
        a++;//not work!
        return num * a;
    };
    //此处不能修改的原因是在编译期间确定的,编译器编译的时候把a的值复制到block作为一个新变量(假设是a‘ = 10),此时a'和a是没有关系的。
    
    //2.这个地方就是函数中的值传递。如果要修改就要加关键字:__block或者static
     __block int a = 7;
     int (^dBlock)(int) = ^(int num)
     {
         a++;// work!
         return num * a;
     };
    
  3. block的调用

    //1.block调用就像调用函数一样。e.g:
    int c = aBlock(10,10);
    bBlock();
    
  4. block 应用

  1. block返回对象以及参数是对象的使用
  
  头文件如下,实现文件中不需要实现任何代码。

#import <Foundation/Foundation.h>
typedef int(^compareBlock)(int a, int b);
@interface HBTestBlock : NSObject

//注意以下两种方式的不同
@property(nonatomic, copy) compareBlock compare;
@property(nonatomic, copy) UIView *(^viewGetter)(NSString *imageName); //注意其返回类型为UIView *
@end

下面这两个函数,展示的是如何在其他的类中,使用这两个属性。

#pragma mark 测试对象的属性为block
- (void)testObjPropertyBlock
{
    HBTestBlock *objPropertyBlockObj = [[HBTestBlock alloc] init];
    objPropertyBlockObj.viewGetter = ^(NSString *imageName){
//        return [[UIView alloc] init]; //特别注意此处,若对象不匹配,则会报错,设置为nil也会报错。
        return [self currentView];
    };
    objPropertyBlockObj.viewGetter(@"hello"); //实际执行block
}

- (UIView *)currentView
{
    NSLog(@"now I am in currentView");
    return nil;
}

- (void)testPropertyBlock
{
    HBTestBlock *properBlockObj = [[HBTestBlock alloc] init];
    properBlockObj.compare = ^(int a,int b)
    {
        int result = [self maxer:a another:b];
        NSLog(@"the result is %d",result);
        return result;
    };
    NSLog(@"the properBlockObj.compare is %d",properBlockObj.compare(100,200));
}

- (int)maxer:(int)a another:(int)b
{
    if (a > b) {
        return a;
    }
    return  b;
}

  

2. Block作为property属性实现页面之间传值

假设我们熟悉代理递值的话,对代理我们可能又爱有恨!我们先建立模型A页面 push B页面,如果把A页面的值传递到B页面,属性和单例传值可以搞定!但是如果Pop过程中把B页面的值传递到A页面,那就可以用单例或者代理了!说到代理,我们要先声明协议,创建代理,很是麻烦。常常我们传递一个数值需要在两个页面间写很多代码,这些代码改变页面的整体顺序,可读性也打了折扣。所以,此时,block是一种优化方案!

需求:在ViewController中,点击Button,push到下一个页面NextViewController,在NextViewController的输入框TextField中输入一串字符,返回的时候,在ViewController的Label上面显示文字内容,

(1)第一种方法:首先看看通过“协议/代理”是怎么实现两个页面之间传值的吧,

//NextViewController是push进入的第二个页面
//NextViewController.h 文件
//定义一个协议,前一个页面ViewController要服从该协议,并且实现协议中的方法
@protocol NextViewControllerDelegate <NSObject>
- (void)passTextValue:(NSString *)tfText;
@end

@interface NextViewController : UIViewController
@property (nonatomic, assign) id<NextViewControllerDelegate> delegate;

@end

//NextViewController.m 文件
//点击Button返回前一个ViewController页面
- (IBAction)popBtnClicked:(id)sender {
    if (self.delegate && [self.delegate respondsToSelector:@selector(passTextValue:)]) {
        //self.inputTF是该页面中的TextField输入框
        [self.delegate passTextValue:self.inputTF.text];
    }
    [self.navigationController popViewControllerAnimated:YES];
}
接下来我们在看看ViewController文件中的内容,

//ViewController.m 文件
@interface ViewController ()<NextViewControllerDelegate>
@property (strong, nonatomic) IBOutlet UILabel *nextVCInfoLabel;

@end
//点击Button进入下一个NextViewController页面
- (IBAction)btnClicked:(id)sender
{
    NextViewController *nextVC = [[NextViewController alloc] initWithNibName:@"NextViewController" bundle:nil];
    nextVC.delegate = self;//设置代理
    [self.navigationController pushViewController:nextVC animated:YES];
}

//实现协议NextViewControllerDelegate中的方法
#pragma mark - NextViewControllerDelegate method
- (void)passTextValue:(NSString *)tfText
{
    //self.nextVCInfoLabel是显示NextViewController传递过来的字符串Label对象
    self.nextVCInfoLabel.text = tfText;
}
这是通过“协议/代理”来实现的两个页面之间传值的方式。

(2)第二种方法:使用Block作为property,实现两个页面之间传值,

先看看NextViewController文件中的内容,

//NextViewController.h 文件
@interface NextViewController : UIViewController
@property (nonatomic, copy) void (^NextViewControllerBlock)(NSString *tfText);

@end
//NextViewContorller.m 文件
- (IBAction)popBtnClicked:(id)sender {
    if (self.NextViewControllerBlock) {
        self.NextViewControllerBlock(self.inputTF.text);
    }
    [self.navigationController popViewControllerAnimated:YES];
}
再来看看ViewController文件中的内容,

- (IBAction)btnClicked:(id)sender
{
    NextViewController *nextVC = [[NextViewController alloc] initWithNibName:@"NextViewController" bundle:nil];
    __weak __typeof(self) weakSelf = self;
    nextVC.NextViewControllerBlock = ^(NSString *tfText){
        [weakSelf resetLabel:tfText];
    };
    [self.navigationController pushViewController:nextVC animated:YES];
}
#pragma mark - NextViewControllerBlock method
- (void)resetLabel:(NSString *)textStr
{
    self.nextVCInfoLabel.text = textStr;
}

好了就这么多代码,可以使用Block来实现两个页面之间传值的目的,实际上就是取代了Delegate的功能。

  1. block的内存管理

    block本身是像对象一样可以retain,和release。但是,block在创建的时候,它的内存是分配在栈(stack)上,而不是在堆(heap)上。他本身的作于域是属于创建时候的作用域,一旦在创建时候的作用域外面调用block将导致程序崩溃。比如下面的例子。 我在view did load中创建了一个block:

    - (void)viewDidLoad
    {
    [superviewDidLoad];
    
    int number = 1;
    _block = ^(){
    
    NSLog(@number %d, number);
    };
    }
    并且在一个按钮的事件中调用了这个block:
    
    - (IBAction)testDidClick:(id)sender {
    _block();
    }
    

此时我按了按钮之后就会导致程序崩溃,解决这个问题的方法就是在创建完block的时候需要调用copy的方法。copy会把block从栈上移动到堆上,那么就可以在其他地方使用这个block了~ 修改代码如下:

_block = ^(){
NSLog(@number %d, number);
};

_block = [_blockcopy];

同理,特别需要注意的地方就是在把block放到集合类当中去的时候,如果直接把生成的block放入到集合类中,是无法在其他地方使用block,必须要对block进行copy。不过代码看上去相对奇怪一些:

[array addObject:[[^{
NSLog(@hello!);
} copy] autorelease]];
  1. 循环引用

对于非ARC下, 为了防止循环引用, 我们使用block来修饰在Block中使用的对象:
对于ARC下, 为了防止循环引用, 我们使用
weak来修饰在Block中使用的对象。原理就是:ARC中,Block中如果引用了__strong修饰符的自动变量,则相当于Block对该变量的引用计数+1。
这一点其实是在第一点的一个小的衍生。当在block内部使用成员变量的时候,比如

@interface ViewController : UIViewController
{
NSString *_string;
}
@end

在block创建中:

_block = ^(){
NSLog(@string %@, _string);
};

这里的_string相当于是self->_string;那么block是会对内部的对象进行一次retain。也就是说,self会被retain一次。当self释放的时候,需要block释放后才会对self进行释放,但是block的释放又需要等self的dealloc中才会释放。如此一来变形成了循环引用,导致内存泄露。

修改方案是新建一个block scope的局部变量,并把self赋值给它,而在block内部则使用这个局部变量来进行取值。因为block标记的变量是不会被自动retain的。

__block ViewController *controller = self;
_block = ^(){
NSLog(@string %@, controller->_string);
};

Objective-c单例模式的几种写法有什么不同

#Objective-c单例模式详解

单例模式出现以后,关于它的争执就一直存在。在开发项目中,有很多时候我们需要一个全局的对象,而且要保证全局有且仅有一份即可。没错,单例在这个时候就是最佳的选择,但是需要注意的是:在多线程的环境下也需要做好线程保护。其实系统已经有很多单例存在,例如UIApplication、NSNotification、NSFileManager等等就是很不错的例子——我们总有时候需要用到单例模式,不过写起代码来还是需要考虑考虑。

  1. 我们先来看一个最简单的单例,假设我们有一个testClass的类需要实现单例:

    + (id)sharedInstance {  
        static testClass *sharedInstance = nil;  
        if (!sharedInstance) {  
            sharedInstance = [[self alloc] init];  
        }  
        return sharedInstance;  
    }  
    
  2. 熟悉单例的童鞋一眼就能看出,这里根本没有考虑线程安全的问题,需要加上线程锁。

    + (id)sharedInstance {  
        static testClass *sharedInstance = nil;  
        @synchronized(self) {  
            if (!sharedInstance) {  
                sharedInstance = [[self alloc] init];  
            }  
        }  
        return sharedInstance;  
    }  
    
  3. 这是很常见的写法。不过,在GCD推出后,有个dispatch_once方法,可以使单例的实现更加容易,dispatch_once的函数原型如下:

    void dispatch_once(dispatch_once_t *predicate, dispatch_block_t block);  
    

    我们可以看到这个函数接收一个dispatch_once_t的参数,还有一个块参数。对于一个给定的predicate 来说,该函数会保证相关的块必定会执行,而且只执行一次,最重要的是——这个方法是完全线程安全的。需要 注意的是,对于只需要执行一次的块来说,传入的predicate必须是完全相同的,所以predicate常常会用 static或者global来修饰。

    + (id)sharedInstance {  
        static testClass *sharedInstance = nil;  
        static dispatch_once_t once;  
        dispatch_once(&once, ^{  
            sharedInstance = [[self alloc] init];  
        });  
        return sharedInstance;  
    }  
    
  4. 我们知道,创建对象的步骤分为申请内存(alloc)、初始化(init)这两个步骤,我们要确保对象的唯一性,因此在第一步这个阶段我们就要拦截它。当我们调用alloc方法时,OC内部会调用allocWithZone这个方法来申请内存,我们重写这个方法,然后在这个方法中调用shareInstance方法返回单例对象,这样就可以达到我们的目的。拷贝对象也是同样的原理,重写copyWithZone方法,然后在这个方法中调用shareInstance方法返回单例对象。

下面来看看两组例子:

一般写法:

#import <Foundation/Foundation.h>

@interface SingleClass : NSObject

+(instancetype) shareInstance ;

@end


#import "SingleClass.h"

@implementation SingleClass

static SingleClass *_sharedInstance = nil;

+(instancetype) shareInstance
{
    static dispatch_once_t onceToken ;
    dispatch_once(&onceToken, ^{
        _sharedInstance = [[self alloc] init] ;
    }) ;

    return _sharedInstance ;
}

@end

具体使用,ViewController:

#import "ViewController.h"
#import "SingleClass.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    NSLog(@"开始《《《");
    SingleClass *obj1 = [SingleClass shareInstance] ;
    NSLog(@"obj1 = %@.", obj1) ;

    SingleClass *obj2 = [SingleClass shareInstance] ;
    NSLog(@"obj2 = %@.", obj2) ;

    SingleClass *obj3 = [[SingleClass alloc] init] ;
    NSLog(@"obj3 = %@.", obj3) ;

    NSLog(@"结束》》》");
}

@end

输出结果为 :

2016-04-11 15:49:29.494 aotulayoutDemo[7267:202275] 开始《《《
2016-04-11 15:49:29.495 aotulayoutDemo[7267:202275] obj1 = <SingleClass: 0x7f901142d660>.
2016-04-11 15:49:29.495 aotulayoutDemo[7267:202275] obj2 = <SingleClass: 0x7f901142d660>.
2016-04-11 15:49:29.495 aotulayoutDemo[7267:202275] obj3 = <SingleClass: 0x7f901160e3a0>.
2016-04-11 15:49:29.495 aotulayoutDemo[7267:202275] 结束》》》

在这里可以看到,当我们调用shareInstance方法时获取到的对象是相同的,但是当我们通过alloc和init来构造对象的时候,得到的对象却是不一样的。我们通过不同的途径得到不同的对象,显然是不行的。我们必须要确保对象的唯一性,所以我们就需要封锁用户通过alloc和init以及copy来构造对象这条道路。

下面来看看严谨的写法:

#import "Singleton.h"

@implementation Singleton

static Singleton* _instance = nil;

+(instancetype) shareInstance
{
    static dispatch_once_t onceToken ;
    dispatch_once(&onceToken, ^{
        _instance = [[super allocWithZone:NULL] init] ;
    }) ;

    return _instance ;
}

+(id) allocWithZone:(struct _NSZone *)zone
{
    return [Singleton shareInstance] ;
}

-(id) copyWithZone:(struct _NSZone *)zone
{
    return [Singleton shareInstance] ;
}

@end

再看看效果如何,ViewController:

#import "ViewController.h"
#import "SingleClass.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    NSLog(@"开始《《《");
    SingleClass *obj1 = [SingleClass shareInstance] ;
    NSLog(@"obj1 = %@.", obj1) ;

    SingleClass *obj2 = [SingleClass shareInstance] ;
    NSLog(@"obj2 = %@.", obj2) ;

    SingleClass *obj3 = [[SingleClass alloc] init] ;
    NSLog(@"obj3 = %@.", obj3) ;

    SingleClass* obj4 = [[SingleClass alloc] init] ;
    NSLog(@"obj4 = %@.", [obj4 copy]) ;

    NSLog(@"结束》》》");
}

@end

输出结果:

2016-04-11 15:56:27.261 aotulayoutDemo[7373:205889] 开始《《《
2016-04-11 15:56:27.263 aotulayoutDemo[7373:205889] obj1 = <SingleClass: 0x7fd9e261d690>.
2016-04-11 15:56:27.263 aotulayoutDemo[7373:205889] obj2 = <SingleClass: 0x7fd9e261d690>.
2016-04-11 15:56:27.264 aotulayoutDemo[7373:205889] obj3 = <SingleClass: 0x7fd9e261d690>.
2016-04-11 15:56:27.264 aotulayoutDemo[7373:205889] obj4 = <SingleClass: 0x7fd9e261d690>.
2016-04-11 15:56:27.264 aotulayoutDemo[7373:205889] 结束》》》

这里我们可以看到,获取到的对象都是一样的了。

Cookie机制以及cookie在iOS中使用介绍

最近在iOS开发中遇到了cookie的问题,经过多番查找资料,终于对cookie有了一些了解,并整理了以下资料。

Cookie机制:

Cookie是在客户端存储服务器状态的一种机制,Web服务器可以通过Set-Cookie或者Set-Cookie2 HTTP头部设置Cookie。
Cookie是在浏览器访问WEB服务器的某个资源时,由WEB服务器在HTTP响应消息头中附带传送给浏览器的一个小文本文件。一旦WEB浏览器保存了某个Cookie,那么它在以后每次访问该WEB服务器时,都会在HTTP请求头中将这个Cookie回传给WEB服务器。

底层的实现原理: WEB服务器通过在HTTP响应消息中增加Set-Cookie响应头字段将Cookie信息发送给浏览器,浏览器则通过在HTTP请求消息中增加Cookie请求头字段将Cookie回传给WEB服务器。
一个Cookie只能标识一种信息,它至少含有一个标识该信息的名称(NAME)和设置值(VALUE)。
一个WEB站点可以给一个WEB浏览器发送多个Cookie,一个WEB浏览器也可以存储多个WEB站点提供的Cookie。
浏览器一般只允许存放300个Cookie,每个站点最多存放20个Cookie,每个Cookie的大小限制为4KB。

Cookie可以分为两类,会话Cookie和持久Cookie,会话Cookie是临时Cookie,当前会话结束(浏览器退出)时Cookie会被删除。持久Cookie会存储在用户的硬盘上,浏览器退出,然后重新启动后Cookie仍然存在。会话Cookie和持久Cookie的区别在于过期时间,如果设置了Discard参数(Cookie 版本1)或者没有设置Expires(Cookie版本0)或Max-Age(Cookie版本1)设置过期时间,则此Cookie为会话Cookie
Cookie有两个版本,一个是版本0(Netscape Cookies)和版本1(RFC 2965),目前大多数服务器使用的Cookie 0。

当然,cookie也应用到了我们iOS中,下面介绍一下iOS中的cookie:

一. NSHTTPCookie

  1. 在iOS中使用NSHTTPCookie类封装一条cookie,通过NSHTTPCookie的方法读取到cookie的通用属性。

    - (NSUInteger)version;
    - (NSString *)name;
    - (NSString *)value;
    - (NSString *)domain;
    - (NSString *)path;
    - (BOOL)isSessionOnly;等等
    
  2. 可以通过手工赋值的方式创建Cookie,如

    + (id)cookieWithProperties:(NSDictionary *)properties;
    - (id)initWithProperties:(NSDictionary *)properties;
    
  3. 也可以从Cookie中读取到所有属性。

    - (NSDictionary *)properties;
    
  4. 使用NSHTTPCookie的类方法可以将NSHTTPCookie实例与HTTP cookie header相互转换.

    根据NSHTTPCookie实例数组生成对应的HTTP cookie header
    + (NSDictionary *)requestHeaderFieldsWithCookies:(NSArray *)cookies;
    
    从headerFileds中读取到Cookie相关内容,生成NSHTTPCookie实例对象数组。
    + (NSArray *)cookiesWithResponseHeaderFields:(NSDictionary *)headerFields forURL:(NSURL *)theURL;
    
    该方法会忽略headerFileds中与cookie无关的字段,如果headerFileds中的cookie没有指定domain,则使用theURL的domain,如果没有指定path,则使用”/”.
    
    除非NSURLRequest明确指定不使用cookie(HTTPShouldHandleCookies设为NO),否则URL loading system会自动为NSURLRequest发送合适的存储cookie。从NSURLResponse返回的cookie也会根据当前的cookie访问策略(cookie acceptance policy)接收到系统中。
    

二. NSHTTPCookieStorage

  1. NSHTTPCookieStorage单件类提供了管理所有NSHTTPCookie对象的接口,在OS X里,cookie是在所有程序中共享的,而在iOS中,cookie只当当前应用中有效。

    通过sharedHTTPCookieStorage方法可获取到共享的NSHTTPCookieStorage单件对象。
    + (NSHTTPCookieStorage *)sharedHTTPCookieStorage;
    
    使用NSHTTPCookieStorage单件对象可获取到当前存储的所有cookie
    - (NSArray *)cookies
    
    或针对特定URL的cookie
    - (NSArray *)cookiesForURL:(NSURL *)theURL;
    
    还可以添加/删除Cookie
    – deleteCookie:
    – setCookie:
    – setCookies:forURL:mainDocumentURL:
    
    通过NSHTTPCookieStorage可读取/修改cookie接收策略,默认为NSHTTPCookieAcceptPolicyAlways.
    - (NSHTTPCookieAcceptPolicy)cookieAcceptPolicy;
    - (void)setCookieAcceptPolicy:(NSHTTPCookieAcceptPolicy)aPolicy.
    
    一共有三种cookie accept policy,
    typedef enum {
       NSHTTPCookieAcceptPolicyAlways,
       NSHTTPCookieAcceptPolicyNever,
       NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain
    } NSHTTPCookieAcceptPolicy;
    
    NSHTTPCookieAcceptPolicyAlways:接收所有cookie,默认策略.
    NSHTTPCookieAcceptPolicyNever: 拒绝所有cookie
    NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain:只接收main document domain中的cookie.
    
  2. 相关通知Notification

    NSHTTPCookieManagerCookiesChangedNotificationNSHTTPCookieStorage实例中的cookies变化时发出此通知。
    NSHTTPCookieManagerAcceptPolicyChangedNotificationNSHTTPCookieStorage实例的cookie acceptance policy变化时发出此通知。
    

三. cookie的使用

当你利用一个URL请求时,NSURLRequest都会帮你主动记录下来你访问的站点设置的cookie(因为NSHTTPCookieStorage可读取/修改cookie接收策略,默认为NSHTTPCookieAcceptPolicyAlways.当然你也可以给它设置为NSHTTPCookieAcceptPolicyNever: 拒绝所有cookie),而且很负责任的,当你下次再访问这个站点时,NSURLRequest会拿着上次保存下来了的cookie继续去请求。这规律同样适用于 ASIHTTPRequest。所以当你做一些基于认证的网络请求时,cookie不失为一个完美的解决方案。

  1. 怎么查看cookie?

    NSHTTPCookieStorage *cookieJar = [NSHTTPCookieStorage sharedHTTPCookieStorage];
    for (NSHTTPCookie *cookie in [cookieJar cookies]) {
           NSLog(@"%@", cookie);
    }
    这样就列出了所有已保存的cookie,如果当前为空怎么办呢?随便请求一个url喽。
    NSURLRequest *request = [NSURLRequest requestWithURL:
    [NSURL URLWithString:@"http://blog.cnrainbird.com"] cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData    timeoutInterval:3];
    [NSURLConnection sendSynchronousRequest:request 
                          returningResponse:nil 
                                         error:nil]; 
    
    NSHTTPCookieStorage *cookieJar = [NSHTTPCookieStorage sharedHTTPCookieStorage];
    for (NSHTTPCookie *cookie in [cookieJar cookies]) {
        NSLog(@"%@", cookie);
    }
    
    是不是得到了类似:
    <NSHTTPCookie version:0 name:"PHPSESSID" value:"evf5rcboo8th1dnl53fs4ukmt2" expiresDate:(null) created:2012-03-13 14:28:13 +0000 (3.53342e+08) sessionOnly:TRUE domain:"blog.cnrainbird.com" path:"/" isSecure:FALSE>
    的东东?这就是cookie啦
    
  2. 怎么清空cookie?

    NSHTTPCookieStorage *cookieJar = [NSHTTPCookieStorage sharedHTTPCookieStorage];
    NSArray *_tmpArray = [NSArray arrayWithArray:[cookieJar cookies]];
    for (id obj in _tmpArray) {
        [cookieJar deleteCookie:obj];
    }
    这样cookie就消失的一干二净了。
    
  3. 会查看cookie了,也会清空cookie了,怎么设置指定的cookie呢?

    NSMutableDictionary *cookieProperties = [NSMutableDictionary dictionary];
    [cookieProperties setObject:@"username" forKey:NSHTTPCookieName];
    [cookieProperties setObject:@"rainbird" forKey:NSHTTPCookieValue];
    [cookieProperties setObject:@"cnrainbird.com" forKey:NSHTTPCookieDomain];
    [cookieProperties setObject:@"cnrainbird.com" forKey:NSHTTPCookieOriginURL];
    [cookieProperties setObject:@"/" forKey:NSHTTPCookiePath];
    [cookieProperties setObject:@"0" forKey:NSHTTPCookieVersion];
    
    NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:cookieProperties];
    [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
    
    这样就可以了。输入一下,是不是得到了下面这样的结果:
    
    <NSHTTPCookie version:0 name:"username" value:"rainbird" expiresDate:(null) crea
    

参考资料:

http://blog.csdn.net/chun799/article/details/17206907 iOS中Cookie介绍

http://blog.csdn.net/majiakun1/article/details/38140359 Cookie的使用

http://www.jianshu.com/p/65094611980c iOS平台下cookie的使用

http://blog.sina.com.cn/s/blog_884e78b20101htpx.html cookie 原理与在IOS中使用的注意点

iOS开发之指定UIView的某几个角(小于4)为圆角

在iOS开发中,我们经常会遇到View设置圆角的问题,如果需要将UIView的4个角全部都为圆角,做法相当简单,只需设置其Layer的cornerRadius属性即可(项目需要使用QuartzCore框架)。而若要指定某几个角(小于4)为圆角而别的不变时,这种方法就不好用了。如图:

Mou icon
Mou icon

对于后者这种情况,下面给出一种比较简单优雅的方案,就是使用UIBezierPath。

示例代码如下:

UIView *testView = [[UIView alloc] initWithFrame:CGRectMake(100, 10, 80, 80)];
testView.backgroundColor = [UIColor redColor];
[self.view addSubview:testView];

UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:testView.bounds byRoundingCorners:UIRectCornerBottomLeft | UIRectCornerBottomRight cornerRadii:CGSizeMake(10, 10)];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.frame = testView.bounds;
maskLayer.path = maskPath.CGPath;
testView.layer.mask = maskLayer;

其中,byRoundingCorners:UIRectCornerBottomLeft | UIRectCornerBottomRight
指定了需要成为圆角的角。该参数是UIRectCorner类型的,可选的值有:

  • UIRectCornerTopLeft
  • UIRectCornerTopRight
  • UIRectCornerBottomLeft
  • UIRectCornerBottomRight
  • UIRectCornerAllCorners

从名字很容易看出来代表的意思,使用“|”来组合就好了。