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 方法的核心特点可归纳为:
- 两类方法:实例方法(
-)操作对象,类方法(+)操作类本身; - 消息传递:调用方式为
[接收者 方法名:参数],本质是向接收者发送消息; - 分段命名:多参数方法的方法名由参数名分段组成,语义清晰;
- 初始化方法:以
init开头,负责对象的初始化,需调用父类实现; - 访问控制:通过声明位置(.h/.m)区分公共/私有方法。
掌握方法的定义、调用和特性,是 OC 面向对象编程的基础,也是理解 iOS 框架(如 UIKit)API 设计的关键。