019-OC特有语法-OC笔记

学习目标

1.【了解】类的本质

2.【掌握】SEL数据类型

3.【掌握】点语法

4.【掌握】@property和@synthesize

5.【了解】动态类型和静态类型

6.【理解】id和instancetype

7.【理解】动态类型检测

8.【掌握】构造方法

一、类的本质

当程序执行的时候,程序中所有类都会自动加载到内存中的代码区(类加载)。并且一旦类加载到代码区,会直到程序结束才会被回收。

那么类以什么形式加载到代码区的呢?

系统首先会在代码区创建一个Class对象,将类的信息(类名、属性、方法)以Class对象的形式存储到这个对象之中,这个Class对象也叫做类对象。

//Person.h文件
#import <Foundation/Foundation.h>
@interface Person : NSObject
+ (void)sayHi;
@end

//Person.m文件
#import "Person.h"
@implementation Person
+ (void)sayHi{
NSLog(@"sayHi");
}
@end

//main.m文件
#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *p = [[Person alloc] init];
//调用对象的class对象方法,就可以获取到存储这个对象所属类的类对象。
Class c1 = [p class];

//调用这个类的class类方法,可以获取到这个类的Class对象的地址
Class c2 = [Person class];

//调用类方法
[c1 sayHi];

//创建对象
Person *p2 = [[c2 alloc] init];
}
return 0;
}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

//Person.h文件

#import <Foundation/Foundation.h>

@interface Person : NSObject

+(void)sayHi;

@end

//Person.m文件

#import "Person.h"

@implementationPerson

+(void)sayHi{

NSLog(@"sayHi");

}

@end

//main.m文件

#import <Foundation/Foundation.h>

#import "Person.h"

intmain(intargc,constchar*argv[]){

@autoreleasepool{

Person*p=[[Personalloc] init];

//调用对象的class对象方法,就可以获取到存储这个对象所属类的类对象。

Classc1=[pclass];

//调用这个类的class类方法,可以获取到这个类的Class对象的地址

Classc2=[Personclass];

//调用类方法

[c1 sayHi];

//创建对象

Person*p2=[[c2 alloc] init];

}

return0;

}

二、SEL数据类型

SEL的全称是selector,译为选择器。SEL是一种用来存储类的方法的数据类型。系统会在Class对象中定义SEL类型的属性,并将类的方法包装为一个SEL对象,每个SEL对象只能包装一个方法。

类的方法以SEL对象的形式的存储在Class对象之中,一个SEL对象包装一个方法,这些方法以Class对象的属性的形式存储在Class对象里。

获取存储方法的SEL对象

//获取存储这个方法的SEL对象
SEL s = @selector(方法名);

1

2

//获取存储这个方法的SEL对象

SELs=@selector(方法名);

调用方法的本质,假如Person类有个eat对象方法

Person *p = [[Person alloc] init];
[p eat];

1

2

Person*p=[[Personalloc] init];

[peat];

获取存储eat方法的SEL对象,将这个SEL对象发送给堆空间中的p对象,p对象接收到这个SEL消息后,就会根据isa指针找到存储类的Class对象。找到类对象以后,再根据SEL对象找到对应方法并执行,如果没有找到就去父类中找,直到基类还没有就报错。

调用方法的时候,其实是在为类或者对象发送SEL消息,将方法的SEL消息发送给对象,对象再根据isa指针找到类对象,查找是否有匹配的SEL对象。

手动向对象发送SEL消息,假如Person类有个eat对象方法和带参数est:andSleep:对象方法。

Person *p = [[Person alloc] init];

//包装方法为SEL对象
SEL s = @selector(eat);
//将SEL对象发送给对象
[p performSelector:s];

//如果带多个参数
SEL s1 = @selector(est:andSleep:);
//将SEL对象发送给对象,最多只能带两个参数
[p performSelector:s1 withObject:@"参数1" withObject:@"参数2"];

1

2

3

4

5

6

7

8

9

10

11

