【Objective-C】13-类和方法的本质

来源:互联网 发布:怎么查手机的网络制式 编辑:程序博客网 时间:2024/05/19 17:10



一、类的本质

1.1 类的本质是对象,是Class类型的对象,简称类对象。

1.2 类的创建过程:Person *p = [[Person alloc] init];

     1>利用Class创建Person类对象

     2>利用Person类对象,创建Person类型的对象。

1.3 获取内存中类对象的两种方式

     1> Class c = [p class];  //调用对象的class对象方法

     2> Class c1 = [Person class];  //调用类的class类方法

1.4 利用上面两行代码获得的Class对象是同一个对象,指向的是同一个内存地址。可通过打印对象的内存地址验证。

1.5 类对象和类是同一个对象,所以可以通过类对象创建类的对象。

#import <Foundation/Foundation.h>@interface Person : NSObject+ (void)test;@end@implementation Person+ (void)test{NSLog(@"调用了test类方法");}@endint main() {//利用Person这个类创建了两个Person类型的对象Person *p = [[Person alloc] init];Person *p2 = [[Person alloc] init];Class c = [p class]; // 通过对象方法class获取内存中的类对象Class c2 = [Person class]; // 通过类方法class获取内存中的类对象NSLog(@"c=%p,c2=%p", c, c2); //打印两个类对象的内存地址,验证是否同一个对象。Class c3 = [p class];[c3 test]; //利用类对象调用类方法Person *p3 = [[c3 new] init]; // 利用类对象创建对象return 0;}



、类的加载和初始化

2.1 当程序启动的时候,就会加载一次项目中所有的类和分类(不管程序运行过程中有没有用到这个类)。类被加载完毕的时候就会调用每个类和分类的+load方法。只会调用一次。

2.2 当第一次使用某个类的时候,就会调用一次当前类的+initialize方法。之后不管调用这个类多少次,整个项目生命周期中只会调用一次。

2.3 加载顺序:先加载父类,再加载子类(先调用父类的+load方法,再调用子类的+load方法)。

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

      分类和类都实现+initialize方法,类的+initialize方法会被分类覆盖。

2.4 +initialize方法可以用来监听类,如果想在类第一次被使用时执行某些行为,可以把代码放在+initialize方法中。

#import <Foundation/Foundation.h>@interface Person : NSObject@end@implementation Person//当程序启动的时候,就会加载一次项目中所有的类。类被加载完毕的时候就会调用+load方法+ (void)load{NSLog(@"Person--load");}//当第一次使用这个类的时候,就会调用一次+initialize方法。整个项目生命周期中只会调用一次+(void)initialize{NSLog(@"Person--initialize");}@end@interface Student : Person@end@implementation Student//在类被加载的时候调用+ (void)load{NSLog(@"Student--load");}+(void)initialize{NSLog(@"Student--initialize");}@end@interface Person(MM)@end@implementation Person(MM)//在类被加载的时候调用+ (void)load{NSLog(@"Person (MM)--load");}// 使用类时会分类会覆盖原来类的+initialize方法+(void)initialize{NSLog(@"Person (MM)--initialize");}@endint main() {Person *p = [[Person alloc] init];Person *p2 = [[Person alloc] init];Student *s = [[Student alloc] init];return 0;}
输出结果:

2015 - 02 - 08 05:28 : 31.400 04 - 类的加载[1465:46788] Person--load
2015 - 02 - 08 05 : 28 : 31.401 04 - 类的加载[1465:46788] Student--load
2015 - 02 - 08 05 : 28 : 31.401 04 - 类的加载[1465:46788] Person(MM)--load
2015 - 02 - 08 05 : 28 : 31.402 04 - 类的加载[1465:46788] Person(MM)--initialize
2015 - 02 - 08 05 : 28 : 31.402 04 - 类的加载[1465:46788] Student--initialize

三、description方法

description方法分对象方法和类方法。默认使用NSLog和%@打印对象时,直接就输出对象的类名和内存地址。默认使用NSLog和%@打印类时,直接就输出类名。当需要显示自定义信息时,需要重写description方法。

3.1 -description

     默认情况下,利用NSLog和%@(NSLog(@"%@",p);)输出对象时,结果是:<类名:内存地址>
    1>会调用对象p的-description方法
    2>拿到-description方法的返回值(NSString *)显示到屏幕上
    3>-description方法默认返回的是“类名+内存地址”
    4>决定了实例对象的输出结果

    5>-description的重写
    - (NSString *)description
    {
    //下面代码会引发死循环
    // NSLog(@"%@",self);

    return [NSString stringWithFormat:@"age=%d,name=%@",_age,_name];
    }


