Objective-C 是 C 的超集,提供了面向对象的机制,而面向对象思想里有两个重要的概念类和实例。那么 Objective-C 里类的结构是怎样的呢?我们来一起学习记录下。
首先了解几个概念,
id
在 objc.h 中我们可以看到id的定义
/// A pointer to an instance of a class.
typedef struct objc_object *id;
正如注释里说明的,id 是指向一个objc_object
的指针。那objc_object
又是什么呢?
objc_object
在 objc.h 中我们也能看到如下定义
/// Represents an instance of a class.
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
于是我们知道了objc_object
会被转换成 C 的结构体,而在这个struct中有一个 isa 指针,指向它的类别 Class。
Class
再往下看 Class的定义:
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;
发现 Class 本身指向的也是一个 C 的 struct objc_class
。
objc_class
在 runtime.h 中我们可以看到如下定义:
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE;
const char *name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE;
struct objc_method_list **methodLists OBJC2_UNAVAILABLE;
struct objc_cache *cache OBJC2_UNAVAILABLE;
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */
MetaClass
我们发现 Class 本身也有一个 isa
指针,指向的是它的 MetaClass
。
- 当我们对一个实例发送消息时(-开头的方法),会在该 instance 对应的类的 methodLists 里查找。
- 当我们对一个类发送消息时(+开头的方法),会在该类的 MetaClass 的 methodLists 里查找。
这一过程如下图所示:
- 每个 Class 都有一个 isa 指针指向一个唯一的 Meta Class
- 每一个 Meta Class 的 isa 指针都指向最上层的 Meta Class,即 NSObject 的 MetaClass,而最上层的 MetaClass 的 isa 指针又指向自己
NSClassFromString
runtime 的动态性还体现在能在运行时新建类,以下我列出了一种使用方式。假设有一个雇员类,雇员里又有工程师、设计师、产品经理这几种岗位分类。有一种比较推荐的方式是用类簇,这样使用者无需记住各种岗位,只要知道雇员即可。具体代码实现如下:
BBEmployee.h
#import <Foundation/Foundation.h>
typedef NS_ENUM(NSInteger, BBEmployeeType)
{
BBEmployeeEngineer,
BBEmployeeDesigner,
BBEmployeeProduceManager
};
@interface BBEmployee : NSObject
+ (instancetype)bb_employeeWithType:(BBEmployeeType)type;
- (instancetype)initWithEmployeeType:(BBEmployeeType)type;
- (void)work;
- (void)rest;
@end
BBEmployee.m
#import "BBEmployee.h"
static NSString *b_engineer = @"BBEngineer";
static NSString *b_designer = @"BBDesigner";
static NSString *b_productManager = @"BBProductManager";
@implementation BBEmployee
+ (instancetype)bb_employeeWithType:(BBEmployeeType)type
{
return [[self alloc] initWithEmployeeType:type];
}
- (instancetype)initWithEmployeeType:(BBEmployeeType)type
{
if ([super init]) {
NSString *className = [self employeeClassNameWithType:type];
if (className.length == 0) {
self = [BBEmployee new];
}
else
{
//根据分类动态生成子类
Class class = NSClassFromString(className);
self = [class new];
}
}
return self;
}
- (void)work
{
NSLog(@"employee start coding...");
}
- (void)rest
{
NSLog(@"employee start coding...");
}
- (NSString *)employeeClassNameWithType:(BBEmployeeType)type
{
if (type == BBEmployeeEngineer) {
return b_engineer;
}
else if (type == BBEmployeeDesigner)
{
return b_designer;
}
else if (type == BBEmployeeProduceManager)
{
return b_productManager;
}
return @"";
}
@end
BBEngineer.h
#import "BBEmployee.h"
@interface BBEngineer : BBEmployee
@end
BBEngineer.m
#import "BBEngineer.h"
@implementation BBEngineer
- (void)work
{
NSLog(@"engineer start coding...");
}
- (void)rest
{
NSLog(@"engineer start resting...");
}
@end
BBDesigner.h
#import "BBEmployee.h"
@interface BBDesigner : BBEmployee
@end
BBDesigner.m
#import "BBDesigner.h"
@implementation BBDesigner
- (void)work
{
NSLog(@"designer start coding...");
}
- (void)rest
{
NSLog(@"designer start resting...");
}
@end
BBProductManager.h
#import "BBEmployee.h"
@interface BBProductManager : BBEmployee
@end
BBProductManager.m
#import "BBProductManager.h"
@implementation BBProductManager
- (void)work
{
NSLog(@"product manager start coding...");
}
- (void)rest
{
NSLog(@"product manager start resting...");
}
@end
以上就是 NSClassFromString 结合类簇的一种使用方式,这样后期要扩展雇员类型时只需在基类中添加子类,要扩展具体子类的行为也只需在基类中暴露接口,具体实现在对应子类中处理。
这是我写的 runtime 系列文章中的一篇,还有以下几篇从其他方面对 runtime 进行了介绍
- iOS runtime之消息转发
- 深入理解 Objective-C 的方法调用流程
- iOS runtime 之 Category
- Objective-C 深入理解 +load 和 +initialize
如果您觉得本文对您有所帮助,请点击「喜欢」来支持我。
转载请注明出处,有任何疑问都可联系我,欢迎探讨。
网友评论
为什么不把MetaClass的类方法列表,放到 Class里面去?即用Class来完整的解释一个类,既存储 类方法列表,又存储实例方法列表
苹果设计MetaClass的初衷是什么?感觉用Class也可以完整的解释一个类,存储所有有关方法信息
希望能有人回答下楼主的疑惑,同时也是我的疑惑!谢谢
理解错误,class也是对象,像类发送消息实际上是像类对象发送消息,是在类的methodLists里查找,不是在类的MetaClass。
希望能有人回答一下!!