oc——隐式转换——类类型

本文探讨了Objective-C中的类继承体系,重点讲解了id、Class和protocol的概念及其相互之间的隐式转换规则。文中指出,由于NSObject是所有OC类的基类,所以其子类对象可以转换为NSObject类型。此外,id类型可以视为一种通用引用,能够指向任何对象。Protocol则定义了一组方法签名,遵守协议的类实例可以转换为相应的protocol类型,如果一个协议包含了另一个协议的所有方法,那么后者可以转换为前者。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

类继承体系

@interface FBAnimal : NSObject
{
    int _food1;
    int _food2;
}

- (void)dataOffset;

@end

@interface FBAnimal ()
{
    int _food3;
    int _food4;
}

@end

@interface FBAnimal ()
{
    int _food5;
    int _food6;
}

@end

@implementation FBAnimal
{
    int _food7;
    int _food8;
}

- (void)dataOffset
{
    NSLog(@"_food1 offset = %ld", (char*)&_food1 - (char*)self);
    NSLog(@"_food2 offset = %ld", (char*)&_food2 - (char*)self);
    NSLog(@"_food3 offset = %ld", (char*)&_food3 - (char*)self);
    NSLog(@"_food4 offset = %ld", (char*)&_food4 - (char*)self);
    NSLog(@"_food5 offset = %ld", (char*)&_food5 - (char*)self);
    NSLog(@"_food6 offset = %ld", (char*)&_food6 - (char*)self);
    NSLog(@"_food7 offset = %ld", (char*)&_food7 - (char*)self);
    NSLog(@"_food8 offset = %ld", (char*)&_food8 - (char*)self);
}

@end

@interface FBDog : FBAnimal
{
    int _age;
    int _color;
}

- (void)dataOffset;

@end

@implementation FBDog

- (void)dataOffset
{
    [super dataOffset];
    
    NSLog(@"_age offset = %ld", (char*)&_age - (char*)self);
    NSLog(@"_color offset = %ld", (char*)&_color - (char*)self);
}

@end
- (void)dataOffset
{
    FBAnimal *animal = [[FBAnimal alloc] init];
    FBDog *dog = [[FBDog alloc] init];
    
    NSLog(@"-----animal data offset-----");
    [animal dataOffset];
    NSLog(@"-----dog data offset-----");
    [dog dataOffset];
}
output:
-----animal data offset-----
_food1 offset = 8
_food2 offset = 12
_food3 offset = 24
_food4 offset = 28
_food5 offset = 16
_food6 offset = 20
_food7 offset = 32
_food8 offset = 36
-----dog data offset-----
_food1 offset = 8
_food2 offset = 12
_food3 offset = 24
_food4 offset = 28
_food5 offset = 16
_food6 offset = 20
_food7 offset = 32
_food8 offset = 36
_age offset = 40
_color offset = 44
子类继承父类,不改变父类数据成员布局(数据成员相对于实例对象首地址offset),子类新增数据成员添加在父类末尾,类继承体系的这种内存布局确保了父类指针能正确访问父类数据成员对应内存空间,且不会访问到子类新增数据成员内存空间,因此把子类实例对象指针赋值给父类实例对象指针是安全的
结论:
  • NSObject是oc类root,因此任何直接或间接继承自NSObject的oc类实例对象指针都可隐式转换为NSObject实例对象指针
- (void)class_convert
{
    NSObject *object = [[NSObject alloc] init];
    FBAnimal *animal = [[FBAnimal alloc] init];
    FBDog *dog = [[FBDog alloc] init];
    
    object = animal;
    object = dog;
    animal = dog;
}

id

id是objc_object指针类型,而NSObject本质是对objc_object的封装,因为把NSObject实例对象指针赋值给id类型是安全的,因为任何直接或间接继承自NSObject的oc类实例对象指针都可隐式转换为NSObject实例对象指针,因此自然也可隐式转换为id类型
- (void)id_convert
{
    NSObject *object = [[NSObject alloc] init];
    FBAnimal *animal = [[FBAnimal alloc] init];
    FBDog *dog = [[FBDog alloc] init];
    
    id anyobj;
    anyobj = object;
    anyobj = animal;
    anyobj = dog;
}