Person*p=[[Personalloc] init];

//包装方法为SEL对象

SELs=@selector(eat);

//将SEL对象发送给对象

[p performSelector:s];

//如果带多个参数

SELs1=@selector(est:andSleep:);

//将SEL对象发送给对象,最多只能带两个参数

[p performSelector:s1 withObject:@"参数1" withObject:@"参数2"];

三、点语法

访问OC对象的属性,需要使用对象调用对应属性的getter、setter方法来访问,让人感觉非常麻烦。苹果考虑到其他语言的程序员也会学习OC,就为Xcode编译器增加了一个编译器特性,可以使用点语法来替代对象调用setter和getter方法。编译器在编译的时候,会将使用点语法代码转换为调用对应的setter、getter方法。

语法:对象名.去掉下划线的属性名;

//Person.h文件
#import <Foundation/Foundation.h>
@interface Person : NSObject
{
NSString *_name;
}
//_name的setter、getter方法声明
- (void)setName:(NSString *)name;
- (NSString *)name;
@end

//Person.m文件
#import "Person.h"
@implementation Person
//_name的setter、getter方法实现
- (void)setName:(NSString *)name{

// self.age = name;
//等价于 [self setName] = name;
//会造成死循环

NSLog(@"调用了setter方法");//如果调用了setter方法会输出这行
_name = name;
}
- (NSString *)name{
NSLog(@"调用了getter方法");//如果调用了getter方法会输出这行
return _name;
}
@end

//main.m文件
#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *p = [[Person alloc] init];

//当点语法在左边就是调用p的setter方法
p.name = @"六阿哥";//为p对象的_name赋值并输出 调用了setter方法

//当点语法在右边就是调用p的getter方法
NSString *name = p.name;//将p对象的_name赋值给name,并输出 调用了getter方法

NSLog(@"%@",p.name);
//输出 调用了getter方法
//输出 六阿哥
}
return 0;
}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

//Person.h文件

#import <Foundation/Foundation.h>

@interface Person : NSObject

{

NSString*_name;

}

//_name的setter、getter方法声明

-(void)setName:(NSString*)name;

-(NSString*)name;

@end

//Person.m文件

#import "Person.h"

@implementationPerson

//_name的setter、getter方法实现

-(void)setName:(NSString*)name{

//    self.age = name;

//等价于 [self setName] = name;

//会造成死循环

NSLog(@"调用了setter方法");//如果调用了setter方法会输出这行

_name=name;

}

-(NSString*)name{

NSLog(@"调用了getter方法");//如果调用了getter方法会输出这行

return_name;

}

@end

//main.m文件

#import <Foundation/Foundation.h>

#import "Person.h"

intmain(intargc,constchar*argv[]){

@autoreleasepool{

Person*p=[[Personalloc] init];

//当点语法在左边就是调用p的setter方法

p.name=@"六阿哥";//为p对象的_name赋值并输出 调用了setter方法

//当点语法在右边就是调用p的getter方法

NSString*name=p.name;//将p对象的_name赋值给name,并输出 调用了getter方法

NSLog(@"%@",p.name);

//输出 调用了getter方法

//输出 六阿哥

}

return0;

}

注意:

1.在setter、getter方法中慎用self,以免造成递归死循环。

四、@property和@synthesize

@property关键字

作用:程序在编译的时候,编译器会根据@property自动生成类的私有属性,并为属性自动生成对应setter、getter方法的声明。

单个属性语法:@property 数据类型 名称;

多个属性语法:@property 数据类型 名称1,名称2...;

@synthesize关键字

作用:程序在编译的时候,编译器会根据@synthesize自动生成对应属性setter、getter方法的实现。

语法:@synthesize @property的名称 = _名称;

//Person.h文件
#import <Foundation/Foundation.h>
@interface Person : NSObject
//这一行代码就相当于下面几行代码
@property int age;

/* ps:这几行代码编译的时候自动生成
{
int _age;
}
- (void)setAge:(int)age;
- (int)age;
*/
@end

