OC 知识复习 (五) 特有语法

20240202052735_image.png

1.创建1个对象,这个对象在内存中是如何分配的,

1).子类对象中有自己的属性和所有父类的属性,
2).代码段中的每1个类都有1个叫做isa的指针,这个指针指向它的父类,一直指到NSObject
	[p1 sayHi]; //假设p1是Person对象,
	先根据p1指针找到p1指向的对象,然后根据对象的isa指针找到Person类
	搜索Person类中是否有这个sayHi方法 如果有执行如果没有 
	就根据类的isa指针找父类NSObject 中如果没有就报错,
copy success

2)。在代码段存储类的那块空间是个什么类型的 在代码段中存储类的步骤

a. 先在代码段中创建1个Class对象,Class是Foundation框架中的1个类.这个Class对象就是用来存储类信息的,

b,将类的信息存储在这个Class对象之中: 这个Class对象,至少有3个属性 类名: 存储的这个类的名称。 属性s: 存储的这个类具有哪些属性 方法s: 存储的这个类具有哪些方法, 所以,类是以Class对象的形式存储在代码段的,存储类的这个Class对象 我们也叫做类对象。用来存储类的1个对象 所以,存储类的类对象也有1个叫做isa指针的属性 这个指针指向存储父类的类对象,


  1. SEL 全称叫做 selector 选择器: SEL 是1个数据类型,所以要在内存中申请空间存储数据SEL其实是1个类。SEL对象是用来存储1个方法的,

2,类是以Class对象的形式存储在代码段之中, 类名:存储的这个类的类名,NSString 还要将方法存储在类对象之中,如何将方法存储在类对象之中, 1).先创建1个SEL对象。 2).将方法的信息存储在这个SEL对象之中, 3).再将这个SEL对象作为类对象的属性,

  1. 拿到存储方法的SEL对象 1).因为SEL是1个typedef类型的 在自定义的时候已经加 "*"了, 所以我们在声明SEL指针的时候 不需要加*

    2).取到存储方法的SEL对象, SEL s1 = @selector(方法名);

4.调用方法的本质, [p1 sayHi]; 内部的原理: 1). 先拿到存储sayHi方法的SEL对象,也就是拿到存储sayHi方法的SEL数据, SEL消息, 2). 将这个SEL消息发送给p1对象, 3). 这个时候,p1对象接收到这个SEL消息以后 就知道要调用方法 4). 根据对象的isa指针找到存储类的类对象 5). 找到这个类对象以后 在这个类对象中去搜寻是否有和传入的SEL数据相匹配的,如果有 就执行 如果没有再找父类 直到NS0bject OC最重要的1个机制:消息机制, 调用方法的本质其实就是为对象发送SEL消息[p1 sayHi];为p1对象发送1条sayHi消息。

6,手动的为对象发送SEL消息, 1),先得到方法的SEL数据: 2),将这个SEL消息发送给p1对象 调用对象的方法 将SEL数据发送给对象。 -(id)performSelector:(SEL)aSelector; Person *p1=[Person new]; SEL s1 = @selector(sayHi); [p1 performselector:s1]; 与[p1 sayHi]效果是完全一样的.

if ([p1 respondsToSelector:s1]) {
    [p1 performSelector:s1];
}

copy success
1
2
3
4

在 Objective-C 中,你是将消息(或者说方法)发送给特定的对象,而不是类。所以当你使用 performSelector: 方法时,实际上是在告诉一个特定的对象去执行与给定选择器关联的方法。

以下面这段代码为例:

Person *p1 = [Person new];
SEL s1 = @selector(sayHi);
[p1 performSelector:s1];
copy success
1
2
3

在这里,你创建了一个 Person 类型的对象 p1,然后告诉 p1 执行名为 sayHi 的方法。运行时系统会查找 p1 对象(包括它的父类)是否有一个名为 sayHi 的方法,如果找到了,就执行那个方法;如果没有找到,则会抛出异常。

对于你的问题,假设 sayHi 方法在多个类中都存在,例如 Person 类和 Dog 类,并且它们的实现方式可能会有所不同。你如何知道选的是哪个方法呢?答案取决于你向哪个对象发送 sayHi 消息。如果你创建的是 Person 对象并向其发送 sayHi 消息,那么就会调用 Person 类中的 sayHi 方法;如果你创建的是 Dog 对象并向其发送 sayHi 消息,那么就会调用 Dog 类中的 sayHi 方法。

