iOS语法糖 简单却不那么简单

转载作者 香蕉大大 (Github

开发过程中我特别喜欢用语法糖,原因很简单,懒得看到一堆长长的代码,但是语法糖我今天无意中看到更有意思的玩法.这里暂时吧把今天新学到的知识点整理一下希望大家喜欢,如果有更好的补充希望能和我说下,我希望更更加好的完善。

语法糖(Syntactic sugar),也译为糖衣语法,是由英国计算机科学家彼得·约翰·兰达(Peter J. Landin)发明的一个术语,指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用。通常来说使用语法糖能够增加程序的可读性,从而减少程序代码出错的机会。

本文的目录如下

  • OC语法糖

    • 基础@[],@{}用法在NSArray,NSDictionary,NSNumber使用

    • !重点!@()使用

    • !重点!语法糖在UI中的使用方法

  • swift

    • 基础if let 与 guard 语法糖

    • 基础语法糖Selector例子

OC语法糖


常用基础使用部分@[],@{}这两部分的用法

1.一般数组的初始化和访问数组元素是这样的在之前的博客中我是这样初始化NSArray的:

1
2
3
4
//NSArray的便利初始化
  NSArray *array1 = [[NSArray alloc] initWithObjects:@ "aaa" , @ "bbb" , @ "ccc" , nil];
  //NSArray的便利构造器
  NSArray *array2 = [NSArray arrayWithObjects:@ "111" , @ "222" , @ "333" , nil];

获取数组的元素

1
2
3
//获取相应索引的元素
id element = [array1 objectAtIndex:0];
NSLog(@ "array1_count = %d, array[0] = %@" , count, element);

简化后的数组初始化和访问的做法如下

1
2
3
4
5
6
7
//NSArray的定义
NSArray *array = @[@ "lu" , @ "da" , @ "shi" , @YES, @123];
int  count = ( int )[array count];
for  ( int  i = 0; i < count; i++)
{
    NSLog(@ "%@" , array[i]);
}

2.对字典(NSDictionary)的简化也引用我之前博客中得一段代码吧

1
2
3
4
5
//不可变字典的初始化
NSDictionary *dictionay = [NSDictionary dictionaryWithObjectsAndKeys:@ "value1" , @ "key1"
@ "value2" , @ "key2" , nil];
id value = [dictionay objectForKey:@ "key1" ];
NSLog(@ "key1 => %@" , value);

我们还可以这样做

1
2
3
4
5
6
7
//NSDictionary的定义简化
NSDictionary *dictionary = @{
                              @ "key0"  : @ "value0" ,
                              @ "key1"  : @ "value1" ,
                              @ "key2"  : @ "value2"
                              };
NSLog(@ "key2 => %@" , dictionary[@ "key2" ]);

3.对NSNumber简化

我们可以这样做

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
把基本类型包装成对象的便利构造函数
 
-(id) initWithChar : ( char ) value;
 
-(id) initWithInt : ( int ) value;
 
-(id) initWithFloat : ( float ) value;
 
-(id) initWithBool: ( BOOL ) value;
 
把基本数据类型包装成对象的便利构造器
 
+(id) numberWithChar : ( char ) value;
 
+(id) numberWithInt : ( int ) value;
 
+(id) numberWithFloat : ( float ) value;
 
+(id) numberWithBool : ( BOOL ) value;

我们也可以这样做,说明:在char转换为NSNumber是存的是ASCII码的形式,c输出为97

1
2
3
4
5
//NSNumber的简化
NSNumber *a = @123;
NSNumber *b = @11.2;
NSNumber *c = @( 'a' );
NSLog(@ "a = %@, b = %@, c = %@" , a, b, c);

针对以上部分的简单练习

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
//NSNumber的语法糖
NSNumber *intNumber = [NSNumber numberWithInt:100];
NSNumber *intNumber2 = @100;
//不可变字符串的语法糖
NSString *string = @ "hanjunqiang" ;
//可变字符串的语法糖
NSMutableString *mString = @ "大爱中华" .mutableCopy; //后缀不能丢
 
//不可变数组的语法糖
NSArray *array = @[@ "1" ,@ "2" ,@ "3" ,@ "4" ];
NSLog(@ "%@" ,array);
 
//访问数组元素的语法糖
NSLog(@ "%@" ,array[1]);
 
//可变数组的语法糖
NSMutableArray *mArray = @[@ "1" ,@ "2" ,@ "3" ,@ "4" ].mutableCopy;
 
//字典的语法糖
//字典对象[key值]取出对应的value值
NSDictionary *dict = @{@ "a" :@ "1" ,@ "b" :@ "2" }; //key值在冒号前,value值在冒号后
NSLog(@ "%@" ,dict);
NSLog(@ "%@" ,dict[@ "a" ]);
//可变字典可以赋值和修改值
NSMutableDictionary *mDic = @{@ "a" :@ "1" ,@ "b" :@ "2" }.mutableCopy;
mDic[@ "a" ]=@ "100" ;
NSLog(@ "%@" ,mDic[@ "a" ]);

@()介绍以及使用

在 Objective-C 中我们可以用 @”foo” 来创建一个 NSString 常量,看起来似乎平淡无奇。
但它背后其实比想象的精彩,@ 可以被理解成一个特殊的宏,其接受一个 C 字符串作为参数,也可写作 @(“foo”)。
之所以说 @ 是一个特殊的宏,是因为其能根据传入的 C 字符串类型不同——C 字符串常量或 C 字符串——在运行时构建返回不同类型的 NSString,
参见下面的代码:

1
2
3
4
5
6
7
8
9
char * obtain_c_string( void )
{
     return  "c_string" ;
}
NSLog(@ "%@" , @ "foo" . class );
NSLog(@ "%@" , @( "bar" ). class );
NSLog(@ "%@" , @(obtain_c_string()). class );
NSLog(@ "%@" ,@ "aaa" );
NSLog(@ "%@" ,@( "aaa" ));

输出如下
__NSCFConstantString
__NSCFConstantString
__NSCFString
aaa
aaa

可见,如果传入的是 C 字符串常量,运行时构建的则为 NSConstantString;如果传入的是C 字符串则创建的是 NSString
众所周知,Objective-C 代码里有很多地方需要我们把代码中的一些文法串写成字符串再作为传入参数,比如 KVO 中的 keyPath 参数往往就要传入形如 propertyA.propertyB 的字符串,从实用角度出发这有两个弊端: 写字符串的时候没有代码提示,很容易写错 即便一开始写对了,如果后来相关类重构了,keyPath 的参数便失效了,而 Xcode Refactor 无法扫描字符串 当我们理解了 @(),再加上自定义的宏,上述两个问题便可迎刃而解。

1
2
3
4
5
6
7
/**
  * # 将宏的参数字符串化,C 函数 strchr 返回字符串中第一个 '.' 字符的位置
  */
#define Keypath(keypath) (strchr(#keypath, '.') + 1)
// 有代码提示,可以被重构扫描到
[objA addObserver: objB forKeyPath: @Keypath(ObjA.property1.property2) options: nil 
context: nil];

UI使用部分

拿UIImageView来做例子,原来创建对象和下面一致

1
2
3
4
5
self.imageView = [[UIImageView alloc] init];  
self.imageView.backgroundColor = [UIColor redColor];  
self.imageView.image = [UIImage imageNamed:@ "12345" ];  
self.imageView.frame = CGRectMake(0, 0, 100, 100);  
[self.view addSubview:self.imageView];

重点来了
这个就是语法糖在UI中创建对象的使用,瞬间感觉很高大上~

1
2
3
4
5
6
7
8
self.imageView = ({  
     UIImageView *imageView = [[UIImageView alloc] init];  
     imageView.backgroundColor = [UIColor redColor];  
     imageView.image = [UIImage imageNamed:@ "12345" ];  
     imageView.frame = CGRectMake(0, 0, 100, 100);  
     [self.view addSubview:imageView];  
     imageView;  
});

swift语法糖


if let 与 guard 语法糖

话说if let 和 guard 只是语法糖,没有也可以,但有了可以使得代码更简洁方便。要理解 if let 和 guard,不妨设想假如没有这两者,代码会怎么写。

1
2
3
4
5
6
7
8
9
if  let
func doSomething(str: String?)
{
     let v: String! = str
     if  v != nil
     {
         // use v to do something
     }
}

Swift 中因为有optional, 经常需要判断是否为空。假如没有if let,大致写成上面的样子,有了if let, 可以改写成

1
2
3
4
5
6
7
func doSomething(str: String?)
{
     if  let v = str
     {
         // use v to do something
     }
}

上面两段代码的控制流是一样的。对照着,可以看出if let的写法更加简洁方便。

1
2
3
4
5
6
7
8
9
10
11
12
guard
假如 if 中出现的代码很长,我们写代码时可以将错误情况先返回。比如:
func doSomething(str: String?)
{
     let v: String! = str
     if  v == nil
     {
         return
     }
 
     // use v to do something
}

这样做可以避免过多的嵌套。上面代码实在太常见了,swift也提供一个guard这个语法糖,用guard可以改写成:

1
2
3
4
func doSomething(str: String?){
     guard let v = str  else  return  }    
     // use v to do something
}

