美文网首页
iOS开发经验(2)

iOS开发经验(2)

作者: Ryan___ | 来源:发表于2016-09-04 22:07 被阅读47次

    目录

    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<NSString *,id> *)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)

          本文链接:https://www.haomeiwen.com/subject/piblettx.html