1、实例变量修饰符
(1)、public:可以在本类中被访问到,也可以在其他类中被访问到,当然,在子类中也能被访问到;
(2)、private:可以在本类中被访问到,但是不可以在其他类中被访问到,在子类中也同样不可以被访问到;
(3)、protected:可以在本类中被访问到,但是不可以在其他类中被访问,然而在子类中却是可以被访问到,没有写明修饰符的默认情况下的实例变量都是用protected修饰的;
(4)、package:介于public和private之间
如果是在当前所在包以外,则是private,其在子类中是不可以被访问的;
如果是在当前所在包以内,则是public,其在子类中是可以被访问的。
**
不管是用什么修饰符修饰的,我们都可以在其他类中查看这个类的属性,但是有些修饰符修饰的属性我们是不能操作的,但是,在**
@implement{
//定义属性,修饰符任意
}
@end
**这个属性也是私有属性,但是这个在外界是看不到的,而private定义的属性可以看到,例:
**
//情形一
Person:NSObject{
@private
int _age;
}
Person *p = [Person new];
p->age; //这里只有warning
//情形二
@implementation Person{
int _age;
}
Person *p = [Person new];
p->age; //这里会报错
//情形三
@implementation Person{
@public
int _age;
}
Person *p = [Person new];
p->age; //这里还是会报错
2、私有“方法”与公有“方法”(OC中只有消息,没有方法)
如果只有方法的实现,没有方法的声明,那么这个方法就是私有方法,但是OC没有真正意义上的私有方法:
@implementation Person{
int _age;
}
-(void)test {
NSLog(@"age = %@",_age);
}
@end
//其他类里面
Person p = [Person new];
[p test]; //报错,访问不到test
id p = [Person new];
[p test]; //这里会打印->age = 0;
//或者
Person p = [Person new];
[p performSelector:@selector(test)];//这里也会打印->age = 0;
3、@property与@synthesize
(1)、@property是一个编译器指令,使用它我们可以代替setter和getter方法的声明,因此我们可以省去自己编写setter和getter方法声明;
(2)、@synthesize也是编译器指令,用来生成setter与getter方法的实现;
*PS:
a、后期的@property已经同时具有声明和实现setter和getter方法的功能,如果有 @property int num;
那么他会自动找到以 _ 开头同名的成员变量进行赋值,即:@property int num;
等价于 @property int num = _num;
;
b、如果我们自己重写了setter与getter方法,那么编译器不会自动帮我们生成这两个方法;
c、我们可以不写成员变量的声明,并且我们没有同时重写setter和getter方法(可以重写其中一个),如方法一所示,系统会自动为我们生成一个带 _ 的成员变量,但是它是一个私有变量,只能在本类中使用;
**
下面三个写法是等价的:
//写法一
@interface Person
@property int age;
@end
@implementation Person
@end
//写法二
@interfae Person{
int _age;
}
-(void)setAge:(int)age;
-(int)age;
@end
@implementation Person
-(void)setAge:(int)age {
_age = age;
}
-(int)getAge{
return _age;
}
@end
//写法三
@interface Person{
int _age;
}
@property int age;
@end
@implementation Person
@synthesize age = _age; //这句不能写在@interface里面
@end
注意
如果不是按照标准写法,@synthesize property = _property;的写法,而是@synthesize property;那么生成的setter与getter方法会是如下:
@interface Person{
int _age; //a
int age; //b
}
@property int age;
@end
@implementation Person
@synthesize age;
/*
等价于
-(void)setAge:(int)age {
age(b行的age)= age(形参age); //而非_age = age;
}
-(int)age{
return age;//而非return _age;
}
*/
@end
4、description方法:
description方法是系统自带的方法,用来打印一个类或者一个对象的所有成员变量的信息,但是我们可以重写这个方法,来输出我们需要的内容。
Person *p = [Person new];
NSLog(@"%@",p); //自动调用系统内的-(NSString *)description; 方法
Class *c = [p class];
NSLog(@"%@",c);//自动调用系统内的+(NSString *)description; 方法
综上,打印对象自动调用对象方法,打印类会调用类方法。
注意:在重写对象description方法的时候,不能用%@输出self,否则会造成死循环。
-(NSString *)description {
NSLog(@"%@",self); //死循环,这里输出会调用description方法
return [NSString stringWithFormat:@"%@",self];//同理这里也会造成死循环
}
5、new与alloc和init
Person *p = [Person new];
做了三件事:
a、开辟存储空间
b、初始化所有的属性(成员变量)
c、返回对象的地址
Person *p = [Person alloc];
做了三件事:
a、开辟存储空间
b、将所有属性设置为0
c、返回当前对象的地址
Person *p2 = [p init];
做的事:
a、初始化成员变量,一般默认情况下的实现是什么都不做
b、返回初始化对象的地址
**PS:alloc返回的地址和init返回的地址是同一个地址 **
6、id与instancetype
(一)相同点:
都是万能指针,一般在多态中使用
(二)不同点:
a、id可以作为返回值,作为形参,定义变量,而instancetype只能用来作为返回值;
b、当将一个类型赋值给一个其他的类型时,instancetype能在编译时判断出其实际类型,从而给出警告,而id不会
例:
//构造方法一
- (id)init {
if(self = [super init]) {
_age = 10;
}
}
//构造方法二
- (instancetype)init {
if(self = [super init]) {
_age = 10;
}
}
NSString *str = [[Person alloc] init];
NSInteger len = [str length];
NSLog("%@",len);
使用构造方法一时,编译过程不会报错,但是使用构造方法二会报错,而当运行时,两者都会奔溃,因为Person类里面根本没有length方法。
7、类工厂方法
类工厂方法:用于快速创建对象的类方法,我们称之为类工厂方法。
用途:主要用于给对象分配存储空间以及初始化这个存储空间。
规范:
a、一定是类方法
b、方法名称以类的名称开头,首字母小写
c、一定有返回值,返回值是id或者instancetype
例:
+(instancetype)person {
Person *p = [Person alloc];
Person *p1 = [p init];
return p1;
}
int main(){
Person *p1 = [[Person alloc] init]; //普通方法
Person *p2 = [Person person]; //类工厂方法
}
PS:自定义类工厂方法是苹果的一个规范,一般情况下,我们会给一个类提供自定义构造方法和自定义类工厂方法用于创建一个对象。
8、类工厂方法的继承
@interface Person :NSobject
@end
@implementation
+ (instancetype)person {
return [[Person alloc] init];
}
@end
@interface Student : Person
@property NSInteger no;
@end
int main() {
Student *s = [Student person]; //这句相当于Student *s =[[Person alloc] init];而Person中是没有no属性的
NSInteger *no = s.no; //这句会报错
}
总结:类工厂方法,最好不要用类名来创建,正确做法:
+ (instancetype)person {
return [[self alloc] init];
}
9、类
(1)、类的本质:
类也是一个对象,这个对象会在类第一次被使用时创建,只要有了类对象,以后就可以通过类对象来创建实例对象,而实例对象中有一个isa指针,指向自己的类对象
(2)、作用:
类对象中保存了当前对象所有的对象方法,当给一个实例对象方法消息的时候,会根据实例对象中的isa指针去对应的类对象中查找。
(3)、isa与类继承关系
类对象中存储属性与对象方法,元类对象存储类方法,实例对象调用方法时根据isa指针来从类对象或者元类对象中查找方法进行调用。
10、@class与#import
区别:@class只是告诉编译器这是一个类,不会将这个类拷贝到另一个类中,而#import会将类的代码拷贝到另一个中,因为@class不拷贝,所以不能访问类中属性与方法。
1)、如果A类拷贝了B类,B类拷贝了C类,如果C类做了修改,那么A类和B类都会重新拷贝一次C类,如果#import放在了.h文件中,那么有间接关系的类,都会重新拷贝
2)、如果在.h文件中用@class在.m文件中使用#import,如果一个文件发生了修改,那么只有直接引用这个文件的类才会重新拷贝,间接引用的类不会去重新拷贝,所以合理使用@class能提升编译效率
3)、@class能有效避免文件循环导入,如果A导入B,B导入A,会造成编译器报错,所以此时应该在.h文件中用@class在.m文件中用#import,这样能避免循环导入