之前我们创建对象的作坊:
// new 是一个类方法,主要用于创建对象
Person *p1 = [Person new];
让1个指针指向1个对象,这个指针就叫做这个对象的名字,其实呢? 其实p1是一个指针。只不过我们都这样叫。
没有名字的对象,如果我们创建了1个对象,没有用1个指针存储这个对象的地址。也就是说没有任何指针指向这个对象,那么这个对象就叫做匿名对象。
[Person new]
我们可以直接使用
[Person new] -> _name = "haha";
[[Person new] sayHi];
C语言中的static可以修饰:
- 局部变量
- 全局变量
- 函数
OC中的static关键字:
- static不能修饰属性,也不能修饰方法
- static可以修饰方法中的局部变量,如果方法中的局部变量被static修饰,那么这个变量就会变成静态变量。存储在常量区,当方法执行完毕后不会回收,下次再执行这个方法的时候回直接使用,而不会再声明了。
self用在对象方法中,指向当前对象,也就是说谁调用方法谁就是当前对象。
- (void)sayHi {
NSLog(@"self = %@", self);
NSLog(@"self = %@", self -> _name);
}
[self sayHi]
使用场景:
-
如果在方法中存在和属性同名的局域变量,你如果想要访问的同名的局部变量,直接写就可以了,但是如果想要访问当前对象的同名属性(成员变量),这时候就必须使用self关键字。
-
在对象方法中,如果要调用当前对象的其他的对象方法,必须使用self。
-
用在类方法中,当类加载的时候会将类的代码存储在代码区,self就相当于当前的这个类。
+ (void)sb { // 应该是Person类在代码段的地址 0x100001290 NSLog(@"self :", self); }
super关键字用于调用父类的方法:
[super sayHi];
OC可以像Java那样通过点去调用属性名
语法:
对象名.去掉下划线的属性名。
它内部会进行转换,将其转换为:
[对象名 set去掉下划线的属性名首字母大写:数据];
Person *p1 = [Person new];
p1.name = @"haha"
p1.age = 10;
// 会转换为
// [p1 setAge : 10];
但是如果我们的setter方法和getter方法不符合规范,那么点语法就会出问题。
我们在写一个类的时候:
- 先为类写属性。
- 再声明该属性的getter和setter方法。
- 再实现getter和setter方法。
有没有更简单的方式呢?
@property就是:它的作用是自动生成getter、setter方法的声明。因为是生成方法的声明,所以要写在@interface类的声明之中。
@interface Person : NSObject {
NSString *_name;
int _age;
}
// 自动生成_age的getter和setter方法的声明。
@propterty int age;
@end
编译器在编译的时候会将@property 数据类型 名称; 生成为:
- (void)set首字母大写的名称:(数据类型)名称;
- (数据类型)名称;
作用: 自动生成getter、setter方法的实现,所以要写在类的实现中。
@implementation Person
@synthesize age;
@end
这种写法是在Xcode4.4之前的写法,从Xcode4.4以后,Xcode对@property做了一个增强。
只需要写一个@property,编译器就会自动:
- 生成私有属性。
- 生成属性的getter、setter的声明。
- 生成getter、setter的实现。
@interface Student : NSObject
@property NSString *name;
@end
是OC中所有类的基类。根据LSP NSObject指针就可以指向任意的OC对象。 所以NSObject指针是一个万能指针,可以指向任意的OC对象。
但是,如果想要调用指向的子类对象的独有的方法,就必须要做类型转换。
NSObject *obj = [Person new];
是一个万能指针,可以指向任意的OC对象。
id是一个typedef自定义类型,在定义的时候已经添加了*,所以声明id指针的时候就不需要再添加*了。
但是它与NSObject的不同点在于:通过NSObject指针去调用对象的方法的时候,编译器会做编译检查。通过id类型的指针去调用对象的方法的时候,编译器会直接通过。 但是id指针不能使用点语法,如果使用点语法编译器会直接报错。如果我们声明一个万能指针,千万不要使用NSObject,而是使用id。
id id1 = [Person new];
[id1 sayHid];
上面,我们说到在创建对象的时候:
类名 *指针名 = [类名 new];
new实际上是1个类的方法。它的作用主要是:
- 创建对象
- 初始化对象
- 把对象的地址返回。
new方法的内部,其实是先调用的alloc方法,再调用init方法。
alloc方法是一个类的方法,作用是创建该类的对象,并把对象返回。 init方法的作用是初始化对象。
所以
Person *p1 = [Person new];
完全等价于
Person *p1 = [[Person alloc] init];
我们在创建一个对象的时候,它的属性默认值都是nil、NULL、0 但是我们想让默认属性是我们自定义的,这时候就可以重写init方法,在这个方法中按照我们自己的想法为对象的属性赋值。
重写init方法的规范:
- 必须要先调用父类的init方法,然后将方法的返回值赋值给self
- 调用init方法初始化对象有可能会失败,如果初始化失败,返回的就是nil
- 判断父类是否初始化成功,判断self的值是否为nil,如果不为nil说明初始化成功
- 如果初始化成功,就初始化当前对象的属性
- 最后,返回self的值
@implemetation Person
- (instancetype)init {
self = [super init];
if (self) {
self.name = @"jack";
}
return self;
}
@end
但是重写init方法后,每次创建出来的对象的属性值都是一样的。有时候创建对象的时候,对象的属性的值由创建对象的人来指定,而不是写死在init方法中。
这时候就需要用到自定义构造方法:
- 自定义构造方法的返回值必须是instancetype
- 自定义构造方法的名称必须以initWith开头
- 方法的实现和init的要求一样。
@interface Dog : NSObject
@property NSString *name;
@property int age;
- (void)shout;
- (instancetype)initWithName:(NSString *)name andAge:(int)age;
@end
@implementation Dog
- (void)shout {
NSLog(@"wangwang...");
}
- (instancetype)initWithName:(NSString *)name andAge:(int)age {
if (self = [super init]) {
self.name = name;
self.age = age;
}
return self;
}
@end
使用:
int main(int argc, const char *argv[]) {
Dog *d1 = [[Dog alloc] initWithName:@"huang" andAge:2];
}
- 邮箱 :charon.chui@gmail.com
- Good Luck!