iOS - 类与对象

iOS - 类与对象

类方法

OC 中类的方法只有实例方法和静态方法两种:

@interface Controller: NSObject
/// 静态方法
+ (void)thisIsAStaticMethod;

/// 实例方法
- (void)thisIsAnInstanceMethod;
@end

OC 中的方法只要声明在 @interface 里,就可以认为是公有的。实际上,OC 没有像 Java,C++ 中的那种绝对的 私有及保护 的成员方法,仅仅可以对调用者隐藏某些方法。

声明和实现都写在 @implementation 里的方法,类的外部是看不到的。

可以使用 分类(Category) 和 类扩展(Extension)来实现私有方法。

// AClass.h
@interface AClass: NSObject
- (void)sayHello;
@end

// AClass.m
@interface AClass (private)
- (void)privateSayHello1;
@end

@interface AClass ()
- (void)privateSayHello2;
@end

@implementation AClass
- (void)sayHello 
{
    [self privateSayHello];
}

- (void)privateSayHello1
{
    NSLog(@"privateSayHello1");
}

- (void)privateSayHello2
{
    NSLog(@"privateSayHello2");
}

@end

使用这种方法外部就不能直接调用到 privateSayHello1privateSayHello2 方法。

类变量

Apple 推荐现代的 Objective-C 中使用 @property 来实现成员变量,使用 @property 声明的变量可以使用 实例名.变量名 来获取和修改。
@property 可以看做一种语法糖,使用 @property 此声明

@interface AClass: NSObject
@property (nonatomic, copy) NSString *name;
@end

可等价于

@interface AClass: NSObject
{
    NSString *_name;
}
- (NSString *)name;
- (void)setName:(NSString *)name;
@end

@implementation AClass
- (NSString *)name
{
    return _name;
}
- (void)setName:(NSString *)name
{
    _name = name;
}
@end

@property 会自动生成 gettersetter 方法,同时进行自动内存管理

@property 可声明的属性修饰符有如下几种:

  • readwrite 可读写,需要生成 gettersetter 方法.
  • readonly 只读,只会生成 getter 方法,不会生成 setter 方法,不希望属性在类外改变时使用。
  • assign 赋值属性,setter 方法将传入的参数赋值给实例变量。
  • strong 持有特性,setter 方法将传入的参数先保留,再赋值,传入参数的引用计数会 +1。
  • copy 拷贝特性,setter 方法将传入对象复制一份;需要完全一份新的变量时使用。
  • nonatomicatomic ,决定编译器生成的 gettersetter 方法是否是原子操作。

默认的 @property
基本数据类型: readwriteassignatomic
对象: readwritestrongatomic

Protocol

OC 是单继承的,OC 中的类可以实现多个 protocol 来实现类似 C++ 中多重继承的效果。

protocol 类似 JAVA 中的 interface,定义了一个方法列表,这个方法列表中的方法可以使用 @required @optional 标注,以表示该方法是否客户类必须要实现的方法。

protocol 中含有 property 时,编译器不会进行自动 synthesize 的,需要手动处理:在实现这个 protocol 的时候要么再次声明 property 要么手动 synthesize

Category

Category 是一种很灵活的扩展原有类的机制,使用 Category 不需要访问原有类的代码,也无需集成,Category 提供了一种简单的方式,来实现类的相关方法的模块化,把不同的方法分配到不同的类文件中。

在使用 Category 时需要注意的是,如果有多个 Category 均实现了同一个方法,那么这些方法在运行时只有一个会被调用,具体哪个会被调用是不确定的,因此建议在使用 Category 时在函数命名方法加上前缀。

Extension

Extension 可以认为是一种匿名的 CategoryExtensionCategory 有如下区别:

  • 使用 Extension 必须有原有类的源码
  • Extension 声明的方法必须在类的主 @implementation 区间内实现,可以避免使用有名 Category 带来的多个不必要的@implementation
  • Extension 可以在类中添加新的属性和实例变量,Category 不可以。
  • ExtensionCategory 里添加的方法必须要有实现。(没有实现编译器会给出警告)
  • Category 一般用来给类添加私有的变量和方法,在类的内部使用。

如果在类中添加全局变量

一个简单直接的做法是在 .m 文件中使用 static 变量。由于 static 变量在编译器就是确定的,因此对于 NSObject 对象来说,初始化的值只能是 nil, 如何进行类似 init 的初始化呢? 可以通过重载 initialize 方法。

static NSOperationQueue *_queue = nil;
@implementation XWPerson
- (void)initialize
{
    if (!_queue) {
        _queue = [[NSOperationQueue alloc] init];
    }
}
@end

为什么这里要判断是否为 nil 呢?因为 initialize 方法可能会调用多次。

有一种方法是声明 static 函数,下面代码来自 AFNetworking,声明了一个当前文件范围可用的队列:

static dispatch_queue_t url_session_manager_creation_queue() {
    static dispatch_queue_t af_url_session_manager_creation_queue;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        af_url_session_manager_creation_queue = dispatch_queue_create("com.alamofire.networking.session.manager.creation", DISPATCH_QUEUE_SERIAL);
    });

    return af_url_session_manager_creation_queue;
}

除此以外,还可以通过编译器的 __attribute_ 特性来实现初始化:

__attribute__((constructor))
static void initialize_Queue() {
    _personOperationQueue = [[NSOperationQueue alloc] init];
}

类的初始化

Objective-C 是建立在 Runtime 基础上的语言,类也不例外。OC 中类的初始化也是动态的,在 OC 中绝大部分类都继承自 NSObject ,它有两个非常特殊的类方法 +load+initialize,用于类的初始化。

+load

+load 方法是当类或分类被添加到 Objective-C runtime 时被调用的,实现这个方法可以让我们在类加载的时候执行一些类相关的行为。子类的 +load 方法会在它所有的父类的 +load 方法之后执行,而分类的 +load 方法会在它的主类 +load 方法之后执行。

+initialize

+initialize 方法是在类或它的子类收到第一条消息之前被调用的,这里所指的消息包括实例方法和类方法的调用。也就是说 +initialize 方法是以懒加载的方式被调用的。如果程序一直没有给某各类或它的子类发送消息,那么这个类的 +initialize 方法是永远不会被调用的。

如果一个分类实现了 +initialize 方法,那么就会对这个类中的实现造成覆盖。


更多干货文章

博客:www.qiuxuewei.com
微信公众号:@开发者成长之路
公众号二维码

一个没有鸡汤只有干货的公众号