------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
一、点语法
利用点语法替代set、get方法。
点在 = 左边就是调用set方法。在 = 右边就是调用get方法。
不允许在set、get方法内使用使用点语法,会造成死循环。
<span style="font-family:SimHei;font-size:18px;"> Person *p = [[Person alloc] init];
p.age = 10;//相当于[p setAge:10];
int c = p.age;//相当于int c = [p age];</span>
二、类中变量的作用域
@private | 只能在当前类的对象方法中直接访问(子类中有该变量,但是不能访问)如果实在想访问只能通过set方法和get方法。 |
@protected | 可以在当前类以及子类的对象方法中直接访问(直接访问的意思是直接“_变量名”这种方式访问。)默认都是这种的。 |
@public | 任何地方都可以访问 |
@package | 同一个”体系内“(框架)可以访问,介于@private和@public之间。 |
<span style="font-family:SimHei;font-size:18px;">@interface Person : NSObject
{
int _age;
}
@property int age;
//上面的@property会让系统自动给你生成set和get方法
- (void)setAge:(int)age;
- (int)age;
//一定要注意写法。
//注意,xcode是很笨的,如果你这样写
@property int _age;
//那么会生成下面那样的set方法和get方法。
- (void)set_age:(int)_age;
- (int)_age;
@end</span>
2、在使用了@property的前提下,在.m文件中使用@synthesize,会自动帮助你生成set、get方法实现。
<span style="font-family:SimHei;font-size:18px;">//完整的写法
//对@property int age;进行方法实现
//并且会访问_age这个成员变量。
@synthesize age = _age;
</span>
= 左边代表你要实现的哪个@property
= 右边是代表你要访问哪个成员变量。
如果你在声明文件中没有声明_age成员变量,会自动帮你生成一个_age成员变量,它的作用域是private类型的。
使用注意:
- @synthesize age;默认会访问名字为age的成员变量,而不是_age;如果没有age就会自动生成@private类型的age成员变量。
- @synthesize age =_age;这样就会访问_age的成员变量。如果没有_age就会自动生成@private类型的_age成员变量。
3、还有更简化的,在.m中不连@synthesize都不用写。不用声明成员变量。
<span style="font-family:SimHei;font-size:18px;">@interface Person : NSObject
@property int age;
@property int weight;
@end</span>
最简便写法有个缺点,成员变量是private的,如果想让子类直接访问,只能定义成员变量。
注意:
如果你自己写了set或get方法,@property会帮你生成没写的get或set方法。
如果你自己写了set和get方法,那么@property就不会帮你set和get方法,并且!生成_成员变量!所以要注意!
四、id类型
id是万能指针,能指向\操纵任何OC对象
定义id类型指针时不能加*,因为它本身就是指针类型。
五、构造方法
要完整的创建一个可用的对象
- 分配存储空间
- 初始化
之前的new方法里面就做了上面个事情。它内部调用了两个方法。
- 分配存储空间 + alloc
- 初始化 -init
但是new太死板,所以开发中基本不不使用。
//不用这种方法后
Person *p = [Personnew];
常用的是下面的方式:
//先分配内存给对象p1
Person *p1 = [Person alloc];
//然后把初始化后的p1给p2
Person *p2 = [p1 init];
//合并到一块
Person *p3 = [[Person alloc] init];
其中,init就是构造方法。
重写构造方法
重写构造方法的条件
- 一定要调用回super的init方法:初始化父类声明的一些成员变量和其他属性,因为子类继承了父类所有的属性和方法。
- [super init];返回self。因为[super init]有时候返回的对象不一定和self一样,所以为了保证一样,要对self进行赋值。self = [super init];
- 为了保证self不为空所以要进行判断。if(self != nil)
- 判断为真的时候,在给成员变量赋值。
@implementation Person
- (instancetype)init
{
//调回super的init方法,初始化父类的属性,然后把返回值赋值给self。
self = [super init];
//判断self是否初始化成功,如果初始化成功,在进行赋值操作
if (self != nil) {
_age = 10;
}
//返回对象自己。
return self;
}
@end
根据C语言的一些特性,对以上写法进行简化
#import "Person.h"
@implementation Person
- (instancetype)init
{
if(self = [super init])
{
_age = 10;
}
return self;
}
@end
构造方法的原则:先初始化父类,在初始化自己。
自定义构造方法
规范:
- 一定是对象方法,一定以-开头
- 返回值是id或者instancetype类型
- 方法名以init开头
- 父类的属性交个父类方法取处理,子类值处理自己的属性。
- (instancetype)initWithAge:(int)age;
- (instancetype)initWithAge:(int)age
{
if (self = [super init]) {
_age = age;
}
return self;
}
六、分类
分类Category,不改变原有类的情况下,给类添加一些方法。
使用场景:
- 分类是依赖于类的
- 分类名可以是人名或模块名
声明
@interface 类名(分类名称)
@end
实现
@implementation 类名(分类名称)
@end
分类的使用注意:
- 分类只增加方法,不能增加成员
- 分类方法实现中可以访问原来类声明的成员变量
- 分类可以重新实现原来类中的方法,但是会覆盖掉原来的方法,导致我们原来的方法失效,开发中不建议这样做。
- 调用方法优先级:分类(最后参与编译的分类优先)>原来类>父类。
- 如果多个分类中实现了相同的方法,最后参与编译的分类有效。
开发过程中可以给系统自带的类添加分类。
七、类的本质
类其实也是个对象
对象都有个类型,那么类是什么类型呢,类是class类型(class里已经有*了)
类对象就是class类型创建出来得对象。
对象创建的过程
1、class创建 Person类对象
2、利用Person类对象创建 Person类型的对象。
一个类在内存中只会有一个类对象。
//获取内存中的类对象,p是一个Person对象。
Class c =[p class];
其中c就是内存中的Person类对象。
Class c2= [Person class]
和上面的地址一样,都是获取内存中的类对象。
所以获取某个类对象有两种方法,通过对象的class方法和通过类的 class方法。
类对象的使用:
- 类就是类对象所以类对象可以调用类方法 [类对象 类方法];
- 运行中类名就代表了类对象。
类的加载过程
- 先加载父类,在加载子类
- 只要加载一个类时,就会调用类的类方法+ (void)load,在类被加载的时候调用。类只会被加载一次。
- 程序启动时,就会加载一次项目中所有的类和分类。类加载完毕后就会调用+load方法。(类和分类的load方法都会被调用)(先调用父类的load方法)分类放在所有原始类加载后加载,所以是最后调用Load方法。。
- 当第一次使用这个类的时候,就会调用一次+(void)initialize类的初始化方法(先调用父类的initalize 初始化 方法。)
- 分类catagory也会加载也有Load方法。
- 如果某个类有分类,使用类的时候会优先调用分类的initalize方法覆盖类的initalize方法。
- load和initalize方法都只会被调用一次。(load在程序记载时使用,initalize在类被第一次使用时调用。)
八、description方法
NSObject有 +description类方法和-description对象方法。
- 当用NSLog打印整个对象时,它会调用对象的 - description方法。
- description返回时是NSString *类型。
- description默认返回的是“类名和内存地址。”
- 想要直接打印对象的属性值,就得重写description方法。
- 不能在description中输出self,造成死循环。
- 当尝试输出一个类对象的时候
- 它会调用类的+description方法。
- 拿到+description方法的返回值,显示到屏幕上。
- 默认的返回值是类名。
SEL类型,这种类型的数据代表方法。
程序加载后,先给类分配存储空间,空间中存储着这个类的方法列表。每个方法都有一个SEL类型的数据与之对应。
每一个对象都有一个隐藏的指针指isa向它对应的类。
当调用p对象的 test方法时
- 把test方法包装成SEL类型的数据
- 通过isa指针去类中找SEL对应的是方法地址。
- 根据方法地址,调用对应方法。
- SEL其实就是方法的地址。根据SEL就能找到方法地址。
SEL在OC中的定义: typedefstruct objc_selector *SEL
调用方法的两种途径:
- 直接调用方法
- 间接调用 performSelect:
- 单参数的 performSelector : withObject:
- 创建SEL类型的数据:@selector(方法名),注意冒号也是方法名的一部分。
例如SELs = @selector(test:);
把字符串转换成SEL数据
SEL s = NSSelectorFromString(字符串);
每个方法内部都有一个隐藏的SEL类型的_cmd,这个_cmd代表当前的方法。_cmd相当于@selector(test).
SEL类型是是不能直接打印的,必须转换成字符串。
NSString *str = NSStringFromSelector(_cmd);
总结:SEL其实是对方法的一种包装,将方法包装成一个SEL类型的数据,去找对应的方法地址,找到地址就能调用方法。传说中的发消息就是发SEL,消息就是SEL。