Object-C方法详解

160 阅读8分钟

Objective-C 的方法(Method)是类或对象的行为定义,是 OC 面向对象编程的核心要素。与其他语言的“函数”不同,OC 方法必须属于某个类或对象,且调用方式采用“消息传递”机制([接收者 方法名])。以下从方法的定义、分类、调用、参数传递到高级特性,全面详解 OC 方法的使用。

一、方法的基本概念与分类

OC 方法分为两类,核心区别在于调用者定义关键字

方法类型定义关键字调用者用途示例声明
实例方法-类的实例对象操作对象的属性或实例相关逻辑- (void)eat;
类方法(静态方法)+类本身工具类方法、工厂方法(创建对象)等+ (instancetype)personWithName;

二、方法的完整定义格式

OC 方法的定义需明确返回值类型方法名参数列表,语法格式严格区分声明(.h)和实现(.m)。

1. 声明格式(.h 文件)

// 实例方法声明
- (返回值类型)方法名[:参数1类型 参数1名[:参数2类型 参数2名...]];

// 类方法声明
+ (返回值类型)方法名[:参数1类型 参数1名[:参数2类型 参数2名...]];
  • -/+:区分实例方法/类方法;
  • (返回值类型):若无返回值,用 void;若返回对象,用对象类型(如 NSString *);
  • 方法名:OC 方法名可拆分(参数名作为方法名的一部分),形成“自然语言式”命名(如 eat:withFriend:)。

2. 实现格式(.m 文件)

// 实例方法实现
- (返回值类型)方法名[:参数1类型 参数1名[:参数2类型 参数2名...]] {
    // 方法体逻辑
    return 返回值; // 若返回值为 void,可省略
}

// 类方法实现
+ (返回值类型)方法名[:参数1类型 参数1名[:参数2类型 参数2名...]] {
    // 方法体逻辑
    return 返回值;
}

三、无参数方法

最简单的方法形式,无输入参数,仅执行固定逻辑。

示例:

头文件(Person.h)

#import <Foundation/Foundation.h>

@interface Person : NSObject
// 实例方法声明:无参数,无返回值
- (void)sayHello;

// 类方法声明:无参数,返回字符串
+ (NSString *)species;
@end

实现文件(Person.m)

#import "Person.h"

@implementation Person
// 实例方法实现:打印问候语
- (void)sayHello {
    NSLog(@"Hello, I'm a person!");
}

// 类方法实现:返回物种信息
+ (NSString *)species {
    return @"Homo sapiens";
}
@end

调用方式

#import "Person.h"

int main() {
    @autoreleasepool {
        // 调用实例方法:需先创建对象
        Person *person = [[Person alloc] init];
        [person sayHello]; // 输出:Hello, I'm a person!
        
        // 调用类方法:直接通过类名调用
        NSString *species = [Person species];
        NSLog(@"Species: %@", species); // 输出:Species: Homo sapiens
    }
    return 0;
}

四、带参数的方法

OC 方法的参数声明具有独特的“分段命名”风格,每个参数名都是方法名的一部分,使调用时语义更清晰。

1. 单个参数

声明与实现

// .h 声明
- (void)eat:(NSString *)food; // 方法名是“eat:”,参数类型 NSString *,参数名 food

// .m 实现
- (void)eat:(NSString *)food {
    NSLog(@"Eating %@", food);
}

调用

[person eat:@"apple"]; // 输出:Eating apple

2. 多个参数

多个参数用“参数名:类型 参数变量名”依次拼接,方法名由多个分段组成(如 method:param1:param2:)。

声明与实现

// .h 声明:方法名是“buy:quantity:price:”
- (void)buy:(NSString *)item quantity:(NSInteger)count price:(CGFloat)price;

// .m 实现
- (void)buy:(NSString *)item quantity:(NSInteger)count price:(CGFloat)price {
    CGFloat total = count * price;
    NSLog(@"Buy %ld %@, total: %.2f", (long)count, item, total);
}

调用

[person buy:@"book" quantity:2 price:39.9]; // 输出:Buy 2 book, total: 79.80

关键特点:

  • 方法名的每个“:”对应一个参数,参数名紧跟在“:”前(如 quantity: 是参数名,对应 count 变量);
  • 调用时参数必须按顺序传递,且每个参数前必须带“参数名:”(与声明一致);
  • 命名规范:参数名需明确表达参数含义(如 withAge: 而非 a:),使调用代码像自然语言(如 [person setName: @"Tom" age: 20])。

五、返回值处理

方法的返回值可以是基本类型、对象类型或 void(无返回值),返回对象时需注意内存管理(ARC 下自动处理)。

示例:返回对象类型

// .h 声明:返回拼接后的字符串
- (NSString *)fullNameWithFirstName:(NSString *)firstName lastName:(NSString *)lastName;

// .m 实现
- (NSString *)fullNameWithFirstName:(NSString *)firstName lastName:(NSString *)lastName {
    return [NSString stringWithFormat:@"%@ %@", lastName, firstName]; // 如“Smith John”
}

调用并接收返回值

NSString *fullName = [person fullNameWithFirstName:@"John" lastName:@"Smith"];
NSLog(@"Full name: %@", fullName); // 输出:Full name: Smith John

六、方法的调用本质:消息传递