这就是动态方法派发的一个重要特性:实际执行的方法取决于消息接收者(即对象)的类型,而不是选择器。

是的,@selector() 可以接受任何方法名作为参数,无论这个方法在当前上下文中是否存在。然而,如果你试图将一个未知的选择器(代表不存在的方法)用于 performSelector: 或其他需要选择器的方法,那么在运行时将会抛出异常。

例如,以下代码段将会在运行时抛出异常:

Person *p1 = [Person new];
SEL unknownSelector = @selector(methodThatDoesNotExist);
[p1 performSelector:unknownSelector];
copy success
1
2
3

因为 Person 类没有实现 methodThatDoesNotExist 方法,所以当你尝试让 p1 执行这个方法时,程序便会崩溃。

不过,你可以通过 respondsToSelector: 方法进行检查,以避免这种情况:

Person *p1 = [Person new];
SEL unknownSelector = @selector(methodThatDoesNotExist);
if ([p1 respondsToSelector:unknownSelector]) {
    [p1 performSelector:unknownSelector];
} else {
    NSLog(@"'Person' does not respond to 'methodThatDoesNotExist'");
}
copy success
1
2
3
4
5
6
7

这样,如果 Person 类没有实现 methodThatDoesNotExist 方法,就不会尝试执行这个方法,从而避免了运行时错误。

请注意,尽管可以使用不存在的方法创建选择器,但在编码时最好避免这种情况,除非你已经掌握了 Objective-C 的动态特性并且明确知道你在做什么。否则,这可能会导致难以调试的运行时错误。

当你需要使用 performSelector: 方法来调用需要参数的方法时,Objective-C 提供了两个额外的方法:performSelector:withObject:performSelector:withObject:withObject:。这两个方法可以分别传递一个和两个参数。

例如,假设你有一个 Person 类,其中包含一个接受一个 NSString 参数的 setName: 方法:

@class Person : NSObject
- (void)setName:(NSString *)name;
@end
copy success
1
2
3

你可以使用 performSelector:withObject: 来调用这个方法:

Person *p1 = [[Person alloc] init];
[p1 performSelector:@selector(setName:) withObject:@"John"];
copy success
1
2

如果你的方法接受两个参数,比如 setFirstName:lastName:,你可以使用 performSelector:withObject:withObject: 方法:

[p1 performSelector:@selector(setFirstName:lastName:) withObject:@"John" withObject:@"Doe"];
copy success
1

请注意,由于 performSelector:withObject:performSelector:withObject:withObject: 方法的限制,它们只能接受对象类型的参数(即所有继承自 NSObject 的类的实例)。对于基本数据类型(如 int、double 等),你需要将它们装箱到相应的 NSNumber 对象中;对于 C 字符串,你需要把它们转换为 NSString 对象。


OC的对象如果要为属性赋值或者取值 就要调用对应的getter或者setter.

2.使用点语法来访问对象的属性,

语法:

对象名,去掉下划线的属性名;

p1.name = @"jack";

这个时候就会将@"jack"赋值给p1对象的 name属性,

NSString *name = p1.name; 

把p1对象的name属性的值取出来.
copy success

3.点语法的原理

p1.age = 18;
这句话的本质并不是把18直接赋值给p1对象的_age属性,
点语法在编译器编译的时候,其实会将点语法转换为调用setter、getter的代码,

	1),当使用点语法赋值的时候,这个时候编译器会将点语法转换为调用setter方法的代码

		对象名.去掉下划线的属性名 = 数据;
		转换为:
		[对象名 set去掉下划线的属性名首字母大写:数据];
		p1.age = 10;
		[p1 setAge:10];
		
	2),当使用点语法取值的时候,这个时候编译器会将点语法转换为调用getter方法的代码
		
		对象名.去掉下划线的属性名;
		转换为:
		[对象名 去掉下划线的属性名];
		int age = p1.age;
		int age = [p1 age];
copy success

4.注意,

1),在getter和setter中慎用点语法,因为有可能会造成无限递归 而程序崩溃,

