NSObject 的 initialize 和 load 方法

本文详细探讨了NSObject类中的initialize和load方法。initialize方法确保类在接收首个消息前完成初始化,而load方法则在类或分类加载到Objective-C运行时环境中时调用。文章通过代码示例展示了这两个方法的实际应用。

作为 NSObject 类中的 2 个方法 initialize 和 load 一直被我们所熟知,但是又没有具体去深入的了解,今天结合 Apple 官方文档,我们来深入了解一下 initialize 和 load 方法 。

initialize

看文档可以得知 initialize 方法的作用是在 class 收到第一个消息之前初始化 class。

Initializes the class before it receives its first message.

+ (void)initialize;
复制代码
  1. runtime 会在程序的 class 收到第一个消息之前给每一个 class 发送 initialize 消息,让 class 进行初始化。
  2. Superclasses 会在 Subclasses 之前收到 initialize 消息。
  3. initialize 方法是线程安全的,在 initialize 方法运行期间, class会被锁定,其他的线程无法向该 class 发送消息,所以我们尽量避免在 initialize 方法里面做复杂的实现。

接下来我们用代码来探究 initialize 这个方法,我们有 3 个 class ,分别是 Man,Woman,Person。 其中 Man 和 Woman 都继承自 Person 。


#import <Foundation/Foundation.h>

@interface Person : NSObject

@end


#import "Person.h"

@implementation Person
+(void)initialize{
    NSLog(@"call person initialize");
}

@end
复制代码
#import "Person.h"

@interface Man : Person

@end

#import "Man.h"

@implementation Man

@end

复制代码

#import "Person.h"

@interface Woman : Person

@end

#import "Woman.h"

@implementation Woman

@end

复制代码

在 main.m 方法代码如下:

#import <Foundation/Foundation.h>
#import "Person.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *p = [[Person alloc] init];
    }
    return 0;
}
复制代码

我们运行程序得到如下输出

 call person initialize
复制代码

在 Person 的 initialize 方法设置断点,查看堆栈调用,Person 调用的第一个方法确实是 initialize ,该方法在 Person 收到第一个消息之前初始化 Person。

接下来修改 main.m 的代码,同时生成 Person ,Man,Woman 的对象实例。

#import <Foundation/Foundation.h>
#import "Person.h"
#import "Man.h"
#import "Woman.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *p = [[Person alloc] init];
        Man *m = [[Man alloc] init];
        Woman *w = [[Woman alloc] init];
    }
    return 0;
}
复制代码

运行程序,查看控制台输出

 call person initialize
 call person initialize
 call person initialize
复制代码

我们可以看出如果子类没有实现 initialize 方法,runtime 会调用父类的 initialize 实现,那么我们就不用在子类中调用 [super initialize] ,同时也意味着父类的 initialize 会被多次调用,那么我们可以采用如下的方式来避免这个问题。

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

那如果 Person子类 Man 和 Woman 都实现 initialize 方法呢?

@implementation Man
+(void)initialize{
    NSLog(@"call man initialize");
}

+(void)initialize{
    NSLog(@"call woman initialize");
}
@end


复制代码

运行程序,查看控制台输出,可以看到 class 都是调用各自的 initialize 方法实现。

call person initialize
call man initialize
call woman initialize
复制代码

每个 class 有且仅有一次 initialize 方法调用,如果想要实现 class 和 category 的分别独立初始化,我们应该使用 load 方法。

load

看文档可以得知 class 或者 category 被添加到 runtime 的时候,load 方法就会被调用。

Invoked whenever a class or category is added to the Objective-C runtime; 
implement this method to perform class-specific behavior upon loading.

+ (void)load;
复制代码
  1. 和 initialize 方法类似,class 的 load 方法会在 superlcasses 的 load 方法调用之后被调用。
  2. category 的 load 方法会在 class 的 load 方法调用之后被调用。