Class

objc_object只有一个数据成员Class isa,相对于objc_object对象首地址offset为0,objc_class有若干数据成员,但其第一个数据成员为Class isa,相对于objc_class对象首地址offset为0,因此objc_object和objc_class在内存布局上满足父类和子类的关系,id是objc_object指针类型,Class是objc_class指针类型,因此Class类型可隐式转换为id类型
- (void)class_convert
{
    Class anycls;
    
    anycls = [NSObject class];
    anycls = [FBAnimal class];
    anycls = [FBDog class];
}

protocol

使用protocol类型两种方式:
  • 类<protocol1, protocol2...> *对象名
  • id<protocol1, protocol2...> 对象名
protocol无自定义数据成员,因此protocol类型本质上是id类型,只不过protocol纯粹作为接口使用,相较于id类型,protocol类型增加了编译期接口调用检查功能
结论:
  • protocol类型作为特殊id类型,自然可隐式转换为id类型,protocol类型包含接口是id类型(无接口)包含接口超集
  • 遵守protocol的oc类实例对象指针可隐式转换为protocol类型,类实例对象指针包含接口是protocol类型包含接口超集
  • 若protocol类型A遵守的所有protocol亦被protocol类型B遵守,则B可隐式转换为A,因为B包含接口是A包含接口超集
@protocol FBFeedFood1

- (void)feedRice:(int)rice andMeat:(int)meat;

@end

@protocol FBFeedFood2

- (void)feedFruit:(int)fruit andFish:(int)fish;

@end

@protocol FBFeedFood <FBFeedFood1, FBFeedFood2>

@end

@protocol FBGuard

@required
- (void)watch;

@optional
- (void)attack;

@end

@interface FBAnimal : NSObject <FBFeedFood>
{
@public
    int _food;
}

@end

@implementation FBAnimal

- (void)feedRice:(int)rice andMeat:(int)meat
{
    NSLog(@"feedRice:andMeat:");
}

- (void)feedFruit:(int)fruit andFish:(int)fish
{
    NSLog(@"feedFruit:andFish:");
}

@end

@interface FBDog : FBAnimal <FBGuard>
{
@public
    int _vol;
}

- (void)bark;

@end

@implementation FBDog

- (void)bark
{
    NSLog(@"bark");
}

- (void)watch
{
    NSLog(@"watch");
}

@end
- (void)protocol_convert
{
    FBAnimal* animal =  [[FBAnimal alloc] init];
    FBDog* dog = [[FBDog alloc] init];
    
    id anyobj;
    id<FBFeedFood1> feed1;
    id<FBFeedFood2> feed2;
    id<FBFeedFood> feed;
    id<FBGuard> guard;
    
    anyobj = feed1;
    
    feed1 = animal;
    feed1 = dog;
    
    feed2 = animal;
    feed2 = dog;
    
    feed = animal;
    feed = dog;
    
    feed1 = feed;
    feed2 = feed;
    
    //feed1 = feed2;
    
    //guard = animal;
    guard = dog;
}

泛型id

id表示未知类类型,类型是确定的,只是不知道,即无法在编译期确定确切类型,运行期才能确定确切类型,因此id类型经常被容器用来作为泛型类型,作为一切类类型(包括instance object和class object)的root,因此一切类类型(包括instance object和class object)都可隐式转换为id类型,但这种转换是不安全的,需要开发者确定是否安全
- (void)id_convert
{
    id anyobj;
    
    NSObject *object;
    FBAnimal *animal;
    FBDog *dog;
    id<FBFeedFood> feed;
    id<FBGuard> guard;
    
    object = anyobj;
    animal = anyobj;
    dog = anyobj;
    feed = anyobj;
    guard = anyobj;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值