OC 方法调用不直接是“函数调用”,而是“向接收者发送消息”,格式为:

[接收者 消息] // 消息 = 方法名 + 参数
  • 接收者:可以是对象(实例方法)或类(类方法);
  • 消息转发:若接收者无法处理消息,OC 会通过“消息转发机制”动态处理(如转发给其他对象),而非直接崩溃(这是 OC 动态性的核心体现)。

例如:

[person eat:@"apple"]; // 向 person 对象发送“eat:”消息,参数为@"apple"
[Person species];      // 向 Person 类发送“species”消息

七、特殊方法:初始化方法

初始化方法是一类特殊的实例方法,用于对象创建后的初始化(如设置初始属性),默认名为 init,遵循“init...”命名规范。

1. 默认初始化方法

// 系统默认提供的初始化方法
Person *person = [[Person alloc] init]; // alloc 分配内存,init 初始化

2. 自定义初始化方法

为方便创建对象时直接设置属性,可自定义初始化方法(必须以 init 开头):

声明与实现

// .h 声明:初始化时设置姓名和年龄
- (instancetype)initWithName:(NSString *)name age:(NSInteger)age;

// .m 实现
- (instancetype)initWithName:(NSString *)name age:(NSInteger)age {
    // 1. 先调用父类初始化方法
    self = [super init]; 
    // 2. 检查父类初始化是否成功
    if (self) { 
        // 3. 初始化当前类的属性
        _name = name; // 直接访问成员变量(属性的成员变量默认以下划线开头)
        _age = age;
    }
    // 4. 返回初始化后的对象
    return self;
}

使用自定义初始化方法

Person *person = [[Person alloc] initWithName:@"Tom" age:20];

注意:

  • 返回值类型用 instancetype(而非 id),编译器会自动推断为当前类类型,提供更严格的类型检查;
  • 必须先调用父类的初始化方法([super init]),并判断返回值是否为 nil(父类初始化可能失败);
  • 初始化方法中直接访问成员变量(_name),而非通过属性(self.name),避免触发不必要的 setter 逻辑。

八、类方法的典型用途

类方法无需创建对象即可调用,常用于以下场景:

1. 工厂方法(简化对象创建)

替代 alloc + init 流程,提供更简洁的对象创建方式:

// .h 声明:工厂方法,创建并初始化对象
+ (instancetype)personWithName:(NSString *)name age:(NSInteger)age;

// .m 实现
+ (instancetype)personWithName:(NSString *)name age:(NSInteger)age {
    // 内部调用 alloc + 自定义初始化方法
    return [[self alloc] initWithName:name age:age]; 
    // 用 self 而非类名(如 Person),支持子类继承时正确创建子类对象
}

调用

Person *person = [Person personWithName:@"Jerry" age:18]; // 比 [[Person alloc] init...] 更简洁

2. 工具类方法

封装独立于实例的通用功能(如数据转换、校验):

// 字符串工具类的类方法
@interface StringUtils : NSObject
+ (BOOL)isValidEmail:(NSString *)email; // 验证邮箱格式
+ (NSString *)md5:(NSString *)string;   // 计算 MD5 哈希
@end

九、方法的访问控制

OC 通过声明位置控制方法的访问权限(无明确的 public/private 关键字):

访问权限声明位置可见范围
公共方法.h 文件的 @interface所有引入该头文件的类均可调用
私有方法.m 文件的 @implementation仅当前 .m 文件内可见(类内部调用)

示例:私有方法

// Person.m
@implementation Person
// 私有方法:仅在当前 .m 文件内可见
- (void)privateMethod {
    NSLog("This is a private method");
}

// 公共方法(在 .h 中声明)
- (void)publicMethod {
    [self privateMethod]; // 类内部可调用私有方法
}
@end

注意:OC 的私有方法并非绝对私有,通过 Runtime 机制可在外部调用(不推荐,破坏封装性)。

十、方法的重写(Override)

子类可重写父类的方法(实例方法或类方法),实现自定义逻辑,通过 @override 关键字标识(Xcode 会自动提示)。

示例:重写父类方法

// 父类 Animal
@interface Animal : NSObject
- (void)makeSound;
@end

@implementation Animal
- (void)makeSound {
    NSLog("Animal makes sound");
}
@end

// 子类 Dog 重写 makeSound 方法
@interface Dog : Animal
@end

@implementation Dog
// 重写父类方法
- (void)makeSound {
    // 可选:调用父类方法
    [super makeSound]; 
    // 子类自定义逻辑
    NSLog("Dog barks: Woof!");
}
@end

调用

Dog *dog = [[Dog alloc] init];
[dog makeSound]; 
// 输出:
// Animal makes sound
// Dog barks: Woof!

总结

OC 方法的核心特点可归纳为:

  1. 两类方法:实例方法(-)操作对象,类方法(+)操作类本身;
  2. 消息传递:调用方式为 [接收者 方法名:参数],本质是向接收者发送消息;
  3. 分段命名:多参数方法的方法名由参数名分段组成,语义清晰;
  4. 初始化方法:以 init 开头,负责对象的初始化,需调用父类实现;
  5. 访问控制:通过声明位置(.h/.m)区分公共/私有方法。

掌握方法的定义、调用和特性,是 OC 面向对象编程的基础,也是理解 iOS 框架(如 UIKit)API 设计的关键。