Apple Documentation --Objective-C系列

方法名:initialize

声明

+ (void)initialize;

讨论

在这个类或集成与这个类的子类第一次被加载前,runtime 会发送initialize消息来调用这个方法。initialize消息是这个类从runtime中收到的第一个消息,父类接收到这个消息在子类之前。

runtime是以线程安全方式向类发送initialize消息,而且对这个类发送的第一个消息就是initialize消息,其他的线程如果尝试发送消息给这个类将会阻塞知道initialize完成。

如果子类没有实现initialize,父类的initialize也许会多次被调用。runtime会根据继承关系来调用或者子类明确的指出调用[super initialize]。如果你想自己去管理或者控制这种多次调用,你可以在下方注释的地方进行实现:

+ (void)initialize {
  if (self == [ClassName self]) {
    // ... do the initialization ...
  }
}

因为initialize是以阻塞方式被调用的,所以在这个方法中应尽可能减少代码工作量,然后由于是阻塞方式调用所以任何调用其他类的代码都可能导致死锁。所以不要在initialize中进行复杂的逻辑,尽量简化。

特别注意

initialize每个类只调用一次,如果你想对类和类的类别个字单独进行初始化,你应该实现load方法。

方法名:load

当类或者类别被加入到runtime的时候进行调用,一般用于在类被加载前实现一些特殊的操作

声明

+ (void)load;

讨论

load消息在动态加载和静态链接的时候都会被发送,但仅仅这个类或者类别实现这个方法的时候才会有相应

初始化顺序如下:

  1. 调用所有的Framework中的初始化方法
  2. 调用所有的+load方法
  3. 调用C++的静态初始化方法及C/C++中的attribute(constructor)函数
  4. 调用所有链接到目标文件的framework中的初始化方法

此外:

  • 一个类的+load方法在其父类的+load方法后调用
  • 一个Category的+load方法在被其扩展的类的自有+load方法后调用

+load方法中,可以安全地向其它无关的类发送消息,但接收消息的类中的+load方法可能尚未实现。

方法名:init

子类用来初始化一个新的对象,并立刻给该对象分配内存

声明

- (instancetype)init;

返回值

一个初始化的对象,如果因为某种原因没有创建成果那么就会返回nil,这样就不会抛出异常。

讨论

一个init消息一定会与alloc(或者allocWithZone:)消息出现在同一行代码中:

SomeClass *object = [[SomeClass alloc] init];

一个对象只有被初始化了才能被使用。

在一般情况下,你必须调用父类初始化方法来初始化并返回一个对象,如果不能初始化的话,就会返回nil。举例,假设有一个类——BuiltInCamera类,这个类当运行的设备中没有摄像头的时候进行初始化会返回nil

- (instancetype)init {
    if (self = [super init]) {
        // Initialize self
    }
    return self;
}

在一般情况下,你都要通过init来获取返回对象,而不是通过alloc或者allocWithZone:


initialize,load,init 代码示例

创建一个person类,并在其.m文件中实现以下三个方法

//
//  Person.m
//  AFNetworingSourceTest
//
//  Created by Mac on 2018/7/12.
//  Copyright © 2018年 MJJ. All rights reserved.
//

#import "Person.h"

@implementation Person

+(void)initialize {
    NSLog(@"%s", __FUNCTION__);
}

-(instancetype)init {
    NSLog(@"%s", __FUNCTION__);
    return  self;
}

+(void)load {
    NSLog(@"%s", __FUNCTION__);
}
@end

然后在viewDidLoad中初始化两个实例

- (void)viewDidLoad {
    [super viewDidLoad];
    Person* person1  = [[Person alloc]init];
    Person* person2  = [[Person alloc]init];
    // Do any additional setup after loading the view, typically from a nib.
}

控制台打印:

2018-07-12 12:34:19.095313+0800 AFNetworingSourceTest[62407:43110238] +[Person load]
2018-07-12 12:15:31.053588+0800 AFNetworingSourceTest[61566:43077247] +[Person initialize]
2018-07-12 12:15:31.053690+0800 AFNetworingSourceTest[61566:43077247] -[Person init]
2018-07-12 12:15:31.053787+0800 AFNetworingSourceTest[61566:43077247] -[Person init]

可以看到尽管创建了2个实例,但是initialize只调用了一次,而且是在创建的时候被调用,然后init调用了两次

接下来给Person创建一个子类Man

#import "Person.h"

@interface Man : Person

@end

#import "Man.h"

@implementation Man

@end

修改Personinitialize方法打印类名

+(void)initialize {
    NSLog(@"%s %@", __FUNCTION__,[self class]);
}

控制台打印:

