iOS开发经验(2)

目录

  1. KVC
  2. 观察者模式:KVO与通知
  3. 精度问题
  4. 三目运算符
  5. 点语法
1. KVC(Key-value coding)键值编码。用字符串动态去操作对象

其实现方法是使用字符串描述要更改的对象状态部分。通过Key名直接访问对象的属性,或者给对象的属性赋值。这样就可以在运行时动态在访问和修改对象的属性。而不是在编译时确定。

所有的对象都可以使用KVC
键统一是字符串,而值是不支持基本数据类型的,必须将值转换为NSNumber或者NSValue类型

操作对象的属性和对象属性的属性,访问变量的属性,即使该属性没有get,set方法也可以调用

Human *human = [[Human alloc]init];       
//将name属性设置为"holydancer"       
[human setValue:@"holydancer" forKey:@"name"];
//将human中的name属性取出  
NSString *nameOfHuman=[human valueForKey:@"name"];

KVC集合运算符

    ProductModel *model = [[ProductModel alloc]init];
    model.name = @"iMac";
    model.price = @18888;
    ProductModel *model1 = [[ProductModel alloc]init];
    model1.name = @"iphone";
    model1.price = @6999;
    NSArray *array = @[model,model1];

简单类型的集合操作符:返回 strings, numbers, dates,简单集合操作符作用于 array 或者 set 中相对于集合操作符右侧的属性。包括 @avg, @count, @max, @min, @sum.

NSString *name = [array valueForKeyPath:@"@count"];//返回集合中对象总数的 NSNumber 对象。操作符右边没有键路径。
NSNumber  *price_max = [array valueForKeyPath:@"@max.price"];//比较由操作符右边的键路径指定的属性值,并返回比较结果的最大值。最大值由指定的键路径所指对象的 compare: 方法决定
NSString *price_min = [array valueForKeyPath:@"@min.price"];//返回的是集合中的最小值
NSNumber  *price_sum = [array valueForKeyPath:@"@sum.price"];//属性值的总和
NSNumber  *price_avg = [array valueForKeyPath:@"@avg.price"];//转换为 double, 计算其平均值,返回该平均值的 NSNumber 对象。当均值为 nil 的时候,返回 0.

提示:你可以简单的通过把 self 作为操作符后面的 key path 来获取一个由 NSNumber 组成的数组或者集合的总值,例如对于数组 @[@(1), @(2), @(3)] 可使用 valueForKeyPath:@"@max.self" 来获取最大值。

NSLog(@"%@,%@,%@,%@,%@",name,price_max,price_min,price_sum,price_avg);

对象操作符,返回 NSArray 对象实例:对象操作符包括 @distinctUnionOfObjects 和 @unionOfObjects, 返回一个由操作符右边的 key path 所指定的对象属性组成的数组。其中 @distinctUnionOfObjects 会对数组去重,而 @unionOfObjects 不会。

    NSArray *unionOfObjects = [array valueForKeyPath:@"@unionOfObjects.name"]; // 1.
    NSArray *distinctUnionObjects = [array valueForKeyPath:@"@distinctUnionOfObjects.name"];  //2.
    NSLog(@"%@,%@",unionOfObjects,distinctUnionObjects);

数组和集合操作符,返回的是一个 array 或者 set 对象
数组和集合操作符作用对象是嵌套的集合,也就是说,是一个集合且其内部每个元素是一个集合。数组和集合操作符包括 @distinctUnionOfArrays,@unionOfArrays,@distinctUnionOfSets:
@distinctUnionOfArrays / @unionOfArrays 返回一个数组,其中包含这个集合中每个数组对于这个操作符右面指定的 key path 进行操作之后的值。 distinct 版本会移除重复的值。
@distinctUnionOfSets 和 @distinctUnionOfArrays 差不多, 但是它期望的是一个包含着 NSSet 对象的 NSSet ,并且会返回一个 NSSet 对象。因为集合不能包含重复的值,所以它只有 distinct 操作。

NSArray *array1 = @[model,model1];
NSArray *totalArray = @[array,array1];
NSArray *distinctUnionOfArrays = [totalArray valueForKeyPath:@"@distinctUnionOfArrays.name"];
NSArray *unionOfArrays = [totalArray valueForKeyPath:@"@unionOfArrays.name"];
NSLog(@"%@,%@",distinctUnionOfArrays,unionOfArrays);

注意: 如果操作符右侧 key path 指定的对象为 nil,那么返回的数组中会包含 NSNull 对象.

  • 四个主要方法
    必须手动将值类型转换成NSNumber或者NSValue类型,才能设置为value
- (void)setValue:(nullable id)value forKey:(NSString *)key; 
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;

valueForKey:总是返回一个id对象,如果原本的变量类型是值类型或者结构体,返回值会封装成NSNumber或者NSValue对象。

