本分享是面向有意向从事iOS开发的伙伴们,或者已经从事了iOS的开发者。如果您对iOS开发有极高的兴趣,可以与我一起探讨iOS开发,一起学习,共同进步。如果您是零基础,建议您先翻阅我之前分享的iOS开发分分钟搞定C语言系列,然后在开始Objective C语言的学习,如果您遇到问题也可以与我探讨,另外将无偿分享自己整理的大概400G iOS学习视频及学习资料,都是干货哦!可以新浪微博私信?关注极客James,期待与您的共同学习和探讨!!由于时间有限,每天在工作之余整理的学习分享,难免有不足之处,也希望各路大神指正!
made by :极客James
一、@property编译器指令
[email protected]概念
(1)首先@property是编译器的指令
(2)编译器指令就是用来告诉编译器要做什么,为了节省重复代码及提供程序员编码
(3)@property 用在声明文件中告诉编译器声明成员变量的的访问器(getter/setter)方法
(4)这样的好处是:免去我们手工书写getter和setter方法繁琐的代码
[email protected]使用
(1)在@inteface中,用来自动生成setter和getter的声明。
用@property int age;就可以代替下面的两行
- (int)age; // getter
- (void)setAge:(int)age; // setter
[email protected]编写步骤
(1)在@inteface和@end之间写上@property
(2)在@property后面写上需要生成getter/setter方法声明的属性名称, 注意因为getter/setter方法名称中得属性不需要, 所以@property后的属性也不需要.并且@property和属性名称之间要用空格隔开
(3)在@property和属性名字之间告诉需要生成的属性的数据类型, 注意两边都需要加上空格隔开。
[email protected]增强特性
自从Xcode 4.x后,@property可以同时生成setter和getter的声明和实现方法。
(1)默认情况下,setter和getter方法中的实现,会去访问下划线 _ 开头的成员变量。
@interface Person : NSObject
{
@public
int _age;
int age;
}
@property int age;
@end
int main(int argc, const char * argv[]) {
Person *p = [Person new];
[p setAge:30];
NSLog(@"age = %i, _age = %i", p->age, p->_age);
return 0;
}
(2)如果没有会自动生成一个_开头的成员变量,自动生成的成员变量是私有变量, 声明在.m中,在其它文件中无法查看,但当可以在本类中查看
@property只会生成最简单的getter/setter方法,而不会进行数据判断
Person *p = [Person new];
[p setAge:-10];
NSLog(@"age = %i", [p age]);
(3)如果需要对数据进行判断需要我们之间重写getter/setter方法
(4)若手动实现了setter方法,编译器就只会自动生成getter方法
(5)若手动实现了getter方法,编译器就只会自动生成setter方法
(6)若同时手动实现了setter和getter方法,编译器就不会自动生成不存在的成员变量
[email protected]修饰符
readonly 只生成setter方法,不生成getter方法
readwrite 既生成getter 又生成setter方法(默认)
@property (readonly) int age;
指定所生成的方法的方法名称
getter=你定制的getter方法名称
setter=你定义的setter方法名称(注意setter方法必须要有 :)
如果给一个属性同时提供了getter/setter方法, 那么我们称这个属性为可读可写属性
如果只提供了getter方法, 那么我们称这个属性为只读属性
如果只提供了setter方法, 那么我们称这个属性为只写属性
如果既没有提供getter也没有提供setter方法, 那么我们称这个属性为私有属性
格式:
@property(属性修饰符) 数据类型 变量名称;
readwrite: 代表既生成getter方法 , 也生成setter方法
三、@synthesize
[email protected]的概念
(1)@synthesize是编译器的指令
(2)编译器指令就是用来告诉编译器要做什么!
(3)@synthesize 用在实现文件中告诉编译器实现成员变量的的访问器(getter/setter)方法
(4)好处是:免去我们手工书写getterr和setter方法繁琐的代码
[email protected]基本使用
用@synthesize age = _age;就可以代替
- (int)age{
return _age;
}
- (void)setAge:(int)age{
_age = age;
}
[email protected]编写步骤
(1)在@implementation和@end之间写上@synthesize
(2)在@synthesize后面写上和@property中一样的属性名称, 这样@synthesize就会将@property生成的什么拷贝到@implementation中
(3)由于getter/setter方法实现是要将传入的形参给属性和获取属性的值,所以在@synthesize的属性后面写上要将传入的值赋值给谁和要返回哪个属性的值, 并用等号连接。
[email protected]注意点
(1)@synthesize age = _age;
setter和getter实现中会访问成员变量_age
如果成员变量_age不存在,就会自动生成一个@private的成员变量_age
@synthesize age;
(2)setter和getter实现中会访问@synthesize后同名成员变量age
如果成员变量age不存在,就会自动生成一个@private的成员变量age
多个属性可以通过一行@synthesize搞定,多个属性之间用逗号连接。
三、静态类型和动态类型
1.静态类型和动态类型的定义
(1)静态类型,将一个指针变量定义为特定类的对象时,使用的是静态类型,在编译的时候就知道这个指针变量所属的类,这个变量总是存储特定类的对象。
(2)动态类型,这一特性是程序直到执行时才确定对象所属的类
2.id数据类型
(1)id是一个数据类型, 并且是一个动态数据类型
既然是数据类型, 所以就可以用来:
- 定义变量
- 作为函数的参数
- 作为函数的返回值
默认情况下所有的数据类型都是静态数据类型
3.静态数据类型的特点
(1)在编译时就知道变量的类型,
(2)知道变量中有哪些属性和方法
(3)在编译的时候就可以访问这些属性和方法, 并且如果是通过静态数据类型定义变量, 如果访问了不属于静态数据类型的属性和方法, 那么编译器就会报错
4.动态数据类型的特点:
在编译的时候编译器并不知道变量的真实类型, 只有在运行的时候才知道它的真实类型。
并且如果通过动态数据类型定义变量, 如果访问了不属于动态数据类型的属性和方法, 编译器不会报错
id == NSObject * 万能指针
id和NSObject *的区别:
NSObject *是一个静态数据类型
通过静态数据类型定义变量, 不能调用子类特有的方法
通过动态数据类型定义变量, 可以调用子类特有的方法
通过动态数据类型定义的变量, 可以调用私有方法
弊端: 由于动态数据类型可以调用任意方法, 所以有可能调用到不属于自己的方法, 而编译时又不会报错, 所以可能导致运行时的错误
应用场景: 多态,可以减少代码量, 避免调用子类特有的方法需要强制类型转换。
5.instancetype
(1) instancetype == id == 万能指针 == 指向一个对象
(2)id在编译的时候不能判断对象的真实类型
(3)instancetype在编译的时候可以判断对象的真实类型
(4) id和instancetype除了一个在编译时不知道真实类型, 一个在编译时知道真实类型以外, 还有一个区别
(5)id可以用来定义变量, 可以作为返回值, 可以作为形参
(6)instancetype只能用于作为返回值
注意: 以后用到自定义构造方法, 返回值尽量使用instancetype, 不要使用id
四、构造方法
1.init方法
想在对象创建完毕后,成员变量马上就有一些默认的值就可以重写init方法。
重写init方法格式:
- (instancetype)init {
self = [super init];
if (self) {
// Initialize self.
}
return self;
}
2.构造方法使用注意
(1)子类拥有的成员变量包括自己的成员变量以及从父类继承而来的成员变量,在重写构造方法的时候应该首先对从父类继承而来的成员变量先进行初始化。
原则:先初始化父类的,再初始化子类的。
先调用父类的构造方法[super init];
再进行子类内部成员变量的初始化。
千万不要把self = [super init]写成self == [super init]
重写构造方法的目的:为了让对象方法一创建出来,成员变量就会有一些固定的值。
3.自定义构造方法
有时候仅仅靠重写构造方法(初始化方法),不能满足需求。比如一个班级中不可能所有学生的年龄都一样,这时候我们需要在创建某个学生的时候能够传入这个学生的年龄。这时候就需要来自定义构造函数(初始化函数)
4.自定义构造方法的规范
(1)一定是对象方法,以减号开头
(2)返回值一般是instancetype类型
(3)方法名必须以initWith开头
实例:
.h 声明文件
@interface Person : NSObject
@property int age;
@property NSString *name;
// 当想让对象一创建就拥有一些指定的值,就可以使用自定义构造方法
- (id)initWithAge:(int)age;
- (id)initWithName:(NSString *)name;
- (id)initWithAge:(int)age andName:(NSString *)name;
@end
.m 实现文件
- (id)initWithAge:(int)age{
if(self = [super init]){
_age = age;
}
return self;
}
- (id)initWithName:(NSString *)name{
if (self = [super init]){
_name = name ;
}
return self;
}
- (id)initWithAge:(int)age andName:(NSString *)name{
if(self = [super init]){
_name = name;
}
return self;
}
5.自定义方法的继承
(1)不能在子类访问父类私有变量.
(2)父类的属性交给父类的方法来处理
(3)自定义构造方法必须以intiWith开头,并且’W’必须大写
6.自定义类工厂方法
什么是类工厂方法:
用于快速创建对象的类方法, 我们称之为类工厂方法
类工厂方法中主要用于 给对象分配存储空间和初始化这块存储空间
规范:
1.一定是类方法 +
2.方法名称以类的名称开头, 首字母小写
3.一定有返回值, 返回值是id/instancetype
实例:
.h 文件声明
@interface Person : NSObject
@property int age;
+ (instancetype)person;
+ (instancetype)personWithAge:(int)age;
@end
.m文件实现
+(instancetype)person{
return [[Person alloc]init];
}
+ (instancetype)personWithAge:(int)age{
Person *p = [[Person alloc]init];
p.age = age;
return p;
}
7.类方法工厂方法的继承
注意:
(1) 但凡自定义类工厂方法, 在类工厂方法中创建对象一定不要使用类名来创建。
(2)一定要使用self来创建
(3)self在类方法中就代表类对象, 到底代表哪一个类对象呢?,谁调用当前方法, self就代表谁。
实例代码:
.h文件
@property int age;
+(instancetype)person;
+(instancetype)personWithAge:(int)age;
.m文件
+(instancetype)person{
return [[self alloc]init];
}
+(instancetype)personWithAge:(int)age{
person *p = [[self alloc]init];
p.age = age;
return = p;
}
五、类的本质及启动过程
(1)类的本质其实也是一个对象(类对象)
(2)程序中第一次使用该类的时候被创建,在整个程序中只有一份。
(3)此后每次使用都是这个类对象,它在程序运行时一直存在。
(4)类对象是一种数据结构,存储类的基本信息:类大小,类名称,类的版本,继承层次,以及消息与函数的映射表等
(5)类对象代表类,Class类型,对象方法属于类对象
(6)如果消息的接收者是类名,则类名代表类对象
(7)所有类的实例都由类对象生成,类对象会把实例的isa的值修改成自己的地址,每个实例的isa都指向该实例的类对象
六、OC实例对象 类对象 元对象之间关系
(1)每一个对象 都是一个类的实例。
(2)每一个对象 都有一个名为isa的指针,指向该对象的类。
(3)每一个类描述了一系列它的实例的特点,包括成员变量的列表,成员函数的列表等。
(4)每一个对象都可以接受消息,而对象能够接收的消息列表是保存在它所对应的类中。
版权声明:本文为博主原创文章,为了能相互促进,相互学习,请关注新浪微博:极客James