3.2 +description

Class c = [Person class];

默认情况下,利用NSLog和%@(NSLog(@"%@",c);)输出类对象时,结果是:类名
1>会调用类的+description方法
2>拿到+description方法的返回值(NSString *)显示到屏幕上
3>决定了类对象的输出结果
NSLog(@"%@",c);
4>+description的重写

+(NSString *)description
{
    return @"ABC";
}

#import <Foundation/Foundation.h>@interface Person : NSObject@property int age;@end@implementation Person// 重写-description方法- (NSString *)description{return[NSString stringWithFormat : @"age=%d", _age];}// 重写+descrition方法+(NSString *)description{return @"调用+description";}@endint main() {Person *p = [[Person alloc] init];NSLog(@"%@", p);//调用-descriptionClass c = [Person class];NSLog(@"%@", c);return 0;}
输出结果:

2015 - 02 - 08 06:06 : 49.832 05 - description[1582:51889] age = 0
2015 - 02 - 08 06 : 06 : 49.834 05 - description[1582:51889] 调用+description

四、NSLog方法补充:
NSLog(@"%p", &p);  // 指针变量的地址
NSLog(@"%p", p);     // 对象的地址
NSLog(@"%@", p);    // <类名:对象地址>


NSLog(@"%d", __LINE__);  // 打印行号
//NSLog(@"%s",__FILE__);   //NSLog输出C语言字符串的时候,不能有中文
printf("%s\n", __FILE__);
NSLog(@"%s", __func__);  //输出当前函数名


五、SEL

5.1 SEL是一种数据类型,全称Selector,表示方法的存储位置。

5.2 方法调用的本质:SEL其实是对方法的一种包装,将方法包装成一个SEL类型的数据,去找对应的方法地址。找到方法地址就可以调用方法。其实消息就是SEL。

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

[p test];

1.把test包装成SEL类型的数据
2.根据SEL数据找到对应的方法地址
3.根据方法地址调用对应的方法

4.以上三个步骤会有缓存机制,当第一次通过SEL找到相对应的方法时,会把该查询结果缓存起来,便于下次的查找。

5.3 通过SEL间接调用方法

间接调用test方法
1>无参数
[p performSelector : @selector(test2)];
2>有参数
[p test : @"123"];
[p performSelector : @selector(test:) withObject:@"456"];

5.4 方法的存储位置

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

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

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

4>SEL类型的定义

typedef struct objc_selector *SEL;

5.5 SEL对象的创建

SEL s = @selector(test);

SEL s2 = NSSelectorFromString(@"test");

5.5  知道方法名的字符串,调用方法
NSString *name = @"test2";
SEL s = NSSelectorFromString(name);
[p performSelector: s];

5.6 _cmd

每个方法内部都包含着一个_cmd,代表着当前方法。如果要打印出来的话,需要调用NSStringFromSelector(_cmd);
NSString *str = NSStringFromSelector(_cmd);
NSLog(@"%@", str);

5.7 注意

在方法内部调用下面语句会引发死循环
[self performSelector : _cmd];


#import <Foundation/Foundation.h>@interface Person : NSObject+ (void)test;-(void)test2;-(void)test3:(NSString *)str;@end@implementation Person+ (void)test{NSLog(@"+test");}-(void)test2{NSString *str = NSStringFromSelector(_cmd);NSLog(@"调用了test2方法---%@", str);//会引发死循环//[self performSelector:_cmd];}-(void)test3:(NSString *)str{NSLog(@"test3---%@", str);}@endint main() {Person *p = [[Person alloc] init];NSString *name = @"test2";SEL s = NSSelectorFromString(name);[p performSelector : s];[p performSelector : @selector(test2)];SEL s2 = @selector(test3:);//间接调用带参数的方法,别忘了方法后面的冒号,因为冒号也是属于方法名的一部分[p performSelector : s2 withObject : @"hhh"];return 0;}

输出结果:

2015-02-08 07:42:12.070 SEL[1725:61095] 调用了test2方法---test2
2015-02-08 07:42:12.072 SEL[1725:61095] 调用了test2方法---test2
2015-02-08 07:42:12.072 SEL[1725:61095] test3---hhh


0 0
原创粉丝点击