接下来我们用代码来探究 load 这个方法,还是用之前的例子,我们有 3 个 class ,分别是 Man,Woman,Person。 其中 Man 和 Woman 都继承自 Person 。

Person 类实现了 load 方法


//Person.m
#import "Person.h"

@implementation Person

+(void)initialize{
    NSLog(@"call person initialize");
}

+(void)load{
    NSLog(@"call person load");
}

// main.m
#import <Foundation/Foundation.h>
#import "Person.h"
#import "Man.h"
#import "Woman.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *p = [[Person alloc] init];

    }
    return 0;
}

@end
复制代码

运行程序,查看控制台输出,可以看出 load 方法调用在 initialize 方法之前。

call person load
call person initialize
复制代码

接下来修改 main.m 实现

#import <Foundation/Foundation.h>
#import "Person.h"
#import "Man.h"
#import "Woman.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *p = [[Person alloc] init];
        Man *m = [[Man alloc] init];
        Woman *w = [[Woman alloc] init];
    }
    return 0;
}

复制代码

运行程序,查看控制台输出,可以看出子类没有实现 load 方法的时候,runtime 不会自动调用父类的 load 方法实现

call person load
call person initialize
call man initialize
call woman initialize
复制代码

接下来修改 Man.m 和 Woman.m 实现


// Man.m
#import "Man.h"

@implementation Man
+(void)initialize{
    NSLog(@"call man initialize");
}

+(void)load{
    NSLog(@"call man load");
}
@end

// Woman.m
#import "Woman.h"

@implementation Woman

+(void)initialize{
    NSLog(@"call woman initialize");
}

+(void)load{
    NSLog(@"call woman load");
}

@end
复制代码

运行程序,查看控制台输出,可以看出,我们没有在 Woman 和 Man 中显式调用父类的 load 方法,但是父类的 load 方法调用都在子类之前,这个和 initialize 方法是一样的,毕竟是要先有父类初始化,才会有子类初始化。

call person load
call woman load
call man load
call person initialize
call man initialize
call woman initialize
复制代码

接下来新建一个 Man 的 category 叫做 Man(Work),

// Mam + Work.m
#import "Man+Work.h"
@implementation Man (Work)
+(void)load{
    NSLog(@"call Man (Work) load");
}
@end
复制代码

运行程序,查看控制台输出,可以看出 category 的 load 方法调用总是在 class 的 load 方法调用之后。

call man load
call Man (Work) load
call man initialize
复制代码

总结

通过一些代码例子和官方文档,我们可以知道 initialize 方法的作用是在 class 收到第一个消息之前初始化 class。load 方法的调用时机是在 class 或者 category 被添加到 runtime 的时候。load 方法调用在 initialize 方法之前。

参考

  1. https://developer.apple.com/documentation/objectivec/nsobject/1418639-initialize?preferredLanguage=occ
  2. https://developer.apple.com/documentation/objectivec/nsobject/1418815-load?language=objc
  3. http://zhangbuhuai.com/initialize-and-load-in-objective-c/