上面两段代码的控制流是一样的。也可以看出guard的写法更加简洁方便。

至于if let 和 guard 语法中出现的where,只是附加一些条件。相当于逻辑运算 && 和 ||。SQL中的条件语法也是用where这个关键字。

假如还不理解,动手将一段代码,不用if let 和 guard 进行改写。试多几次,就会发觉很自然了。

语法糖Selector例子

创建控制器,添加Button

1
2
3
4
5
6
7
8
9
10
11
12
override func viewDidLoad() {
     super.viewDidLoad()
 
     let btn = UIButton(frame: CGRectMake(100,100,100,100))
     btn.backgroundColor = UIColor.redColor()
     view.addSubview(btn)
 
}
 
func printFire() {
     print( "fire" )
}

想实现控制器里的Button点击事件,但是,如果一个控制器里的Button特别的多,那添加Button点击事件的Selector 会觉得特别的臃肿。所以语法糖就是要拓展Selector

1
2
3
4
5
private  extension Selector {
 
     static  let printFire = #selector(ViewController.printFire)
 
}

我们现在给viewDidLoad方法中的Button 添加点击事件

1
btn.addTarget(self, action: .printFire, forControlEvents: .TouchUpInside)

如果Button过多的话,用语法糖方便管理Button的点击事件,又不会把点击事件变得过于臃肿

下面是控制器的完整代码

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
import UIKit
 
class  ViewController: UIViewController {
 
     override func viewDidLoad() {
         super.viewDidLoad()
 
         let btn = UIButton(frame: CGRectMake(100,100,100,100))
         btn.backgroundColor = UIColor.redColor()
         btn.addTarget(self, action: .printFire, forControlEvents: .TouchUpInside)
         view.addSubview(btn)
 
 
     }
 
     func printFire() {
         print( "fire" )
     }
 
     override func didReceiveMemoryWarning() {
         super.didReceiveMemoryWarning()
         // Dispose of any resources that can be recreated.
     }
 
 
}
 
private  extension Selector {
 
     static  let printFire = #selector(ViewController.printFire)
 
}
 
 

猜你喜欢

转载自www.cnblogs.com/tangyuanby2/p/9245011.html
今日推荐