黑马程序员—OC语言基础—类的深入研究

本文详细解析Java中类的加载与初始化过程,包括类的本质、类对象的使用、类的加载与初始化细节以及description方法的应用。通过实例演示,帮助开发者理解类的生命周期和内存管理。

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

————————Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ———————

一、类的本质

1.类对象的概念

类本质上也是对象,简称类对象。

Class类在框架中定义为:

typedef  struct objc_class *Class;

类名就代表类对象,每一个类只有一个类对象。

我们类比一下:

利用Person类对象创建Person类型的对象:

Person *p = [[Person alloc] init];

利用Class类创建Person类对象:

Class c = [Person class];
这句的目的是获取内存中的对象。

2.类对象的使用

int main()
{
    Person *p = [[Person alloc] init];
    
    //[Person test];
    
    // 内存中的类对象
    // 类对象 == 类
    Class c = [p class];
    [c test];
    
    Person *p2 = [[c new] init];   
    NSLog(@"00000");
}


二、类的加载与初始化(细节)

1.加载

在运行程序时,每一个类会自动先加载类+ (void)load方法,加载的顺序为:加载父类---->加载子类。

类的加载是在.m实现中系统自带的+ (void)load类方法。

即使main函数中没有调用加载,运行中也会自动先加载类+ (void)load方法。

那么我们怎么知道何时加载了这个类呢?(即如何监听类的加载?)

我们对类进行重写:

Person.h

#import <Foundation/Foundation.h>
@interface Person : NSObject
@property int age;
+ (void)test;
@end

Person.m

#import "Person.h"
@implementation Person
+ (void)test
{
    NSLog(@"调用了test方法");
}

// 当程序启动的时候,就会加载一次项目中所有的类。类加载完毕后就会调用+load方法
+ (void)load
{
    NSLog(@"Person---load");
}
// 当第一次使用这个类的时候,就会调用一次+initialize方法
+ (void)initialize
{
    NSLog(@"Person-initialize");
}
@end

Student.h

#import <Foundation/Foundation.h>
#import "Person.h"
@interface Student : Person
@end

Student.m

#import "Student.h"
@implementation Student
// 在类被加载的时候调用
+ (void)load
{
    NSLog(@"Student---load");
}
+ (void)initialize
{
    NSLog(@"Student-initialize");
}
@end

main.m

#import <Foundation/Foundation.h>
int main()
{
    [[Student alloc] init];
    return 0;
}

调用子类,是先加载初始化父类之后,才加载子类。

因此输出结果为

Person-load
Student-load
Person-initialize
Student-initialize


总结:

1.当程序启动时,就会加载项目中所有的类和分类,而且加载后会调用每一个类和分类+load方法,只会调用一次。

2.当第一次使用某个类时,就会调用当前类的+initialize方法(先找分类中有没有,然后找该类,最后找父类)。

3.先加载父类,再加载子类(先调用父类+load方法,再调用子类+load方法)

先初始化父类,再初始化子类(先调用父类+initialize方法,再调用子类+initialize方法)

因此用+initialize可以用来监听类的第一次使用!


三、description方法

description方法时NSObject自带的方法,有对象方法-description和类方法+description

1.description方法的使用

大家有没留意到我们用NSLog函数的时候,能够使用%@操作符来输出对象的一些参数,这是怎么实现的呢?

原来在每个对象类中都存在一个description方法,用于输出对象的一些特性,默认情况下,我们自定义的对象类中的这个方法不输出任何内容,我们可以重写这个方法,以输出必要的对象信息,输出格式可以自己定义。如下:

@interface Student : Person  
@property (nonatomic) int number;  
@end  
@implementation Student  
+ (NSString *)description{  
    return [NSString stringWithFormat:@"%@\nnumber = %d\n", [[super description],  number]; // 父类调用 // 输出number  
}  
@end  

下面我们设计一个Person类的子类,在这个类里,重写了description方法:

Student *s=[Student new];  
s.name = @"Xiaoming";  
s.age = 18;  
s.number = 10;  
NSLog(@"%@", s); // 通过%@输出对象参数  

结果输出为

2015-02-15 19:19:21.627 Study[2682:303] <Student: 0x10010a0c0>  
number = 10 
对比之下我们不难发现:

默认情况下,利用NSLog和%@输出对象时,结果是:<类名:内存地址>,在不重写description方法的情况下是这样输出的。

除非要输出NSString*类型的返回值,会正常输出。如:

NSString *name = @"Jack";
NSLog(@"我的名字叫%@", name);

输出结果是我们想要的

Jack

其余的若想输出我们想要的值就需要对-description进行重写。

那么如何重写?我们举一个例子。

(由于重写内容只需要修改.m文件,所以我们只将.m文件贴上来)

Person.m

#import "Person.h"
@implementation Person
//决定了实例对象的输出结果
- (NSString *)description
{
    return [NSString stringWithFormat:@"age = %d, name = %@", _age, _name];
}
@end

这样就能得到我们想要的输出结果。


总结:

当我们需要输出%@的时候,那么我们可以利用description方法重写,得到我们想要的输出结果。

同样地,若想输出一个对象p,则修改-description对象方法;若想改变类的输出,则修改+description类方法。


四、NSLog输出补充

NSLog输出行号:

NSLog(@"%d", __LINE__);

NSLog输出C语言字符串的时候,不能有中文

NSLog(@"%s", __FILE__);
但是printf可以输出中文:

printf("%s\n", __FILE__);

NSLog输出当前函数名

NSLog(@"%s\n",__func__);


五、SEL方法

SEL其实就是消息,发送消息其实就是发送SEL

1.方法的存储位置

1)每个类的方法列表都存储在类对象中

2)每个方法都有一个与之相对应的SEL类型的对象

3)根据一个SEL对象就可以找到方法的地址,进而调用方法

SEL定义

typedef struct objc selector *SEL

2.SEL对象的创建

1)SEL s = @selector(test);

比如:

SEL s = @selector(test3:);
[p performSelector:s withObject:@"123"];

2)SEL s2 =NSSelectorFromString(@"test");

比如:

NSString *name = @"test2";
SEL s = NSStringFromString(name); //把一个字符串转成一个SEL类型的数据
[p performSelector:s];

3.SEL对象的其他用法

1)将SEL对象转为NSString对象

NSString *str = NSStringFromSelector(@"selector(test)");

Person *p = [Person new];

2)调用对象p的test方法

[p performSelector:@selector(test)];

其实每个方法内部都有_cmd数据室SEL方法

//_cmd代表着当前方法
//_cmd==@selector(test2);
- (void)test2
{
    NSString *str = NSStringFromSelector(_cmd);
    NSLog(@"调用了test2方法-%@", str);
}
这里输出test2.。











评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值