黑马程序员---OC特有语法(二)



一、分类-Category

1.基本用途:

如何在不改变原来模型的前提下,给类扩充一些方法有2种方式:

继承;

分类(category);

  1. 分类只能增加方法,不能增加成员变量
  2.  分类方法实现中可以访问原来类中声明的成员变量
  3.  分类可以重新实现原来类中的方法,但是会覆盖掉原来的方法,会导致原来的方法没法再使用
  4.  方法调用的优先级:分类(最后参与编译的分类优先) --> 原来类 --> 父类

2.格式:

分类的声明:

@interface 类名 (分类名称)

// 方法声明;

@end

分类的实现:

@implementation 类名 (分类名称)

// 方法实现;

@end

 

@interface Person (MJ)
- (void)study;
@end
 
 
@implementation Person (MJ)
- (void)study
{
   NSLog(@"学习-----%d", _age);
}
 
- (void)test
{
   NSLog(@"Person (MJ)-test");
}
@end

3.好处:

一个庞大的类可以分模块开发;

一个庞大的类可以由多个人来编写,更有利于团队合作。

4.给系统自带的类添加分类,是分类的简单应用;

5.注意:

Category方法实现可以访问原始类的实例变量,但不能添加变量,只能添加方法。如果想添加变量,可以考虑通过继承创建子类;

Category可以实现原始类的方法(重新实现),但不推荐这么做,因为它是直接替换掉原来的方法,这么做的后果是再也不能访问原来的方法,分类优先级最高;

多个Category中如果实现了相同的方法,只有最后一个参与编译的才会有效,最后编译的会覆盖以前编译的;



二、类的本质

1.类也是个对象,类对象==类:就好像汽车是被实例后的对象,类是汽车的设计图,按照万物皆对象原则,这个设计图也是对象,也可以进行对象操作。而这类“设计图”对象有个统称:Class。简称“类对象”;

Class类型的定义:

typedef struct objc _class *Class;

int main()
{   
Person *p =[[Person alloc] init];
   // 内存中的类对象
   // 类对象 == 类
   Class c = [p class];
[c test];   
    Class c2 = [p2 class];
   
   // 获取内存中的类对象
   Class c3 = [Person class];
   
   
   NSLog(@"c=%p, c2=%p, c3=%p", c, c2, c3);
   
   //  类本身也是一个对象,输出的类地址是相同的
   return 0;
}


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

 

2.获取类对象的2中方式:

类方法,某个类的class方法;

Class c = [Person class];

对象方法,某个对象的class方法;

Person *p = [Person new];

Class c2 = [p class];

都可以获取类对象,某个类通过上面两种方式获取类对象相同。

3.类对象调用类方法:

Class c = [Person Class];

Person *p2 = [c new];

 

4.+load和+initialize:在对象初始化、第一次使用时起到监听作用。

+load:

在程序启动的时候,就会加载项目中所有的类和分类,而且加载后会调用所有类和分类的+load方法;

先加载父类,再加载子类;也就是先调用父类的+load,再调用子类的+load;

先加载原始类,再加载分类;

不管程序运行过程中有没有用到这个类,都会调用+load加载。

+initialize(当作类对象被使用时初始化):

在第一次使用某个类时(比如创建对象等),就会调用一次+initialize方法;

一个类只会调用一次+initialize方法,先调用父类的,再调用子类的。

//在Person.m中重写load和initialize方法

@implementation Person
// 当程序启动的时候,就会加载一次项目中所有的类。类加载完毕后就会调用+load方法
+ (void)load
{
   NSLog(@"Person---load");
}
 
// 当第一次使用这个类的时候,就会调用一次+initialize方法
+ (void)initialize
{
   NSLog(@"Person-initialize");
}
@end
//在Student.m中重写load和initialize方法
@implementation Student
 
// 在类被加载的时候调用
+ (void)load
{
   NSLog(@"Student---load");
}
+ (void)initialize
{
   NSLog(@"Student-initialize");
}
@end
//在Person+MJ..m中重写load和initialize方法
@implementation Person (MJ)
+ (void)load
{
   NSLog(@"Person(MJ)---load");
}
+ (void)initialize
{
   NSLog(@"Person(MJ)-initialize");
}
@end
int main()
{
Student * s = [[Studentalloc] init];
   return 0;
}


运行结果:

Person---load

Student---load

Person-initialize

Student-initialize

总结:

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

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

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



三、description方法(NSObject自带的方法)

1.-description方法(决定了实例对象的输出结果):

使用NSLog和%@输出某个对象时,会调用对象的-description方法;

拿到-description方法的返回值(NSString *)显示到屏幕上;

-description方法默认返回的是“类名+内存地址”。

2.+description方法(决定了类对象的输出结果):

使用NSLog和%@输出某个类对象时,会调用类的+description方法;

拿到+description方法的返回值(NSString *)显示到屏幕上;

+description方法默认返回的是“类名”。

3.修改NSLog的默认输出:

@implementation Person
- (NSString *)description
{
  // 下面代码会引发死循环
  // NSLog(@"%@", self);
   return [NSString stringWithFormat:@"age=%d, name=%@", _age,_name];
}@end
int main()
{
       Person* p = [person new];
       NSLog(@”%@”,p);
       //使用NSLog和%@输出某个对象时,会调用对象的-description方法
}

重写-description或者+description方法即可。

4.死循环陷阱:

如果在-description方法中使用NSLog打印self。


 

四、SEL

1.介绍:

SEL其实是对方法的一种包装,将方法包装成一个SEL类型的数据,去找对应的方法地址,找到方法地址就可以调用方法;

把方法包装成SEL类型的数据;

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

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

2.方法的存储位置:

每个类的方法列表都存储再类对象中;

每个方法都有一个与之对应的SEL类型的对象(数据);

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

SEL类型的定义:typedef struct objc _selector *SEL;。

3.SEL对象的创建:

SEL s = @selector(test);

SEL s2 = NSSelectorFromString(@"test");

4.SEL对象的其他用法:

将SEL对象转为NSString对象:

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

调用对象p的test方法:

Person *p = [Person new];

[p performSelector:@selector(test)];

每个对象方法内部都内置了一个_cmd,_cmd代表着当前方法。

五、NSLog输出增强

1.打印某个对象的地址:

NSLog(@"%p", 对象);

NSLog(@"%@", 对象); // 默认情况,未重写-description

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

3.__FILE__:源代码文件名;

4.__LINE__:NSLog代码再第几行;

5._cmd:代表着当前方法的SEL;

6.__func__:输出当前函数名。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值