2)点语法在编译器编译的时候  会转换为调用setter getter方法的代码,

		p1.name = jack"
		[p1 setName:@"jack"]
		
		NSString *name = pl.name;
		NSString *name = [p1 name];
	
	 如果我们的setter方法和getter方法名不符合规范 那么点语法就会出问题,
copy success

  1. @property

     1).作用: 自动生成getter、setter方法的声明,
     				因为是生成方法的声明,所以应该写在@interface类的声明之中,
     					
     2).语法:
     				@property 数据类型 名称;
     				@property int age;
     				 3).原理:
     				编译器在编译的时候,会根据@property生成getter和setter方法的声明,
     		
     		@property 数据类型 名称;
     		生成为:
     		- (void)set首字母大写的名称:(数据类型)名称;
     		- (数据类型)名称;
     		
     		@property int age;
     		- (void)setAge:(int)age;
     		- (int)age;
    
    copy success

  1. @synthesize

     1).作用:自动生成getter、setter方法的实现,所以,应该写在类的实现之中,
     
     2).语法:
     	@synthesize @property名称;
     	@interface Person : Nsobject
     	{
     		int _age;
     	}
     	@property int age;
     	@end
     	
     	@implmentation Person
     	@synthesize age;
     	@end
     	
     a.生成1个真私有的属性,属性的类型和@synthesize对应的@property类型一致
     	属性的名字和@synthesize对应的@property名字一致,
     	
     b.自动生成setter方法的实现.
     	实现的方式:将参数直接赋值给自动生成的那个私有属性(非接口里面带下划线的属性),并且没有做任何的逻辑验证
     	
     c.自动生成getter方法的实现.
     	实现的方式:将生成的私有属性的值返回
     	
     3.希望@synthesize不要去自动生成私有属性了。getter setter的实现中操作我们已经写好的属性就可以了,
     	语法:
     		@synthesize @property名称 = 已经存在的属性名;
     		@synthesize age =_age;
     		
     		 1),不会再去生成私有属性.
     		 2).直接生成setter getter的实现
     					setter的实现:把参数的值直接赋值给指定的属性,
     					gettter的实现:直接返回指定的属性的值,
    
    copy success

批量声明

	1),如果多个@property的类型一致,可以批量声明,
				@property float height,weight;
	
	2)@synthesize也可以批量声明,
				@synthesize name = _name,age = _age,weight =_weight,height = _height;
copy success

TIP

@property只是生成getter setter 的声明 @synthesize是生成gettersetter 的实现:

这种写法是Xcode4.4之前的写法。从Xcode4.4以后.Xcode对@property做了1个增强

2.@property增强 只需要写1个@property 编译器就会自动 1)生成私有属性。 2).生成getter setter的声明, 3).生成getter setter的实现, @property NSString *name; 做的事情 1).自动的生成1个私有属性,属性的类型和@property类型一致 属性的名称和@property的名称一致 属性的名称自动的加1个下划线, 2).自动的生成这个属性的getter setter方法的声明 3).自动的生成这个属性的getter setter方法的实现

setter的实现:直接将参数的值赋值给自动生成的私有属性, getter的实现:直接返回生成的私有属性的值。


  1. NSObject.

    是OC中所有类的基类,根据LSP NSObject指针就可以指向任意的OC对象,所以NSObiect指针是1个万能指针,可以执行任意的OC对象,

    缺点:如果要调用指向的子类对象的独有的方法,就必须要做类型转换,

  2. id指针 是1个万能指针,可以指向任意的0C对象,

     1). id是1个typedef自定义类型 在定义的时候已经加了*
     		所以声明id指针的时候不需要再加*了。
     
     2)id指针是1个万能指针,任意的OC对象都可以指.
    
    copy success

3.NSObject和id的异同,

相同点:万能指针 都可以执行任意的0C对象,
不同点:
	通过NS0bject指针去调用对象的方法的时候,编译器会做编译检查,
	通过id类型的指针去调用对象的方法的时候,编译器直接通过,无论你调用什么方法,
注意: id指针只能调用对象的方法 不能使用点语法,如果使用点语法就会直接报编译错误 。
copy success

20240202171352_image.png

20240202171529_image.png

20240202172048_image.png

20240202172308_image.png

20240202202240_image.png

20240202202353_image.png

20240202202454_image.png

20240202203421_image.png