🎯 消息传递机制
动态消息传递 vs 静态函数调用
核心区别:
- C++:编译时确定函数地址,直接调用
- Objective-C:运行时动态查找方法实现
// Objective-C 消息传递
[object doSomething];
// 编译后转换为:
objc_msgSend(object, @selector(doSomething));
消息传递流程:
- 在对象的方法列表中查找 selector
- 如果找不到,沿着继承链向上查找
- 如果还找不到,进入消息转发机制
消息转发机制
// 1. 动态方法解析
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(doSomething)) {
class_addMethod([self class], sel, (IMP)dynamicMethod, "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
// 2. 备用接收者
- (id)forwardingTargetForSelector:(SEL)aSelector {
if (aSelector == @selector(doSomething)) {
return _backupObject;
}
return [super forwardingTargetForSelector:aSelector];
}
// 3. 完整消息转发
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if (aSelector == @selector(doSomething)) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
if ([_backupObject respondsToSelector:[anInvocation selector]]) {
[anInvocation invokeWithTarget:_backupObject];
} else {
[super forwardInvocation:anInvocation];
}
}
🔄 运行时特性 (Runtime)
类与对象结构
struct objc_class {
Class isa; // 指向元类
Class super_class; // 指向父类
const char *name; // 类名
long version; // 版本信息
long info; // 类信息
long instance_size; // 实例大小
struct objc_ivar_list *ivars; // 实例变量列表
struct objc_method_list **methodLists; // 方法列表
struct objc_cache *cache; // 方法缓存
struct objc_protocol_list *protocols; // 协议列表
};
方法交换 (Method Swizzling)
#import <objc/runtime.h>
@implementation UIViewController (Swizzling)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
SEL originalSelector = @selector(viewWillAppear:);
SEL swizzledSelector = @selector(swizzled_viewWillAppear:);
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
// 尝试添加方法,如果原方法不存在则添加成功
BOOL didAddMethod = class_addMethod(class,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
// 添加成功,替换方法的实现
class_replaceMethod(class,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
// 直接交换方法实现
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
- (void)swizzled_viewWillAppear:(BOOL)animated {
// 在原始方法执行前添加逻辑
NSLog(@"%@ viewWillAppear", NSStringFromClass([self class]));
// 调用原始实现
[self swizzled_viewWillAppear:animated];
}
@end
关联对象 (Associated Objects)
#import <objc/runtime.h>
static char kAssociatedObjectKey;
@implementation NSObject (AssociatedObject)
- (void)setCustomProperty:(id)object {
objc_setAssociatedObject(self,
&kAssociatedObjectKey,
object,
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (id)customProperty {
return objc_getAssociatedObject(self, &kAssociatedObjectKey);
}
@end
关联策略:
-
OBJC_ASSOCIATION_ASSIGN:弱引用 -
OBJC_ASSOCIATION_RETAIN_NONATOMIC:强引用,非原子 -
OBJC_ASSOCIATION_COPY_NONATOMIC:拷贝,非原子 -
OBJC_ASSOCIATION_RETAIN:强引用,原子 -
OBJC_ASSOCIATION_COPY:拷贝,原子
📊 内存管理
MRC (Manual Reference Counting)
// 手动内存管理规则
- (void)manualMemoryManagement {
// 创建对象,引用计数 = 1
NSObject *obj = [[NSObject alloc] init];
// 引用计数 +1
[obj retain];
// 引用计数 -1
[obj release];
// 自动释放,当前runloop结束时释放
NSObject *autoObj = [[[NSObject alloc] init] autorelease];
// 获取当前引用计数
NSUInteger count = [obj retainCount];
}
ARC (Automatic Reference Counting)
所有权修饰符:
__strong: 强引用,默认修饰符
__weak: 弱引用,对象释放后自动置为nil
__unsafe_unretained: 弱引用,对象释放后不会自动置nil(不安全)
__autoreleasing: 用于按引用传递的参数
循环引用场景与解决
Block 中的循环引用:
// 问题代码:循环引用
@interface MyClass : NSObject
@property (nonatomic, copy) void (^myBlock)(void);
@property (nonatomic, strong) NSString *name;
@end
@implementation MyClass
- (void)setupBlock {
// 产生循环引用:self → block → self
self.myBlock = ^{
NSLog(@"%@", self.name);
};
}
@end
// 解决方案1:使用 __weak
- (void)setupBlockCorrectly {
__weak typeof(self) weakSelf = self;
self.myBlock = ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
NSLog(@"%@", strongSelf.name);
};
}
// 解决方案2:使用 __block 变量(MRC)
- (void)setupBlockWithBlockVariable {
__block id blockSelf = self;
self.myBlock = ^{
NSLog(@"%@", blockSelf.name);
blockSelf = nil; // 打破循环
};
}
委托模式的循环引用:
// 错误:相互强引用
@interface Manager : NSObject
@property (nonatomic, strong) id<ManagerDelegate> delegate;
@end
@interface Controller : NSObject <ManagerDelegate>
@property (nonatomic, strong) Manager *manager;
@end
// 正确:delegate 使用 weak
@interface Manager : NSObject
@property (nonatomic, weak) id<ManagerDelegate> delegate;
@end
计时器的循环引用:
// 问题:timer 强引用 target
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:@selector(tick)
userInfo:nil
repeats:YES];
// 解决方案:使用 block-based timer 或中间对象
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0
repeats:YES
block:^(NSTimer * _Nonnull timer) {
[weakSelf tick];
}];
📝 Category 与 Extension
Category (分类)
// NSString+Utilities.h
@interface NSString (Utilities)
- (BOOL)isValidEmail;
- (NSString *)reversedString;
+ (NSString *)generateUUID;
@end
// NSString+Utilities.m
@implementation NSString (Utilities)
- (BOOL)isValidEmail {
NSString *emailRegex = @"[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}";
NSPredicate *emailTest = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", emailRegex];
return [emailTest evaluateWithObject:self];
}
- (NSString *)reversedString {
NSMutableString *reversed = [NSMutableString string];
NSInteger charIndex = [self length];
while (charIndex > 0) {
charIndex--;
NSRange subRange = NSMakeRange(charIndex, 1);
[reversed appendString:[self substringWithRange:subRange]];
}
return reversed;
}
+ (NSString *)generateUUID {
CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
NSString *uuidString = (__bridge_transfer NSString *)CFUUIDCreateString(kCFAllocatorDefault, uuid);
CFRelease(uuid);
return uuidString;
}
@end
Category 特点:
- 为已有类添加方法
- 不能添加实例变量
- 方法名冲突时,Category 的方法会覆盖原类方法
- 可以声明属性,但需要关联对象支持存储
Extension (扩展)
- 主要用于类的内部实现细节,不暴露给外部调用者。
// MyClass.m
@interface MyClass ()
// 私有属性
@property (nonatomic, strong) NSString *privateProperty;
// 私有方法
- (void)privateMethod;
@end
@implementation MyClass
// 实现
@end
Extension 特点:
- 在编译期解析
- 可以添加实例变量和方法
- 主要用于隐藏私有接口
- 必须在主实现文件中声明
📋 Protocol (协议)
正式协议
// 定义协议
@protocol DataSource <NSObject>
@required
- (NSInteger)numberOfSections;
- (NSInteger)numberOfItemsInSection:(NSInteger)section;
@optional
- (NSString *)titleForSection:(NSInteger)section;
- (void)didSelectItemAtIndexPath:(NSIndexPath *)indexPath;
@end
// 遵守协议
@interface MyViewController : UIViewController <DataSource, UITableViewDelegate>
@end
@implementation MyViewController
// 必须实现 @required 方法
- (NSInteger)numberOfSections {
return 1;
}
- (NSInteger)numberOfItemsInSection:(NSInteger)section {
return 10;
}
// 可选实现 @optional 方法
- (NSString *)titleForSection:(NSInteger)section {
return @"Section Title";
}
@end
协议方法检查
// 检查对象是否实现了协议方法
if ([self.dataSource respondsToSelector:@selector(titleForSection:)]) {
NSString *title = [self.dataSource titleForSection:0];
}
🔄 Block (块)
Block 语法
// 1. 无参数无返回值
void (^simpleBlock)(void) = ^{
NSLog(@"This is a block");
};
// 2. 带参数
void (^blockWithParams)(NSString *, NSInteger) = ^(NSString *name, NSInteger age) {
NSLog(@"%@ is %ld years old", name, age);
};
// 3. 带返回值
NSInteger (^blockWithReturn)(NSInteger, NSInteger) = ^(NSInteger a, NSInteger b) {
return a + b;
};
// 4. 作为方法参数
- (void)doSomethingWithCompletion:(void(^)(BOOL success, NSError *error))completion {
// 执行操作
if (completion) {
completion(YES, nil);
}
}
Block 内存管理
Block 类型:
-
NSGlobalBlock:存储在全局区,不捕获外部变量 -
NSStackBlock:存储在栈区,MRC 下需要注意 -
NSMallocBlock:存储在堆区,可被 retain
// Block 的内存管理
typedef void (^CompletionBlock)(void);
@interface MyClass : NSObject
@property (nonatomic, copy) CompletionBlock completion;
@end
@implementation MyClass
- (void)setupBlock {
// 正确:使用 copy(ARC 下自动处理)
self.completion = ^{
NSLog(@"Block executed");
};
}
@end
Block 捕获变量
- (void)variableCapture {
NSInteger outsideVariable = 10;
__block NSInteger blockVariable = 20;
void (^testBlock)(void) = ^{
// outsideVariable 被复制(值捕获)
NSLog(@"Outside variable: %ld", outsideVariable);
// blockVariable 可修改(引用捕获)
blockVariable++;
NSLog(@"Block variable: %ld", blockVariable);
};
outsideVariable = 30; // 不影响 Block 内的值
blockVariable = 40; // 影响 Block 内的值
testBlock();
}
💡 面试要点总结
必掌握知识点:
-
消息传递机制:理解
objc_msgSend工作原理 - Runtime 应用:方法交换、关联对象的实际场景
- 内存管理:ARC 规则、循环引用识别与解决
- Category vs Extension:区别与适用场景
- Block 高级用法:内存管理、变量捕获、循环引用避免
常见面试问题:
- 解释 Objective-C 的消息传递机制
- 如何在运行时动态添加方法?
- 什么是循环引用?如何避免?
- Category 和 Extension 的区别是什么?
- Block 是如何捕获外部变量的?
实战技巧:
- 理解 MRC 到 ARC 的演进历史
- 掌握 Runtime 在调试和性能优化中的应用
- 熟悉 Block 在异步编程中的使用模式
- 了解 KVO、KVC 等基于 Runtime 的特性
Objective-C 的这些特性使其在 iOS 开发中具有强大的灵活性和动态性,是现代 Swift 开发的重要基础。