- (nullable id)valueForKey:(NSString *)key; 
- (nullable id)valueForKeyPath:(NSString *)keyPath; 
  • 嵌套数据,KEY为数组或者字典

    • 对于有序的容器,可以用下面的方法:
       - (NSMutableArray *)mutableArrayValueForKey:(NSString *)key;
    
    • 对于无序的容器,可以用下面的方法:
      - (NSMutableSet *)mutableSetValueForKey:(NSString *)key;
      
  • 键路径:嵌套数据中,使用KeyPath

  • KVC中处理异常情况

    • 如果某对象有一个常用数据类型,比如bool,在用setvalue:设置value的时候,需要实现 setNilValueForKey:(NSString *)key
  • 在用set value: 设置value的时候,如果该对象不存在该属性,比如bool,需要实现 - (void)setValue:(id)value forUndefinedKey:(NSString *)key

  • KVC使用场景

    • 动态地取值和设值:利用KVC动态的取值和设值是最基本的用途了。
  • 用KVC来访问和修改私有变量:利用KVC可以随意修改一个对象的属性和变量(即使是私有变量)
    对于类里的私有属性,Objective-C是无法直接访问的,但是KVC是可以的。

  • Model和字典转换

  • 利用KVC集合运算符,KVC可以通过运算符层次查找对象的属性;KVC获取值不仅可以返回一个数据,还可以将某一个属性的所有值,数据归类出来(B不一定是类,也可以是数组)

  • 利用KVC可以修改系统的只读变量,修改一些控件的内部属性
    这也是iOS开发中必不可少的小技巧。众所周知很多UI控件都由很多内部UI控件组合而成的,但是Apple度没有提供这访问这些空间的API,这样我们就无法正常地访问和修改这些控件的样式。而KVC在大多数情况可下可以解决这个问题。最常用的就是个性化UITextField中的placeHolderText了。

因为数据造成crash的原因大概几点:

  1. 使用字面量创建数组、字典,valuenil
  2. 使用KVC方法给数组字典赋值为nil
  3. null发送方法。其他的诸如null的判断方法及给控件赋值都不会引起crash。
2. 观察者模式:KVO与通知

iOS的一种设计模式, 观察者设计模式,依赖于 Objective-C 强大的 Runtime。观察者模式包含:

1.通知机制(notification)
2.KVO机制【可参考iOS--KVO的实现原理与具体应用】

  • 通知机制:
    委托机制是代理“一对一”的对象之间的通信,而通知机制是广播“一对多”的对象之间的通信。
//A类获取通知中心,并发送通知
[[NSNotificationCenter defaultCenter] postNotificationName:@"statusBarHidden" object:nil userInfo:dic];
//B类注册通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarHidden:) name:@"statusBarHidden" object:nil];
//释放所有通知
- (void)removeObserver:(id)observer;
//释放名称为aName的通知
- (void)removeObserver:(id)observer name:(nullable NSString *)aName object:(nullable id)anObject;
  • KVO
    KVO提供一种机制,指定一个被观察对象,当对象某个属性发生更改时,对象会获得通知,并作出相应处理。KVO这种编码方式使用起来很简单,很适用与model修改后,引发的UIVIew的变化这种情况,当更改属性的值后,监听对象会立即得到通知;当指定的对象的属性被修改后,对象就会接受到通知,****前提是执行了setter方法、或者使用了KVC赋值****。
    当指定的对象的属性被修改后,则对象就会接受到通知。简单的说就是每次指定的被观察的对象的属性被修改后,KVO就会自动通知相应的观察者了。

原理:

  • 当一个object有观察者时,动态创建这个object的类的子类
  • 对于每个被观察的property,重写其set方法
  • 在重写的set方法中调用- willChangeValueForKey:和- didChangeValueForKey:通知观察者
  • 当一个property没有观察者时,删除重写的方法
  • 当没有observer观察任何一个property时,删除动态创建的子类
    • 注册,指定被观察者的属性
[objc addObserver:self forKeyPath:@"title" options:
NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];
  • 实现回调方法
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{}
  • 移除观察
[self removeObserver:self forKeyPath:@"title" context:nil];
  • 两者区别:
    notification比KVO多了发送通知的一步。两者都是一对多,但是对象之间直接的交互,notification明显多,需要notificationCenter来做为中间交互。
    notification的优点是监听不局限于属性的变化,还可以对多种多样的状态变化进行监听,监听范围广,例如键盘、前后台等系统通知的使用也更显灵活方便.
3. 精度问题
  • 原因:
    NSNumberdescription方法不够严谨,在调用NSNumber的description方法打印数值时,会发生精度损失。
  • 建议 :
    • 如果是double类型,处理精度有关的数据用double。建议把NSNumber转换成double再进行输出(NSString)或计算(CGFloat);
    • 有关浮点型数据,后台传字符串的格式,防止丢失精度.
 NSNumber *value=dic[@"number"];
 NSLog(@"value:%@", value);

输出:value:81.59999999999999
如果是double类型,建议把NSNumber转换成double再进行输出或计算 。

CGFloat numberValue = [self doubleValue];
NSString *value=[NSString stringWithFormat:@"%g",[dic[@"number"] doubleValue]];
4. 三目运算符
  • 基本格式 : (关系表达式) ? 表达式1 : 表达式2;
  • 执行流程 : 关系表达式为 真 返回表达式1 关系表达式为假 返回表达式2
    int num1=8,num2=3,result=0;
    result= num1>num2?num1:num2;
    //因为num1>num2 成立  所以最后结果为num1的值
5. 点语法
  • 点语法的本质还是方法调用,当使用点语法时,编译器会自动展开成相应的方法,而不是访问成员变量.
  • 切记点语法的本质是转换成相应的对settergetter方法调用,如果没有setget方法,则不能使用点语法。
  • 不要在gettersetter方法中使用本属性的点语法

你可能感兴趣的:(iOS开发经验(2))