OC基础 — 4
2018-01-18 15:40
15 查看
- property和synthesize
(1)@porperty是一个编译器指令
在Xocde4.4之前,
可以使用@porperty来代替getter/setter方法的声明
也就是说我们只需要写上@porperty就不用写getter/setter方法的声明
编译器只要看到@property,
就知道我们要生成某一个属性的getter/setter方法的声明
(2)@synthesize是一个编译器指令,
它可以简化我们getter/setter方法的实现
1 在@synthesize后面告诉编译器,
需要实现哪个@property生成的声明
2 告诉@synthesize,
需要将传入的值赋值给谁和返回谁的值给调用者
3 如果在@synthesize后面没有告诉系统将传入的值赋值给谁,
系统默认会赋值给和@synthesize后面写得名称相同的成员变量
(3)增强的property:
从Xcode4.4以后apple对@property进行了一个增强,
不需要使用synthesize,只要利用一个@property就可以同时生成setter/getter方法的声明和实现
没有告诉@property要将传入的参数赋值给谁,
默认@property会将传入的属性赋值给_开头的成员变量
(4)@property有一个弊端:
它只会生成最简单的getter/setter方法的声明和实现,
并不会对传入的数据进行过滤
如果想对传入的数据进行过滤,
那么我们就必须重写getter/setter方法
如果不想对传入的数据进行过滤,
仅仅是提供一个方法给外界操作成员变量,
那么就可以使用@property
(5)如果利用@property来生成getter/setter方法,
那么我们可以不写成员变量,
系统会自动给我们生成一个_开头的成员变量
注意: @property自动帮我们生成的成员变量是一个私有的成员变量,
也就是说是在.m文件中生成的,
而不是在.h文件中生成的
- property修饰符
(1)@property(readwrite)
int age; //可读可写
//注意:默认情况下,@property就是readw
194bc
rite的
@property(readonly)
int age; //只读
@property(getter = get)
int age; //重命名getter方法
@property(setter = set:)
int age; //重命名setter方法
(2)程序员之间有一个约定,
一般情况下获取BOOL类型的属性的值,
我们都会将获取的方法名称改为isXXX
@property(getter=isMarried)
BOOL married;
- id类型
(1)id是一个数据类型,
并且是一个动态数据类型
可以用于:1
定义变量
2 作为函数的参数
3 作为函数的返回值
(2)动态类型和静态类型
静态数据类型的特点:
1
在编译时就知道变量的类型,
有哪些属性和方法
2
在编译的时候就可以访问这些属性和方法,
3 如果是通过静态数据类型定义变量,
如果访问了不属于静态数据类型的属性和方法,
那么编译器就会报错
4 默认情况下所有的数据类型都是静态数据类型
动态数据类型的特点:
1
在编译的时候编译器并不知道变量的真实类型,
只有在运行的时候才知道它的真实类型
2
如果通过动态数据类型定义变量,
如果访问了不属于动态数据类型的属性和方法,
编译器不会报错
3
动态类型的弊端:
由于动态数据类型可以调用任意方法,
所以有可能调用到不属于自己的方法,
而编译时又不会报错,
所以可能导致运行时的错误
4
应用场景:
多态,
可以减少代码量,
避免调用子类特有的方法需要强制类型转换
通过静态数据类型定义变量,
不能调用子类特有的方法
通过动态数据类型定义变量,
可以调用子类特有的方法
通过动态数据类型定义的变量,
可以调用私有方法
(4)id == NSObject *
万能指针
id和NSObject *的区别:
NSObject *是一个静态数据类型,id
是一个动态数据类型
(5)为了避免动态数据类型引发的运行时的错误,
一般情况下如果使用动态数据类型定义一个变量,
在调用这个对象的方法之前会进行一次判断,
判断当前对象是否能够调用这个方法
id obj = [Student
new];
if ([obj
isKindOfClass:[Student
class]]) {
// isKindOfClass:判断指定的对象是否是某一个类,
或者是某一个类的子类
[obj
eat];
}
if ([obj
isMemberOfClass:[Student
class]]) {
// isMemberOfClass:判断指定的对象是否是当前指定的类的实例
[obj
eat];
}
- new方法与alloc方法
(1)new做了三件事情:
1 开辟存储空间(+ alloc
方法)
2 初始化所有的属性(成员变量)(-
init 方法)
3 返回对象的地址
(2)alloc做了什么事情:
1 开辟存储空间
2 将所有的属性设置为0
3 返回当前实例对象的地址
//初始化成员变量,
但是默认情况下init的实现是什么都没有做
Person *p1 = [Person
alloc];
//返回初始化后的实例对象地址
Person *p2 = [p1
init];
//注意: alloc返回的地址,
和init返回的地址是同一个地址
//[[Person alloc] init]; == [Person new];
//建议创建一个对象时都使用 alloc init,
这样可以统一编码格式
Person *p3 = [[Person
alloc] init];
- 构造方法
(1)在OC中init开头的方法,
我们称之为构造方法
构造方法的用途:
用于初始化一个对象,
让某个对象一创建出来就拥有某些属性和值
(2)重写init方法,
在init方法中初始化成员变量
注意:
重写init方法必须按照苹果规定的格式重写,
如果不按照规定会引发一些未知的错误
1 必须先初始化父类,
再初始化子类
2 必须判断父类是否初始化成功,
只有父类初始化成功才能继续初始化子类
3 返回当前对象的地址
- (instancetype)init
{
//初始化父类
//只要父类初始化成功,
就会返回对应的地址,
如果初始化失败,
就会返回nil
//注意:
不要把 =
号写为 ==,一定要将[super init]的返回值赋值给self
self = [super
init];
//判断父类是否初始化成功
if (self !=
nil) {
//初始化子类,设置属性的值
_age =
6;
}
// 4.返回地址
return
self;
}
- instancetype与id的区别
(1)instancetype == id ==
万能指针 ==
指向一个对象
1 id在编译的时候不能判断对象的真实类型
instancetype在编译的时候可以判断对象的真实类型
2 id和instancetype除了一个在编译时不知道真实类型,
一个在编译时知道真实类型以外,
还有一个区别
id可以用来定义变量,
可以作为返回值,
可以作为形参
instancetype只能用于作为返回值
(2)如果init方法的返回值是instancetype,
那么将返回值赋值给一个其它的对象会报一个警告
如果init方法的返回值是id,
那么将init返回的对象地址赋值给其它对象是不会报错的
自定义构造方法时,
返回值尽量使用instancetype,
不要使用id
- 自定义构造方法
(1)自定义构造方法:
1 一定是对象方法
2 一定返回id/instancetype
3 方法名称一定以init开头
4 一个类可以有0个或者多个自定义构造方法
5 自定义构造方法可以有1个或多个参数
- (instancetype)initWithAge:(int)age andName:(NSString *)name
{
if (self = [super
init]) {
_age = age;
_name = name;
}
return
self;
}
- 类工厂方法
(1)用于快速创建对象的类方法,
我们称之为类工厂方法
类工厂方法中主要用于给对象分配存储空间和初始化这块存储空间
(2)规范: 1
一定是类方法
2
方法名称以类的名称开头,
首字母小写
3
一定有返回值,
返回值是id/instancetype
+ (instancetype)person
{
return [[Person
alloc] init];
}
//自定义类工厂方法
+ (instancetype)personWithAge:(int)age
{
Person *p = [[Person
alloc] init];
p.age = age;
return p;
}
(3)注意:
以后但凡自定义类工厂方法,
在类工厂方法中创建对象一定不要使用类名来创建,要使用self来创建
self在类方法中就代表类对象,
谁调用当前方法, self就代表谁
- 类的本质
(1)类其实也是一个对象,
这个对象会在这个类第一次被使用的时候创建
只要有了类对象,
将来就可以通过类对象来创建实例对象
实例对象中有一个isa指针,
指向创建自己的类对象
(2)类对象中保存了当前对象所有的对象方法
当给一个实例对象发送消息的时候,
会根据实例对象中的isa指针去对应的类对象中查找
//获取类对象
Class c1 = [p1 class];
//[实例对象 class];
Class c2 = [Person
class]; //[类名 class];
(3)类对象的应用场景:1
用于创建实例对象
2
用于调用类方法
- 类的启动过程
(1)只要程序启动就会将所有类的代码加载到内存中,
放到代码区
load方法会在当前类被加载到内存的时候调用,
有且仅会调用一次
如果存在继承关系,
会先调用父类的load方法,
再调用子类的load方法
+ (void)load
{
NSLog(@"Person类被加载到内存了");
}
(2)当当前类第一次被使用的时候就会调用initialize方法(创建类对象的时候)
initialize方法在整个程序的运行过程中只会被调用一次,
无论你使用多少次这个类都只会调用一次
initialize用于对某一个类进行一次性的初始化
initialize和load一样,
如果存在继承关系,
会先调用父类的initialize再调用子类的initialize
+ (void)initialize
{
NSLog(@"Person initialize");
}
- SEL类型
1 SEL类型的第一个作用:配合对象/类来检查对象/类中有没有实现某一个方法
SEL sel =
@selector(setAge:);
Person *p = [Person
new];
BOOL flag = [p
respondsToSelector:sel];
/* 判断p对象中有没有实现-号开头的setAge:方法
如果P对象实现了setAge:方法那么就会返回YES
如果P对象没有实现setAge:方法那么就会返回NO
*/
respondsToSelector注意点:
如果是通过一个对象来调用该方法那么会判断该对象有没有实现-号开头的方法
如果是通过类来调用该方法,
那么会判断该类有没有实现+号开头的方法
SEL sel1 =
@selector(test);
flag = [p respondsToSelector:sel1];
//判断该对象有没有实现-号开头的方法
flag = [Person
respondsToSelector:sel1];
//判断该类有没有实现+号开头的方法
2 SEL类型的第二个作用:配合对象/类来调用某一个SEL方法
SEL sel =
@selector(demo);
Person *p = [Person new];
//调用p对象中sel类型对应的方法
[p performSelector:sel];
SEL sel1 =
@selector(signalWithNumber:);
[p performSelector:sel1
withObject:@"13838383438"];
// withObject: 需要传递的参数
/* 注意:
如果通过performSelector调用有参数的方法,
那么参数必须是对象类型,
也就是说方法的形参必须接受的是一个对象,
因为withObject只能传递一个对象 */
// 注意:performSelector最多只能传递2个参数
SEL sel2 =
@selector(sendMessageWithNumber:andContent:);
[p performSelector:sel2
withObject:@"138383438"
withObject:@"abcdefg"];
3 配合对象将SEL类型作为方法的形参
Car *c = [Car
new];
SEL sel =
@selector(run);
Person *p = [Person new];
[p makeObject:c
andSel:sel];
(1)@porperty是一个编译器指令
在Xocde4.4之前,
可以使用@porperty来代替getter/setter方法的声明
也就是说我们只需要写上@porperty就不用写getter/setter方法的声明
编译器只要看到@property,
就知道我们要生成某一个属性的getter/setter方法的声明
(2)@synthesize是一个编译器指令,
它可以简化我们getter/setter方法的实现
1 在@synthesize后面告诉编译器,
需要实现哪个@property生成的声明
2 告诉@synthesize,
需要将传入的值赋值给谁和返回谁的值给调用者
3 如果在@synthesize后面没有告诉系统将传入的值赋值给谁,
系统默认会赋值给和@synthesize后面写得名称相同的成员变量
(3)增强的property:
从Xcode4.4以后apple对@property进行了一个增强,
不需要使用synthesize,只要利用一个@property就可以同时生成setter/getter方法的声明和实现
没有告诉@property要将传入的参数赋值给谁,
默认@property会将传入的属性赋值给_开头的成员变量
(4)@property有一个弊端:
它只会生成最简单的getter/setter方法的声明和实现,
并不会对传入的数据进行过滤
如果想对传入的数据进行过滤,
那么我们就必须重写getter/setter方法
如果不想对传入的数据进行过滤,
仅仅是提供一个方法给外界操作成员变量,
那么就可以使用@property
(5)如果利用@property来生成getter/setter方法,
那么我们可以不写成员变量,
系统会自动给我们生成一个_开头的成员变量
注意: @property自动帮我们生成的成员变量是一个私有的成员变量,
也就是说是在.m文件中生成的,
而不是在.h文件中生成的
- property修饰符
(1)@property(readwrite)
int age; //可读可写
//注意:默认情况下,@property就是readw
194bc
rite的
@property(readonly)
int age; //只读
@property(getter = get)
int age; //重命名getter方法
@property(setter = set:)
int age; //重命名setter方法
(2)程序员之间有一个约定,
一般情况下获取BOOL类型的属性的值,
我们都会将获取的方法名称改为isXXX
@property(getter=isMarried)
BOOL married;
- id类型
(1)id是一个数据类型,
并且是一个动态数据类型
可以用于:1
定义变量
2 作为函数的参数
3 作为函数的返回值
(2)动态类型和静态类型
静态数据类型的特点:
1
在编译时就知道变量的类型,
有哪些属性和方法
2
在编译的时候就可以访问这些属性和方法,
3 如果是通过静态数据类型定义变量,
如果访问了不属于静态数据类型的属性和方法,
那么编译器就会报错
4 默认情况下所有的数据类型都是静态数据类型
动态数据类型的特点:
1
在编译的时候编译器并不知道变量的真实类型,
只有在运行的时候才知道它的真实类型
2
如果通过动态数据类型定义变量,
如果访问了不属于动态数据类型的属性和方法,
编译器不会报错
3
动态类型的弊端:
由于动态数据类型可以调用任意方法,
所以有可能调用到不属于自己的方法,
而编译时又不会报错,
所以可能导致运行时的错误
4
应用场景:
多态,
可以减少代码量,
避免调用子类特有的方法需要强制类型转换
通过静态数据类型定义变量,
不能调用子类特有的方法
通过动态数据类型定义变量,
可以调用子类特有的方法
通过动态数据类型定义的变量,
可以调用私有方法
(4)id == NSObject *
万能指针
id和NSObject *的区别:
NSObject *是一个静态数据类型,id
是一个动态数据类型
(5)为了避免动态数据类型引发的运行时的错误,
一般情况下如果使用动态数据类型定义一个变量,
在调用这个对象的方法之前会进行一次判断,
判断当前对象是否能够调用这个方法
id obj = [Student
new];
if ([obj
isKindOfClass:[Student
class]]) {
// isKindOfClass:判断指定的对象是否是某一个类,
或者是某一个类的子类
[obj
eat];
}
if ([obj
isMemberOfClass:[Student
class]]) {
// isMemberOfClass:判断指定的对象是否是当前指定的类的实例
[obj
eat];
}
- new方法与alloc方法
(1)new做了三件事情:
1 开辟存储空间(+ alloc
方法)
2 初始化所有的属性(成员变量)(-
init 方法)
3 返回对象的地址
(2)alloc做了什么事情:
1 开辟存储空间
2 将所有的属性设置为0
3 返回当前实例对象的地址
//初始化成员变量,
但是默认情况下init的实现是什么都没有做
Person *p1 = [Person
alloc];
//返回初始化后的实例对象地址
Person *p2 = [p1
init];
//注意: alloc返回的地址,
和init返回的地址是同一个地址
//[[Person alloc] init]; == [Person new];
//建议创建一个对象时都使用 alloc init,
这样可以统一编码格式
Person *p3 = [[Person
alloc] init];
- 构造方法
(1)在OC中init开头的方法,
我们称之为构造方法
构造方法的用途:
用于初始化一个对象,
让某个对象一创建出来就拥有某些属性和值
(2)重写init方法,
在init方法中初始化成员变量
注意:
重写init方法必须按照苹果规定的格式重写,
如果不按照规定会引发一些未知的错误
1 必须先初始化父类,
再初始化子类
2 必须判断父类是否初始化成功,
只有父类初始化成功才能继续初始化子类
3 返回当前对象的地址
- (instancetype)init
{
//初始化父类
//只要父类初始化成功,
就会返回对应的地址,
如果初始化失败,
就会返回nil
//注意:
不要把 =
号写为 ==,一定要将[super init]的返回值赋值给self
self = [super
init];
//判断父类是否初始化成功
if (self !=
nil) {
//初始化子类,设置属性的值
_age =
6;
}
// 4.返回地址
return
self;
}
- instancetype与id的区别
(1)instancetype == id ==
万能指针 ==
指向一个对象
1 id在编译的时候不能判断对象的真实类型
instancetype在编译的时候可以判断对象的真实类型
2 id和instancetype除了一个在编译时不知道真实类型,
一个在编译时知道真实类型以外,
还有一个区别
id可以用来定义变量,
可以作为返回值,
可以作为形参
instancetype只能用于作为返回值
(2)如果init方法的返回值是instancetype,
那么将返回值赋值给一个其它的对象会报一个警告
如果init方法的返回值是id,
那么将init返回的对象地址赋值给其它对象是不会报错的
自定义构造方法时,
返回值尽量使用instancetype,
不要使用id
- 自定义构造方法
(1)自定义构造方法:
1 一定是对象方法
2 一定返回id/instancetype
3 方法名称一定以init开头
4 一个类可以有0个或者多个自定义构造方法
5 自定义构造方法可以有1个或多个参数
- (instancetype)initWithAge:(int)age andName:(NSString *)name
{
if (self = [super
init]) {
_age = age;
_name = name;
}
return
self;
}
- 类工厂方法
(1)用于快速创建对象的类方法,
我们称之为类工厂方法
类工厂方法中主要用于给对象分配存储空间和初始化这块存储空间
(2)规范: 1
一定是类方法
2
方法名称以类的名称开头,
首字母小写
3
一定有返回值,
返回值是id/instancetype
+ (instancetype)person
{
return [[Person
alloc] init];
}
//自定义类工厂方法
+ (instancetype)personWithAge:(int)age
{
Person *p = [[Person
alloc] init];
p.age = age;
return p;
}
(3)注意:
以后但凡自定义类工厂方法,
在类工厂方法中创建对象一定不要使用类名来创建,要使用self来创建
self在类方法中就代表类对象,
谁调用当前方法, self就代表谁
- 类的本质
(1)类其实也是一个对象,
这个对象会在这个类第一次被使用的时候创建
只要有了类对象,
将来就可以通过类对象来创建实例对象
实例对象中有一个isa指针,
指向创建自己的类对象
(2)类对象中保存了当前对象所有的对象方法
当给一个实例对象发送消息的时候,
会根据实例对象中的isa指针去对应的类对象中查找
//获取类对象
Class c1 = [p1 class];
//[实例对象 class];
Class c2 = [Person
class]; //[类名 class];
(3)类对象的应用场景:1
用于创建实例对象
2
用于调用类方法
- 类的启动过程
(1)只要程序启动就会将所有类的代码加载到内存中,
放到代码区
load方法会在当前类被加载到内存的时候调用,
有且仅会调用一次
如果存在继承关系,
会先调用父类的load方法,
再调用子类的load方法
+ (void)load
{
NSLog(@"Person类被加载到内存了");
}
(2)当当前类第一次被使用的时候就会调用initialize方法(创建类对象的时候)
initialize方法在整个程序的运行过程中只会被调用一次,
无论你使用多少次这个类都只会调用一次
initialize用于对某一个类进行一次性的初始化
initialize和load一样,
如果存在继承关系,
会先调用父类的initialize再调用子类的initialize
+ (void)initialize
{
NSLog(@"Person initialize");
}
- SEL类型
1 SEL类型的第一个作用:配合对象/类来检查对象/类中有没有实现某一个方法
SEL sel =
@selector(setAge:);
Person *p = [Person
new];
BOOL flag = [p
respondsToSelector:sel];
/* 判断p对象中有没有实现-号开头的setAge:方法
如果P对象实现了setAge:方法那么就会返回YES
如果P对象没有实现setAge:方法那么就会返回NO
*/
respondsToSelector注意点:
如果是通过一个对象来调用该方法那么会判断该对象有没有实现-号开头的方法
如果是通过类来调用该方法,
那么会判断该类有没有实现+号开头的方法
SEL sel1 =
@selector(test);
flag = [p respondsToSelector:sel1];
//判断该对象有没有实现-号开头的方法
flag = [Person
respondsToSelector:sel1];
//判断该类有没有实现+号开头的方法
2 SEL类型的第二个作用:配合对象/类来调用某一个SEL方法
SEL sel =
@selector(demo);
Person *p = [Person new];
//调用p对象中sel类型对应的方法
[p performSelector:sel];
SEL sel1 =
@selector(signalWithNumber:);
[p performSelector:sel1
withObject:@"13838383438"];
// withObject: 需要传递的参数
/* 注意:
如果通过performSelector调用有参数的方法,
那么参数必须是对象类型,
也就是说方法的形参必须接受的是一个对象,
因为withObject只能传递一个对象 */
// 注意:performSelector最多只能传递2个参数
SEL sel2 =
@selector(sendMessageWithNumber:andContent:);
[p performSelector:sel2
withObject:@"138383438"
withObject:@"abcdefg"];
3 配合对象将SEL类型作为方法的形参
Car *c = [Car
new];
SEL sel =
@selector(run);
Person *p = [Person new];
[p makeObject:c
andSel:sel];
相关文章推荐
- OC基础笔记
- OC基础回顾(十三)代码块和并发性
- OC基础-Xcode+点语法+变量作用域+@property/@synthesize
- iOS开发OC基础:OC的内存管理
- 黑马程序员 _7 OC基础 继承、多态、分类
- OC 基础知识要点
- OC基础--对象做参数在方法间传递
- 黑马程序员——OC基础03—内存管理
- 黑马程序员——OC基础---分类(Category)、非正式协议及分类延展
- iOS开发之OC语法基础(四)--内存管理
- 黑马程序员——OC基础语法—类的本质和SEL类型
- OC基础(不喜欢勿喷)
- OC基础--ARC的基本使用
- 黑马程序员----oc基础笔记----类和对象
- OC基础数据类型之字符串,数组和字典
- OC基础数据类型-NSData
- OC基础-内存管理
- 黑马程序员---iOS基础---OC的开始
- OC基础:getter和setter,@public @protected @private 分类: ios学习 OC 2015-06-15 19:23 22人阅读 评论(0) 收藏
- 黑马程序员——Objective-C——OC基础