美文网首页
30天学会ios开发"4.类的介绍与使用"

30天学会ios开发"4.类的介绍与使用"

作者: 沈悦 | 来源:发表于2019-07-29 15:39 被阅读0次
    类就好像设计图,组装出你的逻辑实体

    类好比设计图,根据类来实例化出可以使用的对象.
    对象能承载类的设计功能,将你的逻辑具体实现.

    //.h 声明一个类
    @interface SYBaseNavigationController : UINavigationController //表明这个类继承自UINavigationController
    //此处可以定义公有属性
    @end
    
    //.m 实现这个类
    @interface SYBaseNavigationController ()
    //此处可以定义私有属性
    @end
    @implementation SYBaseNavigationController
    @end
    

    1 所有的类皆继承自父类NSObject,如果你要做一个工具类,你可以继承自NSObject.
    2 oc 只支持单继承,所以你的父类只能有一个.

    @implementation ViewController
    
    - (void)viewDidLoad {
        NSLog(@"123");
        [super viewDidLoad];
        NSLog(@"456");
    }
    

    super 代表调用父类里的方法, 父类里也有一个viewDidLoad方法,此写法表明要扩展父类的方法.扩展代码写的位置要根据实际情况来决定.

    - (void)viewDidLoad {
        [super viewDidLoad];
        _view.backgroundColor = [UIColor purpleColor];
        [self.view addSubview:_vv];
    }
    

    属性与懒加载

    类的属性访问有两种方式 一种是 _加上属性名字的直接访问方式,一种为self.加上属性名的访问方式,接下来说一下区别.
    view 的访问 与赋值方式直接对属性奇效,而self.view 的读取与赋值则会经过setter 和 getter 函数的处理.
    getter 和setter 函数为oc 编译代码时默认生成,如果你不覆写它们,那么"self." 和 "
    "方式没有任何区别.不过设想一个情景,如果我在读取某属性的时候想记录一条日志,那么getter方法就很方便,比如

    @implementation ViewController
    @synthesize arr; //如果你需要getter 和 setter 都改的话需要这句,同时 _arr 访问方式将无法再使用只能使用 arr 属性,只能使用self.arr!!!!反之 如果只改 getter 和 setter 可以不用写,同时 _arr 还能继续使用
    - (NSArray *)arr {
        NSLog(@"123"); //每次访问数据我们就都能打出日志啦
        return arr;
    }
    
    - (void)setArr:(NSArray *)arr {
        arr = @[@"123"];
    }
    

    以下情况不需要@synthesize arr

    @implementation ViewController
    //@synthesize arr;
    
    //- (NSArray *)arr {
    //    NSLog(@"123");
    //    return arr;
    //}
    
    - (void)setArr:(NSArray *)arr {
       NSLog (@"456")
        _arr =  @[@"123"]; // 这里是能继续用_arr的
    }
    
    
    @implementation ViewController
    //@synthesize arr;
    
    - (NSArray *)arr {
        NSLog(@"123");
        return _arr;
    }
    
    //- (void)setArr:(NSArray *)arr {
    //    _arr =  @[@"123"]; // 这里是能继续用_arr的
    //}
    
    


    原则上建议都用"self."的方式,不过@"_"写起来更快,就看你的个人喜好了.

    懒加载则是灵活使用getter方法的一直方式,有时候你的属性不确定在什么时候初始化好,那么就在第一次使用的时候初始化吧,懒加载的意思就是用到了再赋值,不用则永远不会赋值! 懒加载能帮你减少内存占用,提升页面加载的效率.

    //标准的懒加载模板
    - (NSArray *)arr {
        //self.arr 访问时如果为空就会初始化,否则直接返回值,简单而精妙,
        if (_arr==nil) {
            _arr = [NSArray new];
        }
        return arr;
    }
    


    如果你继续使用_arr 方式访问,懒加载是不会起效的哦~要用"self."

    析构与构造

    面向对象的设计如java php pychon 也包含OC 都有所谓的析构和构造函数,在类的诞生和消失时会被自动调用.

    //每次你在打init的时候IDE会自动提示 
    //构造函数
    - (instancetype)init
    {
        self = [super init];
        if (self) {
            <#statements#> //如果想在类初始化时做些动作,可以写在这里哦
        }
        return self;
    }
    
    //析构函数
    - (void)dealloc
    {
        <#statements#> //析构函数不需要写[super dealloc]
    }
    


    析构方法里经常会用来释放资源句柄或者停止监听等操作,请不要忘记实现, 其后会反复提及此事!

    category(类扩展)

    category 在oc里有时又被称为分类,不过这个词不是能很好地反应他的功能,所以还是遵循英文的叫法.
    category的主要作用为直接对类进行扩展,举列子如F35战斗机原本只有空对空导弹,你可以写一个海军用扩展那么F35就多了对舰导弹,也可以写一个陆军扩展多一个反坦克导弹.那么你用这个类的时候,直接就能对空对地对海了.哈哈
    类的影响是在编译阶段就会开始的,所以扩展的方法一定要起好,务必添加独一无二的前缀.


    image.png
    @interface F35AirFighter (ocean)
    ....
    @implementation F35AirFighter (ocean)  //圆括号+分类名称 你就定义了一个分类,不过你必须要有一个F35AirFighter的原始类.
    

    1.分类是用于给原有类添加方法的,因为分类的结构体指针中,没有属性列表,只有方法列表。所以原则上讲它只能添加方法, 不能添加属性(成员变量),实际上可以通过其它方式添加属性(接下来会说) ;
    2.分类中的可以写@property, 但不会生成setter/getter方法, 也不会生成实现以及私有的成员变量(编译时会报警告);
    3.可以在分类中访问原有类中.h中的属性;
    4.如果分类中有和原有类同名的方法, 会优先调用分类中的方法, 就是说会忽略原有类的方法。所以同名方法调用的优先级为 分类 > 本类 > 父类。因此在开发中尽量不要覆盖原有类;
    5.如果多个分类中都有和原有类中同名的方法, 那么调用该方法的时候执行谁由编译器决定;编译器会执行最后一个参与编译的分类中的方法。

    来看看一个比较经典的UIVIew分类

    .h
    @interface UIView (Additions)
    // Position of the top-left corner in superview's coordinates 此分类简化了uiview 各属性的访问方式
    @property CGPoint position;
    @property CGFloat x;
    @property CGFloat y;
    @property CGFloat top;
    @property CGFloat bottom;
    @property CGFloat left;
    @property CGFloat right;
    
    .m
    @implementation UIView (Additions)
    
    - (CGFloat)x {
        return self.frame.origin.x;
    }
    
    - (void)setX:(CGFloat)x {
        CGRect rect = self.frame;
        rect.origin.x = x;
        [self setFrame:rect];
    }
    
    - (CGFloat)y {
        return self.frame.origin.y;
    }
    
    - (void)setY:(CGFloat)y {
        CGRect rect = self.frame;
        rect.origin.y = y;
        [self setFrame:rect];
    }
    ....
    
    使用时引入
    #import "UIView+Additions.h"
    
    

    这个category 向uivew 添加了 x,y 的属性,这样就不需要使用view.frame.origin.x 这样繁琐的访问,直接x 和y 就可以了.不过有同学要提问了,不是说category 不能扩展属性吗, 其实根本原因是使用了系统没有生成的setter/getter方法,可不可以在手动添加setter/getter来避免崩溃,完成调用呢? 其实是可以的。由于OC是动态语言,方法真正的实现是通过runtime完成的,虽然系统不给我们生成setter/getter,但我们可以通过runtime手动添加setter/getter方法。

    #import <objc/runtime.h>
    
    static NSString *nameWithSetterGetterKey = @"nameWithSetterGetterKey"; //定义一个key值
    @implementation Programmer (Category)
    
    //运行时实现setter方法
    
    - (void)setNameWithSetterGetter:(NSString *)nameWithSetterGetter {
    objc_setAssociatedObject(self, &nameWithSetterGetterKey, nameWithSetterGetter, OBJC_ASSOCIATION_COPY);
    }
    
    //运行时实现getter方法
    - (NSString *)nameWithSetterGetter {
    return objc_getAssociatedObject(self, &nameWithSetterGetterKey);
    }
    
    @end
    
    programmer.nameWithSetterGetter = @"有setter/getter"; //调用setter,成功
    
    NSLog(@"%@",programmer.nameWithSetterGetter); //调用getter,成功
    // NSLog(@"%@",_nameWithSetterGetter); //这是调用_成员变量,错误提示为:(Use of undeclared identifier '_nameWithSetterGetter')
    

    <objc/runtime.h> 为运行时所需要引入库 OBJC_ASSOCIATION_COPY 等同于 copy 如果需要strong 使用OBJC_ASSOCIATION_RETAIN_NONATOMIC
    static NSString *nameWithSetterGetterKey = @"nameWithSetterGetterKey"; //为定义的常量指针即可.
    之前的uiview(addition)之所以x和y 都没有定义key 是因为他们所需的值其实已经在frame.origin中了,所以 objc_setAssociatedObject / objc_getAssociatedObject只在值没有存储的载体时才需要使用,如果是快捷访问方式这样的扩展,你可以直接定义扩展方法和属性.

    相关文章

      网友评论

          本文标题:30天学会ios开发"4.类的介绍与使用"

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