黑马程序员——8、OC语言(分类、类的本质)

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


一、分类Category

  1. 分类的格式

* 分类的声明

@interface 类名 (分类名称)

// 方法的声明

@end

* 分类的实现

@implementation 类名 (分类名称)

// 方法的实现

@end

  2. 分类的使用注意:

1》分类只能增加方法,不能增加成员变量;

2》分类方法实现中,可以访问原来类中声明的成员变量;

3》分类可以重新实现原来类中的方法,但是会覆盖掉原来的方法(分类的优先级最高),导致原来的方法失效;

4》方法调用的优先级:分类(最后参与编译的分类才会有效) > 原来的类 > 父类。

/* 给NSString类添加分类*/
#import <Foundation/Foundation.h>

@interface NSString (NumberCount)
+ (int)numberCountOfString:(NSString *)str;

- (int)numberCount;
@end

@implementation NSString (NumberCount)
+ (int)numberCountOfString:(NSString *)str
{
    // 1.定义变量计算数字的个数
    int count = 0;
    // 2.遍历
    for (int i=0; i<str.length; i++) {
        unichar c = [str characterAtIndex:i];
        
        if (c>=48 && c<=57) {
            count++;
        }
    }
    
    return count;
}

- (int)numberCount
{
//    int count = 0;
//    for (int i=0; i<self.length; i++) {
//        unichar c = [self characterAtIndex:i];
//        if (c>='0' && c<='9') {
//            count++;
//        }
//    }
//    return count;
    
    return [NSString numberCountOfString:self];
}
@end

  3. 分类和继承

分类和继承都是在不改变原来模型的情况下,给类扩充一些方法。



二、类的本质

  1. 类对象

类也是一个对象,是Class类型的对象,简称“类对象”。

类对象的本质:typedef struct objc_class *Class;

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

    Person *p = [[Person alloc] init];
    Class c = [p class];
    
    // 类名代表类对象 类对象==类
    [c test]; // [Person test];

    // 利用Person这个类创建了2个Person类型的对象
    Person *p1 = [[Person alloc] init];
    
    Person *p2 = [[Person alloc] init];
    
    // 类本身也是一个对象,是个Class类型的对象,简称类对象
    
    /*
     利用Class 创建 Person类对象(类对象)
     
     利用Person类对象 创建 Person类型的对象(实例对象)
     */
    
    // 获取内存中的类对象
    // 1.方式一
    Class c1 = [p1 class];
    Class c2 = [p2 class];
    // 2.方式二
    Class c3 = [Person class];
    
    NSLog(@"c1=%p, c2=%p, c3=%p", c1, c2, c3); // 相同类对象

  2. +load和+initialize

+ load

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

加载顺序:先加载父类,在加载子类,最后加载分类。

不管程序有没有用到这个类,都会调用+load这个方法把类加载进来。

+ initialize

当第一次使用某个类时,就会调用当前类的+initialize方法,只会调用一次。

调用顺序:先调用(初始化)父类的,再调用(初始化)子类。如果有分类,则分类会替换原来的类来进行初始化。

#import <Foundation/Foundation.h>

@interface Person : NSObject
@property int age;

+ (void)test;
@end

@implementation Person

+ (void)test
{
    NSLog(@"调用了test类方法");
}

// 当程序启动的时候,就会加载一次项目中所有的类,类加载完毕后就会调用+load方法
// 先加载父类,再加载子类
+ (void)load
{
    NSLog(@"Person load");
}

// 当第一次使用这个类的时候,就会调用一次initialize方法
+ (void)initialize
{
    // 监听
    NSLog(@"Person initialize");
}
@end



三、NSLog输出特性和SEL对象

  1. description方法

1》-description方法

Person *p = [[Person alloc] init];
p.age = 20;
p.name = @"jack";
    
