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

类就好像设计图,组装出你的逻辑实体

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

//.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 

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_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.类的介绍与使用")