对象名.去掉下划线的属性名;
p1.name = @"小明";
NSString *name = p1.name;
如果是赋值,就自动调用这个属性对应的setter方法.
如果是取值,就自动调用这个属性对应的getter方法.
原理
HMPerson *p1 = [HMPerson new];
p1.name = @"小明"; //不是把@"小明"直接赋值给p1对象的_name属性
原理:==编译器在编译的时候==,其实就把点语法替换为了调用setter方法的代码
替换规则:
对象名.去掉下划线的属性名 = 数据;
替换成:
[对象名 set去掉下划线的属性名并首字母大写:数据];
p1.name = @"小明";
[p1 setName:@"小明"];
所以使用点语法为属性赋值本质之上还是调用setter方法.
格式 : 对象名.去掉下换线的属性名;
NSString *name = p1.name;
本质上是调用p1对象的_name属性的getter方法.
取到的值是getter方法的返回值.
点语法是一个编译器特性.原理:==编译器在编译的时候==,其实就把点语法替换为了调用setter(getter)方法的代码!
几个使用注意
1. setter getter方法内如果使用点语法赋值,就容易无限递归,死循环!慎用!!
2. 如果属性没有封装setter getter自然是掉用不了点语法的.
3. 点语法是在编译器编译的时候转换位对应的set get.如果方法名字不符合规范也是无法使用的.
4. 点语法不是访问成员变量,而是方法的调用.->才是访问属性.
5. 在等号的左边就是赋值,set. 在等号右边或者没有等号就是取值.调用get.
@interface
{
NSString *_name;
}
@property 数据类型 去掉下划线名称;
@property NSString* name;
原理: 在编译器编译的时候.会根据@property自动生成getter setter方法的声明.
生成方法的规则:
a. 先生成setter方法的声明
- (void)set名称首字母大写并去掉下划线:(数据类型)去掉下划线名称;
b. 再生成getter方法的声明.
- (数据类型)去掉下划线名称;
@property NSString *name;
- (void)setName:(NSString *)name;
- (NSString *)name;
- @property的类型要和属性的类型一致
- 名称要和属性名称一致(去掉下划线)
- 这样才能生成符合规范的的getter setter
- 批量声明����开发式可不要用哦~
@property NSString * name,stuNum;
当property的类型相同的时候可以批量声明
@synthesize @pro名称;
@synthesize name;
注意,这个@synthesize后面的名称必须要是@interface中@property中的名称
一定是前面已经声明过的
syn异步
interfae HMPerson : NSObject
{
NSString *_name;
}
@property NSString *name;
@end
@implementation HMPerson
@synthesize name;
//@synthesize做的事情
//编译器在编译的时候
//a. 先自动生成一个私有属性,类型和@synthesize对应的@pro类型一致
//名称和@pro的名称一致,不带下划线
//这个属性是一个真私有属性,也就是声明在@implementation里面.
{
NSString *name;
}
//b. 自动的生成setter方法的实现.
//方法的内部什么都没做,直接将参数赋值给了它自动生成的属性.
- (void)setName:(NSString *)name
{
//私有属性name和形参name同名 必须用self->
self->name = name;
}
//c. 自动生成getter方法
//直接返回生成的私有属性的值
- (NSString *)name
{
return name;
}
语法
@synthesize @pro名称 = 已经存在的属性名
@synthesize name = _name;
1. 代表@synthesize 不会再去生成私有属性了.
2. 自动生成setter方法的实现.
3. 直接将参数的值赋值给后面指定的属性.
4. 自动生成getter方法的实现,直接返回后面指定的属性的值.
- (void)setName:(NSString *)name
{
_name = name;
}
- (NSString *)name
{
return _name;
}
@synthesize name = _name , age = _age , stuNum = _stuNum;
类型不同也可以批量.
==老项目中老框架中会有这样的写法.要看懂!==
@interface HMPerson : NSObject
//只写这一个@property 就有很多代码产生
@property NSString *name;
@end
@implementation HMPerson
@end
----------编译后---------
@interface HMPerson : NSObject
- (void)setName:(NSString *)name;
- (NSString *)name;
@end
@implementation HMPerson
{
NSString *_name;
}
- (void)setName:(NSString *)name
{
_name = name;
}
- (NSString *)name
{
return _name;
}
@end
@property 想要生成的属性的类型 想要生成的名称一致并去掉下划线.
@property int num1,num2,num3;
//前提是类型相同的情况下.
如果需要可以自己重写
- (void)setName:(NSString *)name
{
if(name.length < 2)
{
_name = @"无名";
}
else
{
_name = name;
}
}
1. 如果你重写了setter方法,仍然会自动生成getter方法.反之亦然.
2. 但自动生成的是没有逻辑验证的
3. 如果你同时重写了两个方法,@property就不会再自动生成那个私有属性了.只能自己加
1. 数据类型不一样可以强行赋值
int num = 10.1
2.这样只有警告,居然还能打印出来.
HMPerson *p1 = @"jack";
3. 警告,而且也可以%@打印
NSString *str = [HMPerson new];
强类型语言 Java C#…
弱语言的优缺点
HMPerson *p1 = [HMPerson new];
指针指向的对象是一个本类对象
> HMPerson *p1 = [HMStudent new]
> //指向的是子类对象或者@"jack";
>
1. 所以OC中任意的指针可以指向任意的对象.编译器最多给个警告
2. 当指向的是一个本类对象或者子类对象的时候,编译器不会给警告.
3. 因为要存储一个对象的地址,其实任意类型的指针变量都不所谓,任何类型指针都是8个字节.
[对象名 方法名]; //这句话能不能通过编译,编译器的判断准则是什么?
[p1 sayHi];
检查对象(p1)的类(HMPerson)中有没有这个方法(sayHi),如果有就编译通过,没有就编译报错
NSString *str = [HMPerson new]
[str length];
检查NSString 中 有没有length方法.有就通过.
HMPerson *p1 = [HMStudent new];
[p1 sayHi];//可以编译
[p1 study];//person类型没有这个方法,但是student有,所以编译仍然不能通过.
[(HMStudent*)p1 study]; //可以通过强制类型转换骗过编译器
NSString *str = [HMPerson new];
[str study];//编译肯定报错.
[(HMStudent*)str study];//编译通过运行还是会崩
1. 程序在运行的时候,当要调用一个指针指向的方法的时候,仍然会做一个检查.
2. 会去检查这个指针指向的对象当中是否真的有这个方法,
3. 如果真的有就执行,如果没有就直接运行报错
[str length];//还是崩.这样崩了就是运行检查.
NSObject *obj = [HMPerson new];
obj = @"jack";
obj = [NSArray new];
//C字符串不行!基础数据类型不行!
obj = "rose"
obj = 12;
obj = YES;//YES就是1 也不行!
[obj sayHi];//不行 NSObject类型没有sayHi
[(HMPerson*)obj sayHi]; //可以
当NSObject指针指向子类对象的时候,如果要调用子类对象的独有成员,就必须要做类型转换.
NSObject *obj = @"jack";//obj指针指向的是一个NSString对象
[obj length];//报错,NSObject类型没有length方法
[(NSString*)obj length];//强制转换就行了.
NUUInterger len = [(HMPerson *)obj length];
NSLog(@"len = %lu",len);
注意易错点点!!
1. 编译的时候检查person有length方法.
2. 运行的时候运行字符串对象@"jack"中的length方法.得到的结果是4!
id id1 = @"jack";
id id2 = [HMPerson new];
id id3 = 12;//不可以
和NSObject*一样是万能指针
- 当编译器在编译的时候如果发现指针的类型是id类型的
- 这个时候,直接通过,不会做任何的编译检查.不需要强转
id id1 = [HMPerson new];
[id1 sayHi]; //可以运行
id1.name = @"jack";//不行,虽然有set方法.
[id1 setName:@"jack"];//可以
- 如果指针的类型是NSObject类型的时候,编译器在编译的时候会做编译检查.需要做类型转换
- 如果是id类型编译器在编译的时候就会直接通过.不用做类型转换.
HMPerson *p1 = [HMPerson person];
HNStudent *s1 = [HMStudent person];//继承以后有person方法
但是会报警告,用一个Student接person.
+ (id)person
{
return [HMPerson new];
}
就算接返回值的类型不对也不会给警告,因为id不会做检查.
但是方法里面本来就是创建的person对象,
+ (id)person
{
//返回一个调用这个类方法的对象
return [self new];
}
在类方法中创建对象的时候类名不要写死了,而是写一个self代表当前类
//现在就不会报类型不匹配的警告了因为是id类型
NSString *str = [HMPerson person];
NSString *str1 = [HMStudent person];
1. 这个时候,要接收这个类方法返回的对象的地址,实际上任意类型的指针都可以
2. 编译器都不会报警高,因为id类型不会做编译检查,id是一个无类型指针.
希望类方法通过哪一个类型调用,返回值类型就是那个对象类型.有警告提示!
这个方法通过哪一个类去调用,就代表返回的是哪一个类的对象.返回值有类型
+ (instancetype)person
{
return [self new];
}
这个才是终极写法.创建的是调用类的对象,返回值是调用类的类型.
调用当前对象的class方法.
id obj = [self.class new];//点语法换成调用方法
id obj = [[self class] new];
就算通过了编译检查,运行不一定会成功.
编译检查只是检查指针的类型,我们希望检查一下这个指针指向的这个对象当中到底有没有这个方法
BOOL res = [str respondsToSelector:@selector(length)];
//判断指针对象的方法中是不是有SEL发送的指定的方法.
res == YES //就执行
==可以判断对象中有没有这个方法,有没有实现== 给五颗星
BOOL res = [p1 isKindOfClass:[HMPerson class];
//判断s1对象是不是person的对象或者是不是其子类对象
isMenberOfClass: (Class)aClass
只能判断本类对象不包括子类
isSubclassOfClass: (Class)aClass;
这个是类方法 判断一个类是不是另一个类的子类.
- new方法是一个类方法,哪个类调用就创建哪个类的对象,并且初始化,返回值是这个对象地址.
- new方法内部什么都没做,而是调用了alloc方法和init方法
- alloc方法首先是一个类方法,这个方法做的事情:创建对象
- init方法是对象方法,做的事:初始化这个对象.为对象的属性赋默认值.
- 创建对象我们也可以这么做.
[HMPerson new] == [[HMPerson alloc] init];
1. 这两种完全等价
2. 开发中多用这种方法更专业
3. init方法 我们叫做构造方法
4. init方法不仅仅为对象赋默认值还有别的事情
5. 创建的对象务必要调用init方法初始化以后才可以使用,否则就有可能会出问题!
init叫做构造方法 ==一定是对象方法,一定要注意(系统的方法)==
我们的快速创建对象一定是类方法.
为什么对象一创建出来,对象的属性的默认值就是0,nil,NULL,因为构造方法中初始化了
- (instancetype)init
{
//1. 先调用父类的init方法,因为父类的init方法不仅仅是初始化属性的值,还做了别的重要的事情
//2. 返回的值是当前的对象,调用init方法有可能执行失败返回nil,将返回值赋给self,再判断有没有执行成功
self = [super init];
//3. 判断self有没有初始化成功,再去按照自己的方式赋值
if(self != nil)//说明父类的init方法执行成功
{
self.name = @"小明";
self.age = 17;
self.height = 176.6f;
}
//4. 最后返回当前对象
return self;
}
- 对象创建出来不希望属性默认值是0 nil,而是指定的值.
- 如果你有一段代码想在对象创建的同时执行,也可以把代码写在init方法里面.
- 重写init方法的规范 必须要调用父类的init方法,并且赋值给self判断成功.
if(self = [super init])
//一般直接这么写.判断
- 肯定是对象方法
- 返回值写 instancetyper
- 方法名必须以initWith开头
- (instancetype)initWithName:(NSString *)name andAge:(int)age andHeight:(float)height
{
if(self = [super init])
{
self.name = name;
self.age = age;
self.height = height;
}
return self
}
调用:
HMPerson *p2 = [[HMPerson alloc] initWithName:@"jack" andAge:18 andHeight:176.7f;
+ (instancetype)personWithName:(NSString *)name andAge:(int)age
{
return [[self alloc]initWithName:@"name" andAge:age];
}