<think>好的,我现在需要回答用户关于类方法对象方法区别的问题。用户已经提供了三个引用,我需要仔细看看这些内容是否相关,然后结合自己的知识来组织答案。 首先看引用[1],里面提到类方法使用@classmethod装饰,第一个参数是cls,可以通过类或实例调用。实例方法第一个参数是self,必须通过实例调用。静态方法用@staticmethod,也可以通过类或实例调用。这应该就是Python中的情况,但用户的问题可能更通用,比如包括其他语言吗?不过用户给的引用里有PythonObjective-C的例子,可能用户主要关注这两种,但问题没有明确语言,所以可能需要一般性的回答,再结合例子。 引用[2]是关于Python静态方法如何调用类属性的,可能不太直接相关,但里面提到静态属性方法的用途,可能可以忽略这部分,除非需要解释静态方法。 引用[3]是Objective-C的例子,里面展示了类方法+ (void)hello;对象方法-开头的,说明在OC中类方法实例方法的声明方式不同,调用时类方法用类名调用,实例方法需要对象实例。 用户的问题是比较类调用方法对象调用方法的区别使用方式,可能包括定义、调用方式、用途等。 接下来需要整理这些信息,分点回答,结构清晰。可能的结构: 1. 定义方式不同:类方法使用装饰器(Python)或+符号(OC),参数为cls或类本身;实例方法参数为self或实例。 2. 调用主体不同:类方法由类调用,实例方法由对象实例调用。 3. 访问权限不同:类方法不能访问实例属性,只能访问类属性;实例方法可以访问实例类属性。 4. 使用场景:类方法用于工厂模式、操作类属性;实例方法处理对象的具体数据。 然后举PythonOC的例子,如引用中的代码片段。注意要符合用户给的引用内容,比如Python中的@classmethod实例方法,OC中的+-方法。 需要确保回答正确,比如在Python中,类方法可以通过实例调用吗?根据引用[1]中的描述,类方法可以通过类或实例调用,但实例方法必须通过实例调用。这点需要注意,可能会在区别中提到。 另外,静态方法方法的区别是否需要提及?用户的问题可能只关注类方法实例方法,但引用[1]提到了静态方法,可能需要简要说明,但用户的问题重点可能不在此处。 最后生成相关问题,比如适用场景、如何定义、设计模式中的应用等。 现在组织语言,确保符合用户的中文要求,使用正确的术语,分点说明,结构清晰。</think>### 类方法 vs 对象方法:核心区别与使用方式 #### 一、定义方式 1. **类方法** - **Python**:使用 `@classmethod` 装饰器,第一个参数为 `cls`(指向类本身) ```python class MyClass: @classmethod def class_method(cls): # cls 指向类本身 print("调用类方法") ``` - **Objective-C**:以 `+` 符号开头,直接关联类而非实例 ```objectivec @interface People : NSObject + (void)hello; // 类方法声明 @end ``` 2. **对象方法(实例方法)** - **Python**:默认定义方式,第一个参数为 `self`(指向实例对象) ```python class MyClass: def instance_method(self): # self 指向实例 print("调用实例方法") ``` - **Objective-C**:以 `-` 符号开头,需通过实例调用 ```objectivec @interface People : NSObject - (void)printInfo; // 实例方法声明 @end ``` #### 二、调用方式 | **方法类型** | **Python 调用方式** | **Objective-C 调用方式** | |--------------|---------------------------|--------------------------------| | 类方法 | `类名.方法名()` 或 `实例.方法名()` | `[类名 方法名]` | | 实例方法 | `实例.方法名()` | `[实例 方法名]` | **示例**: - Python 类方法可通过类实例调用,但本质仍操作类属性[^1] - Objective-C 类方法只能通过类名调用(如 `[People hello]`)[^3] #### 三、访问权限 1. **类方法** - 只能访问**类属性**或**类级数据** - 无法直接操作实例属性(因无 `self` 参数) ```python class MyClass: class_attr = "类属性" @classmethod def update_class_attr(cls, value): cls.class_attr = value # 正确:操作类属性 ``` 2. **实例方法** - 可访问**实例属性****类属性** - 通过 `self` 操作实例数据,通过 `类名` 或 `self.__class__` 操作类属性 ```python class MyClass: class_attr = "类属性" def instance_method(self): print(self.instance_attr) # 访问实例属性 print(self.__class__.class_attr) # 访问类属性 ``` #### 四、使用场景 | **方法类型** | **典型应用场景** | |--------------|------------------------------------------| | 类方法 | 1. 工厂模式(创建实例)<br>2. 操作类属性(如全局配置) | | 实例方法 | 1. 处理对象特有数据<br>2. 修改实例状态 | **示例场景**: - **类方法**:实现对象创建的灵活逻辑(如根据参数返回不同子类实例) - **实例方法**:实现对象行为(如修改用户账户余额、更新订单状态)[^2] --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值