//Person.m文件
#import "Person.h"

@implementation Person

//Xcode新版本里,@synthesize都不需要了,@property全部搞定。
@synthesize age = _age;//为实例变量_age自动生成setter、getter方法的实现

/* ps:这几行代码编译的时候自动生成
- (void)setAge:(int)age {
_age = age;
}
- (int)age {
return _age;
}
*/

/*
@synthesize age;//如果只写名称,则会生成以下代码
{
int age;
}
- (void)setAge:(int)age {
self.age = age;
}
- (int)age {
return age;
}
*/
@end

//main.m文件
#import <Foundation/Foundation.h>
#import "Person.h"

int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *p = [[Person alloc] init];

//使用点语法调用_age的setter、getter方法
p.age = 18;
int age = p.age;
}
return 0;
}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

//Person.h文件

#import <Foundation/Foundation.h>

@interface Person : NSObject

//这一行代码就相当于下面几行代码

@propertyintage;

/* ps:这几行代码编译的时候自动生成

{

int _age;

}

- (void)setAge:(int)age;

- (int)age;

*/

@end

//Person.m文件

#import "Person.h"

@implementationPerson

//Xcode新版本里,@synthesize都不需要了,@property全部搞定。

@synthesizeage=_age;//为实例变量_age自动生成setter、getter方法的实现

/* ps:这几行代码编译的时候自动生成

- (void)setAge:(int)age {

_age = age;

}

- (int)age {

return _age;

}

*/

/*

@synthesize age;//如果只写名称,则会生成以下代码

{

int age;

}

- (void)setAge:(int)age {

self.age = age;

}

- (int)age {

return age;

}

*/

@end

//main.m文件

#import <Foundation/Foundation.h>

#import "Person.h"

intmain(intargc,constchar*argv[]){

@autoreleasepool{

Person*p=[[Personalloc] init];

//使用点语法调用_age的setter、getter方法

p.age=18;

intage=p.age;

}

return0;

}

注意:

[email protected]的数据类型和属性的类型一致,名称和去掉下划线的实例变量名一致。

[email protected]需要指定对应属性,比如@synthesize age = _age;否则会自动生成一个跟名称同名的真私有属性。

[email protected]生成的setter、setter方法不做任何逻辑验证,如果我们希望在赋值、取值的时候需要逻辑验证,所以就得自己写。

[email protected]批量定义属性的时候类型必须一致,@synthesize批量实现类型可以不一致。

[email protected]关键字在Xcode4.4之后的版本里,已经替代了@synthesize的功能,所以我们以后写程序只需要写@property。

@property增强

1.如果属性已经存在,则不会自动生成属性,直接使用已经存在的属性。

[email protected]生成的setter、getter方法也是没有任何逻辑验证的,所以我们可以自己重新写setter或者getter方法。

[email protected]生成的属性和方法都可以被子类继承。

五、动态类型和静态类型

OC是一门弱类型语言,编译器在编译的时候在语法检测上没有强类型(比如java)语言那么严格,比如int num = 12.2;这是不会保错的。

动态类型:指针指向的对象不是本类对象,而是一个别的对象。这样的类型就叫做动态类型。比如:

Person *p = [[Student alloc] init];

1

Person*p=[[Studentalloc] init];

静态类型:指针指向的对象是本类对象,而不是一个别的对象。这样的类型就叫做静态类型。比如:

Person *p = [[Person alloc] init];

1

Person*p=[[Personalloc] init];

编译检查:在程序编译的时候,根据指针的类型,在这个类里查找方法,如果有就编译通过。

运行检查:在程序运行的时候,根据对象检查是否有这个方法,如果有才会执行,没有就报错。

六、id和instancetype

id是一个万能指针,可以指向任意OC对象,可以作为方法的返回值,还有可以作为方法的参数。instancetype只能作为方法的返回值,代表返回当前类的对象,常用于构造方法。

id d = [[NSObject alloc] init];//指向任意OC对象