2018-07-12 12:31:47.690548+0800 AFNetworingSourceTest[62342:43103509] +[Person load]
2018-07-12 12:31:49.123319+0800 AFNetworingSourceTest[62342:43103509] +[Person initialize] Person
2018-07-12 12:31:49.123443+0800 AFNetworingSourceTest[62342:43103509] -[Person init]
2018-07-12 12:31:49.123572+0800 AFNetworingSourceTest[62342:43103509] -[Person init]
2018-07-12 12:31:49.123665+0800 AFNetworingSourceTest[62342:43103509] +[Person initialize] Man
2018-07-12 12:31:49.123740+0800 AFNetworingSourceTest[62342:43103509] -[Person init]

initialize方法输出了两次,一个子类没有实现initialize方法,那么父类会调用这个方法两次,一次为自己,另一次为子类。load方法在initialize之前调用


方法名:alloc

给对应的类返回一个新的实例

声明

+ (instancetype)alloc;

返回值

一个新的实例

讨论

这个新的实例的isa指针被初始化为一个数据结构体用来描述该类;其他实例变量的内存被设置为0.

你必须使用init方法来完成初始化过程。
举例:

TheClass *newObject = [[TheClass alloc] init];

初始化不要重写alloc,而应该根据每个类来实现其自身的init方法。

因为历史原因,alloc会唤起allocWithZone:方法

方法名:allocWithZone

给对应的类返回一个新的实例

声明

+ (instancetype)allocWithZone:(struct _NSZone *)zone;

参数

zone 这个参数已被忽略,传nil即可

返回值

一个新的实例

讨论

这个新的实例的isa指针被初始化为一个数据结构体用来描述该类;其他实例变量的内存被设置为0.

你必须使用init方法来完成初始化过程。
举例:

TheClass *newObject = [[TheClass allocWithZone:nil] init];

初始化不要重写alloc,而应该根据每个类来实现其自身的init方法。

这个方法没用了,没啥好看的

方法名:copy

返回一个从从copyWithZone:获取的对象

声明

- (id)copy;

返回值

返回的对象是从NSCopying协议的copyWithZone:方法中获取

讨论

使用NSCoping协议可以很方便的为一个类添加copy方法,当然,一定要实现copyWithZone:方法,不然会抛出一个异常。
继承于NSObject的类自身不提供NSCopying协议,子类必须对这个协议添加支持,并且实现copyWithZone:方法
子类的copyWithZone:调用顺序:这个方法会首先向父类发送消息,并将其实现,除非这个子类是直接从NSObject派生

方法名:copyWithZone

返回一个接收者(对象)

声明

+ (id)copyWithZone:(struct _NSZone *)zone;

参数

zone
不用管这个参数,直接可以忽略

返回值

一个对象

讨论

当你需要一个可以实现NSCopying协议的类时需要用到这个方法,要实现这个方法.例如,该方法允许您使用类对象作为NSDictionary对象的键,不用重写这个方法

方法名:mutableCopy

mutableCopyWithZone 返回一个对象,zone参数为nil

声明

- (id)mutableCopy;

返回值

返回的对象是由NSMutableCopying协议方法mutableCopyWithZone:返回,zone参数为nil

讨论

对于采用NSMutableCopying协议的类来说,这是一种方便的方法。如果mutableCopyWithZone没有实现,则会引发异常。

方法名:mutableCopyWithZone:

返回一个对象

声明

zone:用于创建内存区域

讨论

类对象可以使用这个方法来使自身符合NSMutableCopying协议,例如,该方法允许您使用类对象作为NSDictionary对象的键,不用重写这个方法

方法名:dealloc

释放对象占用的内存

声明

- (void)dealloc;

讨论

如果在该对象内存被释放时候再次发送消息给该对象就会生成一个错误,可以重写这个方法来释放实例变量之外的对象的内存释放,例:

- (void)dealloc {
    free(myBigBlockOfMemory);
}

delloc的实现中,不要调用超类的实现。你应该尽量避勉使用 dealloc 管理有限资源诸如文件描述符的生命周期。决不要直接发送 dealloc 消息。相反,任何对象的 dealloc 方法由运行时调用。参看 高级内存管理编程指南 以获得更详细的内容。

特别注意事项

当未使用 ARC 时,你的 dealloc 实现必须把调用父类的实现作为最后一条指令。(隐含的意思就是,使用 ARC 时不能调用父类的实现)

方法名:new

分配给接收类一个新实例,向它发送一个init消息,并返回初始化的对象。

声明

+ (instancetype)new;

返回值

一个接收类的新的实例

讨论

该方法是alloc和init的组合。与alloc一样,它初始化新对象的isa实例变量,以便指向类数据结构。然后调用init方法来完成初始化过程。

方法名:class

返回一个类对象

声明

+ (Class)class;

返回值

一个类对象

讨论

当一个类是消息接受者是可以通过它的名称来引用它。在其他所有情况下,类对象必须通过此方法或者类似方法获得。例如,这里将SomeClass作为一个参数餐递给isKindOfClass:方法(在NSObject协议中声明):

BOOL test = [self isKindOfClass:[SomeClass class]];

推荐阅读更多精彩内容