美文网首页
iOS 基础读书杂集(二)

iOS 基础读书杂集(二)

作者: 浮桥小麦 | 来源:发表于2017-06-19 10:54 被阅读23次
    没有废话直接来,传送门:

    iOS 基础读书杂集(一)

    NO.11 处理KVC:setValue赋值时,给属性赋值nil的问题
    当我们给引用数据类型赋值nil,不会出现问题,但是给int类型呢?
    举例:
    @interface JJTest : NSObject
    {
        //定义两个属性,一个string,一个int
        NSString *_name;
        NSInteger _age;
    }
    - (void)viewDidLoad {
        [super viewDidLoad];
        JJTest *test = [[JJTest alloc]init];
        //给这两个属性都赋值为nil
        [test setValue:nil forKey:@"_name"];
        [test setValue:nil forKey:@"_age"];
        NSLog(@"name = %@",[test valueForKey:@"_name"]);
        NSLog(@"age = %@",[test valueForKey:@"_age"]);
    }
    //崩溃报错:
    *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '[<JJTest 0x600000030e60> setNilValueForKey]: could not set nil as the value for the key _age.'
    
    //报错是因为:给基本数据类型赋值nil.
    //解决方法:
    上面的报错信息是因为在程序尝试给一个属性赋值为nil的时候,该属性又不接受nil值,那么程序会自动调用setNilValueForKey:方法
    那么我们可以重写这个方法来解决这个崩溃问题
    //重写setNilValueForKey:方法
    -(void)setNilValueForKey:(NSString *)key{
        //如果尝试将key为_age的属性设置为nil
        if ([key isEqualToString:@"_age"]) {
            //那么将_age直接设置为0
            _age = 0;
        }else{
            //其它情况依然回调父类的方法
            [super setNilValueForKey:key];
        }
    }
    //再次运行打印:
     name = (null)
     age = 0
    
    
    NO.12 KVO的简单模拟实现
    使用KVO的步骤:
    1.为被监听对象(通常是数据模型组件)注册监听器
    2.监听对象重写observeValueForKeyPath:ofObject:change:context方法
    举例:
    @interface JJTest1 : NSObject -- 相当于模型类
    /**<#注释#>*/
    @property (nonatomic , copy) NSString *name;
    /**<#注释#>*/
    @property (nonatomic , assign) NSInteger age;
    @end
    @class JJTest1;
    
    @interface JJTest : NSObject -- 相当于Cell类
    //在这里JJTest类相当于一个视图View. JJTest1相当于一个模型数据类
    /**test1*/
    @property (nonatomic , weak) JJTest1 *test1;
    //这个方法用于显示Model对象的状态
    -(void)showModelInfo;
    @end
    #import "JJTest.h"
    #import "JJTest1.h"
    @implementation JJTest
    
    //展示模型数据
    -(void)showModelInfo{
        NSLog(@"test1的名称为%@,年龄为%ld",self.test1.name,self.test1.age);
    }
    
    //重写setTest1方法
    //监听器的设置添加时机:当JJTest对象对test1属性设置值的时候,添加监听器
    -(void)setTest1:(JJTest1 *)test1{
        
        _test1 = test1;
        //为test1添加监听器,监听test1的name属性的改变
        //理解为:test1对象你的name属性被这个JJTest对象监听了,只要你这个name属性改变了。下面的observeValueForKeyPath:方法就会被调用
        [self.test1 addObserver:self forKeyPath:@"name" options: NSKeyValueObservingOptionNew context:nil];
        //监听test1的age属性改变
        [self.test1 addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];
    }
    //重写该方法,当被监听的数据模型发生改变时,就会回调监听器的该方法
    -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
        
        NSLog(@"observeValueForKeyPath:方法被调用");
        //获取修改时所设置的数据
        NSLog(@"被修改的keyPath为:%@",keyPath);
        NSLog(@"被修改的对象为:%@",object);
        NSLog(@"新被修改的属性值为:%@",[change objectForKey:@"new"]);
        NSLog(@"被修改的上下文为:%@",context);
        
    }
    
    -(void)dealloc{
      //移除监听器
        [self.test1 removeObserver:self forKeyPath:@"name"];
        [self.test1 removeObserver:self forKeyPath:@"age"];
     }
    
    //使用:
    - (void)viewDidLoad {
        [super viewDidLoad];
       //创建JJTest1对象
        JJTest1 *test1 = [[JJTest1 alloc]init];
        //设置test1的属性值
        test1.name = @"tmac";
        test1.age = 18;
        //解读:以上就相当于这个在cell中通过 JJModel *model = self.modelArr[indexPath.row];获取到了对应cell行的模型数据
        
        //创建JJTest对象
        JJTest *test = [[JJTest alloc]init];
        //将test的test1属性设置为test1
        test.test1 = test1;
        //解读:这个就相当于 cell.model = model.现在在这一瞬间我们还给model的每个属性添加了监听器
        
        //调用方法展示一下test1中的东西
        [test showModelInfo];
        
        //再次更改test1对象的属性,将会激发监听器方法
        test1.name = @"kobe";
        test1.age = 24;
       
        /*
        observeValueForKeyPath:方法被调用
         被修改的keyPath为:name
         被修改的对象为:<JJTest1: 0x60000003f9a0>
         新被修改的属性值为:kobe
         被修改的上下文为:(null)
         observeValueForKeyPath:方法被调用
         被修改的keyPath为:age
         被修改的对象为:<JJTest1: 0x60000003f9a0>
         新被修改的属性值为:24
         被修改的上下文为:(null)
         */
    }
    
    
    NO.13 对象初始化
    Objective-C的初始化是分为两步
    alloc:分配内存空间
    init:初始化对象
    
    Java: new xxx();构造器是一样的原理,只不过两步合成为一步了
    
    
    NO.14 便利初始化
    //原有初始化方法
    -(id)init;
    //便利初始化方法
    -(id)initWithName:(NSString *) age:(NSInteger)age;
    
    
    NO.15 重写父类方法
    1.子类重写父类方法,直接在类实现里重写父类方法的实现部分
    
    2.通过super关键字可以调用父类的实例方法
    //父类方法
    -(void)fly{
      NSLog(@"飞起来了");
    }
    //子类重写父类方法]
    -(void)fly{
      NSLog(@"我不会飞啊!");
    }
    //子类中单独写个方法,可以在里面用Super调用被覆盖的父类方法
    -(void)fatherMethod{
         [super fly];
    }
    
    
    NO.16 子类成员变量问题
    1.由于子类继承自父类,会获得父类中所有的成员变量。所以
    子类接口部分不允许定义与父类接口部分重名的成员变量
    
    2.类实现中定义的成员变量不受影响
    
    
    NO.17 多态
    概述: Objective-C 指针类型的变量有两个: 一个是编译时的类型,一个是运行时的类型。 
    编译时的类型由声明该变量时使用的类型决定
    运行时的类型由实际赋给该变量的对象决定
    如果编译时类型和运行时类型不一致,就可能出现所谓的多态
    
    
    NO.18 多态简单演示
    //定义一个父类
    @interface JJFather : NSObject
    //在父类定义两个方法
    -(void)method1;
    -(void)method2;
    @end
    @implementation JJFather
    //实现这个两个方法
    -(void)method1{
            NSLog(@"父类的普通method1方法");
    }
    -(void)method2{
           NSLog(@"父类即将被覆盖的method2方法");
    }
    @end
    
    //搞一个子类继承JJFather
    //继承自JJFather
    @interface JJSon : JJFather
    //子类自己的一个方法
    -(void)sonMethod;
    @end
    @implementation JJSon
    //子类独有的方法
    -(void)sonMethod{
        NSLog(@"子类独有的方法");
    }
    //子类覆盖父类的方法
    -(void)method2{
        NSLog(@"子类覆盖了父类的method2方法");
    }
    @end
    
    //多态演示:
    - (void)viewDidLoad {
        [super viewDidLoad];
       //演示多态
        //1.下面编译时类型和运行时类型完全一致,不存在多态
        JJFather *father = [[JJFather alloc]init];
        //调用父类自己的方法
        [father method1];
        [father method2];
        
        //2.下面编译时类型和运行时类型完全一致,不存在多态
        JJSon *son = [[JJSon alloc]init];
        //分别调用父类的方法和子类自己的方法
        [son method1];
        [son method2];
        [son sonMethod];
        
        //3.下面编译时类型和运行时类型不一致,多态产生
        //解释:[[JJSon alloc]init]:创建了一个JJSon的对象,然后由父类类型的引用指向这个对象
        //其实按照Java的说法是父类引用指向子类对象,进行了一次向上转型
        JJFather *fatherSon = [[JJSon alloc]init];
        //调用方法
        //(1).
        [fatherSon method1];//调用从父类继承的方法
        //(2).
        [fatherSon method2];//调用覆盖父类的方法
        //(3).调用子类特有的方法是不行的
        //[fatherSon sonMethod];
        /**解释:
         对于(2):编译时会看JJFather的类型,从而找到method2方法的声明。
         当运行时,总是看赋值对象的类型,所以运行时会找到子类重写父类后的method2方法的实现
         
         对于(3).你就发现在fatherSon指针变量调用这个sonMethod时,编译时在JJFather中找不到该方法的声明,所以直接会报编译错误。
         这就是多态...
         **/
    }
    //打印结果:
         test1[899:35039] 父类的普通method1方法
         test1[899:35039] 父类即将被覆盖的method2方法
         test1[899:35039] 父类的普通method1方法
         test1[899:35039] 子类覆盖了父类的method2方法
         test1[899:35039] 子类独有的方法
         test1[899:35039] 父类的普通method1方法
         test1[899:35039] 子类覆盖了父类的method2方法
    
    
    NO.19 Objective-C 包装类
    基本概念不做过多概念,我们只说注意点
    1. NSInteger / NSUInteger / CGFloat 
    这三个并不是包装类,依然是基本数据类型。 在64位平台和类似于64位平台的各种平台上,NSInteger-> long, NSUInteger-> unsigned long, CGFloat-> double. 所以为了更好的兼容不同的平台,当需要定义整型变量的时候,用这三个
    
    2. Objective-C虽然提供了类似于自动装箱的机制,把int型等直接赋值给NSNumber变量。但机制并不完善,使用自动装箱生成的NSNumber不支持ARC. 因此通常建议显式将基本类型的值包装成NSNumber对象
    基本类型变量-> [NSNumber numberWihtXxx:值]-> 包装类对象
    包装类对象-> [NSNumber对象 xxxValue]-> 基本类型变量
    
    3.为什么需要将基本类型包装为类对象:
    只有通过包装才能将基本类型放入数组,集合中.
    
    
    NO.20 重写description方法
    //创建一个测试类
    @interface Test : NSObject
    /**姓名*/
    @property (nonatomic , copy) NSString *name;
    //便利构造
    -(instancetype)initWithName:(NSString *)name;
    @end
    @implementation Test
    //便利构造
    -(instancetype)initWithName:(NSString *)name{
        if (self = [super init]) {
            self.name = name;
        }
        return self;
    }
    @end
    //演示:
    - (void)viewDidLoad {
        [super viewDidLoad];
        Test *test = [[Test alloc]initWithName:@"克里斯蒂亚诺-罗"];
        NSLog(@"打印结果:%@",test);
        //打印结果:<Test: 0x608000001f90>
        //我们解读这个打印:
        //上面的结果是<Test: 16进制的首地址>
        //那么上面是怎么来的,其实我们直接打印test和 [test description]方法的返回值
        //所以对于我们程序员来说,有时需要自己的自定义类能够详细显示一些信息,我们就可以重写这个方法
        //description:是所有继承自NSObject类都有的方法,所以我们在Test的实现中重写这个方法
        
        //重写description方法
    //    -(NSString *)description{
    //        return [NSString stringWithFormat:@"Test类-> name = %@",self.name];
    //    }
        
        //重写后:打印结果:Test类-> name = 克里斯蒂亚诺-罗
     }
    
    

    相关文章

      网友评论

          本文标题:iOS 基础读书杂集(二)

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