- (instancetype)init;//构造方法

1

2

3

idd=[[NSObjectalloc] init];//指向任意OC对象

-(instancetype)init;//构造方法

通过NSObject指针去调用指向子类对象的方法,编译器会做编译检查,如果这个方法不是NSObject类中的方法,就报错。而通过id指针去调用对象的方法,编译不做检查。

id应用场景:

#import <Foundation/Foundation.h>
@interface Person : NSObject
@property NSString *name;
@property int age;
+ (id)PersonWithName:(NSString *)name andAge:(int)age;//返回一个不确定类型的对象地址
@end

1

2

3

4

5

6

#import <Foundation/Foundation.h>

@interface Person : NSObject

@propertyNSString*name;

@propertyintage;

+(id)PersonWithName:(NSString*)name andAge:(int)age;//返回一个不确定类型的对象地址

@end

instancetype只能用在方法返回值中,代表返回当前类的对象。所以上面这个方法可以改为:

#import <Foundation/Foundation.h>
@interface Person : NSObject
@property NSString *name;
@property int age;
+ (instancetype)PersonWithName:(NSString *)name andAge:(int)age;//返回当前类的对象的地址
@end

1

2

3

4

5

6

#import <Foundation/Foundation.h>

@interface Person : NSObject

@propertyNSString*name;

@propertyintage;

+(instancetype)PersonWithName:(NSString*)name andAge:(int)age;//返回当前类的对象的地址

@end

注意:

1.被id指向的对象能直接通过编译检查,不过只能使用id指针去调用方法,不能使用点语法。

2.instancetype只能作为方法的返回值。

七、动态类型检测

在程序运行的时候通过代码去检查方法是否可以调用,避免运行时报错。

#import "Person.h"
#import "Dog.h"
int main(){
Person *p = [[Person alloc] init];

//获取要判断的方法的SEL对象
SEL s = @selector(shout);

//判断这个对象中是否有这个方法
BOOL result = [p respondsToSelector:s];

if (result == YES) {
//如果有就执行方法
[(Dog *)p shout];
}
}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

#import "Person.h"

#import "Dog.h"

intmain(){

Person*p=[[Personalloc] init];

//获取要判断的方法的SEL对象

SELs=@selector(shout);

//判断这个对象中是否有这个方法

BOOLresult=[p respondsToSelector:s];

if(result==YES){

//如果有就执行方法

[(Dog*)pshout];

}

}

判断一个对象所属的类是不是指定的类或者指定的子类

#import "Person.h"
int main(){
Person *p = [[Person alloc] init];

//判断p指向的对象是不是Person类的对象或者Person类的子类对象
BOOL result = [p isKindOfClass:[Person class]];
}

1

2

3

4

5

6

7

#import "Person.h"

intmain(){

Person*p=[[Personalloc] init];

//判断p指向的对象是不是Person类的对象或者Person类的子类对象

BOOLresult=[p isKindOfClass:[Personclass]];

}

判断对象是不是特定类型的对象,不包括子类

#import "Person.h"
int main(){
Person *p = [[Person alloc] init];

//判断p指向的对象是不是Person类的对象
BOOL result = [p isMemberOfClass:[Person class]];
}

1

2

3

4

5

6

7

#import "Person.h"

intmain(){

Person*p=[[Personalloc] init];

//判断p指向的对象是不是Person类的对象

BOOLresult=[p isMemberOfClass:[Personclass]];

}

判断一个类是不是另外一个类的子类

#import "Person.h"
#import "Student.h"
int main(){
//判断Student类是不是Person的子类
BOOL result = [Student isSubclassOfClass:[Person class]];
}

1

2

3

4

5

6

#import "Person.h"

#import "Student.h"

intmain(){

//判断Student类是不是Person的子类

BOOLresult=[Student isSubclassOfClass:[Personclass]];

}

八、构造方法