// 默认情况下,利用NSLog和%@输出对象时,结果是:<类名: 内存地址>
// 1.会调用对象p的-description方法
// 2.拿到-description方法的返回值(NSString *),显示到屏幕上
// 3.-description方法默认返回的时"类名+内存地址"
NSLog(@"%@", p);

2》+description方法

Class c = [Person class];
    
// 默认情况下,利用NSLog和%@输出类对象时,结果是:类名
// 1.会调用类的+description方法
// 2.拿到+description方法的返回值(NSString *),显示到屏幕上
// 3.+description方法默认返回的时"类名"
NSLog(@"%@", c);

3》重写description方法

打印对象的时候用:

// 决定了实例对象的输出结果
- (NSString *)description
{
    // NSLog(@"%@", self); // 死循环
    
    return [NSString stringWithFormat:@"age=%d, name=%@", _age, _name];
}

// 决定类对象的输出结果
+ (NSString *)description
{
    return NSStringFromClass([self class]);
}

  2. SEL对象

每个类的方法列表都封装在类对象中,每个方法都有一个与之对应的SEL类型的对象,根据这个SEL对象就能找到方法的地址,然后调用方法。

对象放消息,就是发SEL。

SEL类型的本质:typedef struct objc_selector *SEL;

1》SEL对象的创建

SEL s1 = @selector(方法名);
// 【NSString对象转换为SEL对象】
SEL s2 = NSSelectorFromString(@"方法名"); // 知道方法名字符串 ==> 调用该方法,用SEL类型数据接收字符串,再调用方法

2》SEL对象的使用

// 【将SEL对象转换为NSString对象】
NSString *str = NSStringFromSelector(@selector(方法名));
// 调用对象p的test方法:
Person *p = [[Person alloc] init];
[p performSelector:@selector(test)];

NSString *str = @"12345";
[p test3:str];
[p performSelector:@selector(test3:) withObject:str]; // performSelector方法可以有参数

间接调用对象方法:

* 首先会把test2包装成SEL类型的数据 (创建SEL数据:@selector(test2))

* 根据SEL数据找到对应的方法地址

* 根据方法地址调用对应的方法

3》_cmd

_cmd 代表当前方法的SEL,每个方法内部都有这么一个变量。

- (void)test2
{
    // 死循环
    // [self performSelector:_cmd];
    
    // _cmd 代表当前方法的SEL,每个方法内部都有这么一个变量..
    NSString *str = NSStringFromSelector(_cmd);
    
    NSLog(@"test2.., _cmd=%@", str);
}

  3. NSLog输出增强

NSLog(@"%d", __LINE__); // 代码当前行号

// NSLog输出C语言字符串的时候,不能有中文
// NSLog(@"%s", __FILE__); // 输出不成功!(可以先做转换)
printf("%s\n", __FILE__); // 当前文件路径
NSLog(@"%s", __func__); // 当前函数名



四、类名、对象名、方法名的输出

  1. 输出类名

@implementation Person
// + (NSString *)description
// {
//     return NSStringFromClass([self class]);
// }
@end

int main()
{
    Class c = [Person class];
    NSLog(@"%@", c);
}

  2. 输出对象

我们可以用类名和对象的地址(不是对象指针的地址),来表达一个什么类型的什么对象。

@implementation Person
// - (NSString *)description
// {
//     return self;
// }
@end

int main()
{
    Person *p = [[Person alloc] init];
    NSLog(@"%@", p);
}

  3. 输出方法名

@interface Person : NSObject
- (NSString)fangfaming1;
+ (NSString)fangfaming2;
@end

@implementation Person
- (NSString)fangfaming1
{
    NSString *str = NSStringFromSelector(_cmd);
    
    return str;
}
+ (NSString)fangfaming2
{
    NSString *str = NSStringFromSelector(_cmd);
    
    return str;
}
@end

int main()
{
    Person *p = [Person new];
    NSString *s = [p fangfaming1];

    NSString *s2 = [Person fangfaming2];
    NSLog(@"%@ %@", s, s2);
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值