一、成员变量的作用域
@public : 在任何地方都能直接访问对象的成员变量。
@private : 只能在当前类的对象方法中直接访问(@implementation中默认是@private)。@protected: 可以在当前类及其子类的对象方法中直接访问 (@interface中默认就是@protected)。
@package : 只要处在同一个框架中,就能直接访问对象的成员变量。
@interface和@implementation中不能声明同名的成员变量。
例如:
#import <Foundation/Foundation.h>
@interface Person : NSObject
{
int _no;
@public // 在任何地方都能直接访问对象的成员变量
int _age;
@private // 只能在当前类的对象方法中直接访问
int _height;
@protected // 能在当前类和子类的对象方法中直接访问
int _weight;
int _money;
}
- (void)setHeight:(int)height;
- (int)height;
- (void)test;
@end
二、@property和@synthesize
@property:可以自动生成某个成员变量的setter和getter声明
@synthesize自动生成age的setter和getter实现,并且会访问_age这个成员变量
例如:
#import<Foundation/Foundation.h>
@interfacePerson : NSObject
{
int _age;
// int age;
int _height;
double _weight;
NSString *_name;
}
//@property:可以自动生成某个成员变量的setter和getter声明
@propertyint age;// 这一句可以代替下面的两行
//-(void)setAge:(int)age;
//-(int)age;
@propertyint height;
//-(void)setHeight:(int)height;
//-(int)height;
-(void)test;
@propertydouble weight;
@propertyNSString *name;
@end
三、构造方法
构造方法:用来初始化对象的方法,是个对象方法,-开头
重写构造方法的目的:为了让对象创建出来,成员变量就会有一些固定的值
重写构造方法的注意点
1.先调用父类的构造方法([super init])
2.再进行子类内部成员变量的初始化
#import <Foundation/Foundation.h>
#import "Person.h"
#import "Student.h"
/*
重写构造方法的注意点
1.先调用父类的构造方法([super init])
2.再进行子类内部成员变量的初始化
*/
int main()
{
// Person *p = [Person new];
/*
完整地创建一个可用的对象
1.分配存储空间 +alloc
2.初始化 -init
*/
// 1.调用+alloc分配存储空间
// Person *p1 = [Person alloc];
// 2.调用-init进行初始化
// Person *p2 = [p1 init];
// // 调用-init进行初始化
// Person *p3 = [Person new];
/
// // 每个Person对象创建出来,他的_age都是10
Person *p4 = [[Person alloc] init];
Student *stu = [[Student alloc] init];
NSLog(@"------");
}
return 0;
}
自定义构造方法的规范
1.一定是对象方法,一定以 - 开头
2.返回值一般是id类型
3.方法名一般以initWith开头
四、分类
作用:在不改变原来类模型的前提下,给类扩充一些方法。
两种方式:
分类
继承
格式:
分类的声明
@interface 类名 (分类名称)
// 方法声明
@end
分类的实现
@implementation 类名 (分类名称)
// 方法实现
@end
使用注意:
1.分类只能增加方法,不能增加成员变量
2.分类方法实现中可以访问原来类中声明的成员变量
3.分类可以重新实现原来类中的方法,但是会覆盖掉原来的方法,会导致原来的方法没法再使用
4.方法调用的优先级:分类(最后参与编译的分类优先) --> 原来类 --> 父类
// 优先去分类中查找,然后再去原来类中找,最后再去父类中找
5.多个Category中如果实现了相同的方法,只有最后一个参与编译的才会有效
四、类的本质
其实类的本质也是个对象,是Class类型的对象。
1.当程序启动时,就会加载项目中所有的类和分类,而且加载后会调用每个类和分类的+load方法。只会调用一次。
2.当第一次使用某个类时,就会调用当前类的+initialize方法
3.先加载父类,再加载子类(先调用父类的+load方法,再调用子类的+load方法)
先初始化父类,再初始化子类(先调用父类的+initialize方法,再调用子类的+initialize方法)
例如:
//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
#import <Foundation/Foundation.h>
#import "Person.h"
#import "Student.h"
#import "GoodStudent.h"
/*
1.当程序启动时,就会加载项目中所有的类和分类,而且加载后会调用每个类和分类的+load方法。只会调用一次。
2.当第一次使用某个类时,就会调用当前类的+initialize方法
3.先加载父类,再加载子类(先调用父类的+load方法,再调用子类的+load方法)
先初始化父类,再初始化子类(先调用父类的+initialize方法,再调用子类的+initialize方法)
*/
int main()
{
// [[GoodStudent alloc] init];
return 0;
}
void test1()
{
Person *p = [[Person alloc] init];
//[Person test];
// 内存中的类对象
// 类对象 == 类
Class c = [p class];
[c test];
Person *p2 = [[c new] init];
NSLog(@"00000");
}
void test()
{
// 利用Person这个类创建了2个Person类型的对象
Person *p = [[Person alloc] init];
Person *p2 = [[Person alloc] init];
Person *p3 = [[Person alloc] init];
// 获取内存中的类对象
Class c = [p class];
Class c2 = [p2 class];
// 获取内存中的类对象
Class c3 = [Person class];
NSLog(@"c=%p, c2=%p, c3=%p", c, c2, c3);
// 类本身也是一个对象,是个Class类型的对象,简称类对象
/*
利用Class 创建 Person类对象
利用 Person类对象创建 Person类型的对象
*/
}
五、description方法
默认情况下,利用NSLog和%@输出对象时,结果是:<类名:内存地址>
1.会调用对象p的-description方法。
2.拿到-description方法的返回值(NSString*)显示到屏幕上。
3.-description方法默认返回的是“类名+内存地址”。
4.如果在-description方法中使用NSLog打印self,注意死循环。
六、SEL
SEL其实是对方法的一种包装,将方法包装成一个SEL类型的数据,去找对应的方法地址。找到方法地址就可以调用方法。消息就是SEL。
#import <Foundation/Foundation.h>
#import "Person.h"
int main()
{
Person *p = [[Person alloc] init];
[p test2];
// NSString *name = @"test2";
//
// SEL s = NSSelectorFromString(name);
//
// [p performSelector:s];
// 间接调用test2方法
//[p performSelector:@selector(test2)];
//[p test3:@"123"];
// SEL s = @selector(test3:);
//
// [p performSelector:s withObject:@"456"];
//[p test2];
// 1.把test2包装成SEL类型的数据
// 2.根据SEL数据找到对应的方法地址
// 3.根据方法地址调用对应的方法
return 0;
}
七、NSLog输出增强
__FILE__ :源代码文件名
__LINE__ :NSLog代码在第几行
_cmd :代表着当前方法的SEL
// 下面的代码会引发死循环
- (void)test {
[self performSelector:_cmd];
}