new方法内部其实是先调用了alloc方法,在堆空间中创建对象并返回对象。再用这个对象调用init方法,初始化对象的属性,返回这个已经被初始化的对象。这个init方法是定义在NSObject中的构造方法,作用就是初始化对象。

Person *p = [Person new];
//上面表达式等价于
Person *p = [[Person alloc] init];

1

2

3

Person*p=[Personnew];

//上面表达式等价于

Person*p=[[Personalloc] init];

重写构造方法 创建对象的时候直接为对象的属性赋自定义默认值

//Person.h文件
#import <Foundation/Foundation.h>
@interface Person : NSObject
@property NSString *name;
@property int age;
@end

//Person.m文件
#import "Person.h"
@implementation Person
- (instancetype)init {
//先让父类初始化,返回已经初始化的对象
self = [super init];

//调用init初始化对象有可能初始化失败,失败则返回nil。所以我们可以判断一下
if (self) {
self.name = @"无名";
self.age = 18;
}
return self;
}
@end

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

//Person.h文件

#import <Foundation/Foundation.h>

@interface Person : NSObject

@propertyNSString*name;

@propertyintage;

@end

//Person.m文件

#import "Person.h"

@implementationPerson

-(instancetype)init{

//先让父类初始化,返回已经初始化的对象

self=[superinit];

//调用init初始化对象有可能初始化失败,失败则返回nil。所以我们可以判断一下

if(self){

self.name=@"无名";

self.age=18;

}

returnself;

}

@end

自定义构造方法 让调用者创建对象的时候可以为对象的属性自定义赋值

//Person.h文件
#import <Foundation/Foundation.h>
@interface Person : NSObject
@property NSString *name;
@property int age;
- (instancetype)initWithName:(NSString *)name andAge:(int)age;
@end

//Person.m文件
#import "Person.h"
@implementation Person
- (instancetype)initWithName:(NSString *)name andAge:(int)age{
//先让父类初始化,返回已经初始化的对象
self = [super init];

//调用init初始化对象有可能初始化失败,失败则返回nil。所以我们可以判断一下
if (self) {
self.name = name;
self.age = age;
}
return self;
}
@end

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

//Person.h文件

#import <Foundation/Foundation.h>

@interface Person : NSObject

@propertyNSString*name;

@propertyintage;

-(instancetype)initWithName:(NSString*)name andAge:(int)age;

@end

//Person.m文件

#import "Person.h"

@implementationPerson

-(instancetype)initWithName:(NSString*)name andAge:(int)age{

//先让父类初始化,返回已经初始化的对象

self=[superinit];

//调用init初始化对象有可能初始化失败,失败则返回nil。所以我们可以判断一下

if(self){

self.name=name;

self.age=age;

}

returnself;

}

@end

注意:

1.重写构造方法时,必须先调用父类的init方法,来初始化父类的属性,再初始化子类的属性。

2.自定义构造方法名必须以initWith开头,必须有返回值(类型为instancetype),一定是一个对象方法。

时间: 2024-10-13 08:42:37

019-OC特有语法-OC笔记的相关文章

【黑马程序员】————OC特有语法

一.点语法 点语法的本质还是方法调用 p.age = 10; // [p setAge:10]; int a = p.age; // [p age]; 二.成员变量的作用域 @public : 在任何地方都能直接访问对象的成员变量 @private : 只能在当前类的对象方法中直接访问(@implementation中默认是@private) @protected : 可以在当前类及其子类的对象方法中直接访问 (@interface中默认就是@protected) @package : 只要处在

黑马程序员-OC特有语法:分类category,给NSString增加方法计算字符串中数字的个数

1:分类的使用场景:想对一个类,扩充一些功能,而又不改变原来类的模型,也不用继承,这时OC中的特有语法:分类可以做到: 当然分类也是一个类,也需要声明和实现,声明在.h文件中,实现在.m文件中,格式如下 // 声明 @interface  类名  (分类名称) @end // 实现 @implementation 类名 (分类名称) @end 2:分类的好处,当一个类比较庞大时,不同的部分可以放到不同的分类中,也方便团队中类的开发: 3:分类使用注意: a:分类不能增加成员变量,只能对原类增加方

