Objective-C 重要特性

🎯 消息传递机制

动态消息传递 vs 静态函数调用

核心区别

  • C++:编译时确定函数地址,直接调用
  • Objective-C:运行时动态查找方法实现
// Objective-C 消息传递
[object doSomething];

// 编译后转换为:
objc_msgSend(object, @selector(doSomething));

消息传递流程

  1. 在对象的方法列表中查找 selector
  2. 如果找不到,沿着继承链向上查找
  3. 如果还找不到,进入消息转发机制

消息转发机制

// 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();
}

💡 面试要点总结

必掌握知识点:

  1. 消息传递机制:理解 objc_msgSend 工作原理
  2. Runtime 应用:方法交换、关联对象的实际场景
  3. 内存管理:ARC 规则、循环引用识别与解决
  4. Category vs Extension:区别与适用场景
  5. Block 高级用法:内存管理、变量捕获、循环引用避免

常见面试问题:

  • 解释 Objective-C 的消息传递机制
  • 如何在运行时动态添加方法?
  • 什么是循环引用?如何避免?
  • Category 和 Extension 的区别是什么?
  • Block 是如何捕获外部变量的?

实战技巧:

  • 理解 MRC 到 ARC 的演进历史
  • 掌握 Runtime 在调试和性能优化中的应用
  • 熟悉 Block 在异步编程中的使用模式
  • 了解 KVO、KVC 等基于 Runtime 的特性

Objective-C 的这些特性使其在 iOS 开发中具有强大的灵活性和动态性,是现代 Swift 开发的重要基础。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容