OC特有语法-01

点语法 利用点语法替换set方法和get方法 方法调用 Student *stu = [Student new]; [stu setAge:100]; int age = [stu age]; 点语法 stu.age = 100; int age = stu.age; 点语法的本质 其实点语法的本质还是方法调用 当使用点语法时,编译器会自动展开成相应的方法 死循环注意 - (void) setAge:(int)age { // 下面的代码会引发死循环 self.age = age; } - (i

oc特有语法

分类 问题 1.什么是分类? 就是把一个类的功能,分出一部分来放在一个独立的文件中 2.分类的语法是什么样的? @interface Person(SuperMan) 3.分类与类是什么关系? 分类依赖类而存在,没有类也就没有分类 4.分类有什么作用? 用于把一个比较庞大的类,分割开来,具有相同功能的方法放到一个分类中 把太多的功能封装到一个类中,导致类文件过于庞大 5.分类中能够像类一样声明成员变量? 不能,分类中不能够定义成员变量 1>.难以维护 2>.难以使用 6.分类中能否访问原类中的

OC特有语法问题总结

分类 问题1.什么是分类? 就是把一个类的功能,分出一部分来放在一个独立的文件中2.分类的语法是什么样的?@interface Person(SuperMan) 3.分类与类是什么关系? 分类依赖类而存在,没有类也就没有分类4.分类有什么作用?用于把一个比较庞大的类,分割开来,具有相同功能的方法放到一个分类中把太多的功能封装到一个类中,导致类文件过于庞大 5.分类中能够像类一样声明成员变量?不能,分类中不能够定义成员变量1>.难以维护2>.难以使用 6.分类中能否访问原类中的成员变量? 7.分

黑马程序员——OC的特有语法

1. 分类-Category 1. 基本用途:Category  分类是OC特有的语言,依赖于类. ? 如何在不改变原来类模型的前提下,给类扩充一些方法?有2种方式 ● 继承 ● 分类(Category) 2. 格式 ? 分类的声明 @interface 类名 (分类名称) // 方法声明 @end ? 分类的实现 @implementation 类名 (分类名称) // 方法实现 @end 3. 好处 ? 一个庞大的类可以分模块开发 ? 一个庞大的类可以由多个人来编写,更有利于团队合作 ? 4

iOS开发——面试篇&amp;OC基本语法总结(面试)

OC基本语法总结(面试) C和OC对比 OC中主要开发在什么平台上的应用程序? 答:可以使用OC开发Mac OS X平台和iOS平台的应用程序 OC中新增关键字大部分是以什么开头? 答:OC中新增关键字大部分是以@开头 OC中新增加了那些数据类型? 答: Block类型 指针类型(Class, id类型) 空类型 特殊类型(SEL, nil) 面向对象特性是什么? 答:继承性,封装性,多态性 import和#include有什么区别? 答:import 的功能和 include一样, 是将右边的

黑马程序员— OC基本语法、类和对象、三大特性

------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- 第一讲     OC简介及基本语法 Objective-C简称OC是在C语言的基础上,增加了一层最小的面向对象语法,完全兼容C语言,也就是可以在OC代码中混入C语言代码,甚至是C++代码.可以使用OC开发Mac OS X平台和IOS平台的应用程序.简单的介绍了一下OC,下面我们来看看OC的基本语法,学习OC之前我们先学习了C语言,因为OC是在C语言的基础上的一门开发语言,因此OC的很多基本语法

OC点语法和变量作用域

OC点语法和变量作用域 一.点语法 (一)认识点语法 声明一个Person类: 1 #import <Foundation/Foundation.h> 2 3 @interface Person : NSObject 4 { 5 int _age;//默认为@protected 6 } 7 8 - (void)setAge:(int)age; 9 - (int)age; 10 11 @end Person类的实现: 1 #import "Person.h" 2 3 @imp