IOS之 基础编程知识-Object-c

第一节:内存管理

1.自动释放池
1).MRC下才具备作用
2).原理:
3).如何创建1个自动释放池@autoreleasepool
4).调用对象的autorelease方法的代码放在自动释放池
5).好处:在MRC下创建对象把对象存储到自动释放池中 省略1个release.


2.ARC 自动内存管理,系统自动的计算对象的引用计数器的值.编译器特性
没有任何强指针指向1个对象的时候 这个对象就会被立即回收

 

第二节:SEL消息介绍

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

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

3,拿到存储方法的SEL对象
1). 因为SEL是1个typedef类型的 在自定义的时候已经加*了所以 我们在声明SEL指针的时候 不需要加*
2),取到存储方法的SEL对象SEL s1 = @selector(方法名);

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

5,重点掌握:
1)方法是以SEL对象的形式存储起来
2)如何拿到存储方法的SEL对象


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]效果是完全一样
3).调用1个对象的方法有两种
>[对象名 方法名];
>手动的为对象发送SEL消息

7,注意事项:
如果方法有参数 那么方法名是带了冒号的1).
如果方法有参数,如何传递参数2.
那么就调用另外1个方法。
(id)performSelector:(SE )aSelector withObject:(id)object;(id)performSelector:(SEL)aSelector withobject:(id)object1 withobject:(id)object2

如果有多个参数3

8,重点:
1,类是以class对象的形式存储在代码段
2,如何取到存储类的类对象
3,如何使用类对象调用类的类方法
4.方法是以SEL数据的形式存储的
5,调用方法的两种方式.

 

第三节:属性修饰符

@property参数
strong weak
当1个类的属性是1个0C对象的时候,在ARC模式下 这个属性应该要是1个强类型的还是1个弱类型的绝大多数情况下 应该要是1个强类型
strong与weak 都是应用在属性的类型是0C对象的时候g9,99% strong当出现循环引用的时候。strong weak

1.我们写1个类
要先为类写属性.a.
在声明属性的getter setter
再实现getter setter
哎! 1点点技术含量都没有,有没有更为简单的方式来实现同样的代码效果呢

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

3,使用@property注意
@property的类型和属性的类型一致@property的名称和属性的名称一致(去掉下划线)不要乱写
@property的名称决定了生成的getter和setter方法的名称所以,@property的名称要和属性的名称一致 去掉下划线 否则生成的方法名就是不符合规范的@property的数据类型决定了生成的setter方法的参数类型 和 getter方法的返回值类型
@property只是生成getter和setter方法的声明,实现还要自己来,属性还要自己定义

4. @property.
1),自动生成私有的带下划线的属性,
2),自动生成这个属性的getter setter方法的声明
3),自动生成这个属性的getter setter方法的实现
强调:
setter的实现: 直接赋值
getter的实现: 直接返回

5.@property可以带参数.带不同的参数就可以实现一些特别的功能.
1),语法.
@property(参数1,参数2,参数3.....)数据类型 名称;
2),与多线程相关的参数:
atomic
nonatomic
与生成的setter方法实现相关的参数3)
assignretain
4),是否生成只读、读写的封装
readwrite
readonly
5),指定生成的getter setter方法的名称
getter
setter

6,和多线程相关的参数
atomic :默认值
生成的setter方法和getter方法是很安全的方法但是这样做的后果:安全但是性能低下
nonatomic:生成的setter方法和getter方法是不安全的方法但是效率高,
推荐使用nonatomic
这两个参数不要管什么意思,反正你使用nonatomic.

//@synthesize
1).作用: 自动生成getter、setter方法的实现,所以,应该写在类的实现之中,

2).语法:
@synthesize @property名称;
@interface Person : NSObject
int _age;
@property int age;
@end

3).@synthesize做的事情aimplmentation Person
asynthesize age;dend
aimplementaion Person
int age;
(void)setAge:(int)age
@end
生成1个真私有的属性属性的类型和@synthesize对应的@property类型一致属性的名字和@synthesize对应的@property名字一致。
自动生成setter方法的实现实现的方式:将参数直接赋值给自动生成的那个私有属性,并且没有做任何的逻辑验证
自动生成getter方法的实现实现的方式: 将生成的私有属性的值返回

4),希望@synthesize不要去自动生成私有属性了getter setter的实现中操作我们已经写好的属性就可以了
语法:
@synthesize @property名称 = 已经存在的属性名;
asynthesize age =  age!1),不会再去生成私有属性

5),直接生成setter getter的实现
setter的实现: 把参数的值直接赋值给指定的属性gettter的实现: 直接返回指定的属性的值,

6),注意:
如果直接写1个@synthesize1).asynthesize name;
如果指定操作的属性asynthesize name = name;
生成的setter方法实现中 是没有做任何逻辑验证的 是直接赋值3 生成的getter方法的实现中 是直接返回属性的值。
如果setter或者getter有自己的逻辑验证 那么就自己在类的实现中重写就可以了

7),批量声明
如果多个@property的类型一致,可以批量声明
@property float height,weight;
@synthesize也可以批量声明@synthesize name = name,age = age,weight = weight,height = height;

//
@property增强
1,@property只是生成gettersetter的声明
@synthesize是生成qettersetter 的实现
这种写法是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的实现:直接返回生成的私有属性的值

3.使用注意
1):@property的类型一定要和属性的类型一致名称要和属性的名称一致 只是去掉下划线,
2),也可以批量声明相同类型的@property
3)property生成的方法实现没有做任何逻辑验证
直接赋值setter:直接返回getter:
所以,我们可以重写setter来自定义验证逻辑,如果重写了setter 还会自动生成getter如果重写了getter 还会自动生成setter
如果同时重写getter setter 那么就不会自动生成私有属性了

4,如果你想为类写1个属性 并且为这个属性封装getter setter1个@property就搞定

5,继承.父类的@property一样可以被子类继承@property生成的属性是私有的 在子类的内部无法直接访问生成的私有属性。但是可以通过setter getter来访问。


//
静态类型与动态类型
1,0C是1门弱语言。
编译器在编译的时候.语法检查的时候没有那么严格不管你怎么写都是可以的.
int num = 12.12;
优点: 灵活 咋个行都写
缺点: 太灵活
强类型的语言: 编译器在编译的时候 做语法检查的时候 行就是行不行就是不行

2,静态类型:
指的是1个指针指向的对象是1个本类对象,动态类型:指的是1个指针指向的对象不是本类对象

3,编译检查
编译器在编译的时候,能不能通过1个指针去调用指针指向的对象的方法
判断原则: 看指针所属的类型之中是有这个方法,如果有就认为可以调用 编译通过如果这个类中没有 那么编译报错.
这个叫做编译检查,在编译的时候 能不能调用对象的方法主要是看指针的类型
我们可以将指针的类型做转换,来达到骗过编译器的目的,

4,运行检查,
编译检查只是骗过了编译器,但是这个方法究竟能不能执行
所以在运行的时候,运行时会去检查对象中是否真的有这个方法.如果有就执行 如果没有就报错误。

5.LSP
父类指针指向子类对象
实际上任意的指针可以执行任意的对象编译器是不会报错的
当1个子类指针执行1个父类对象的时候,编译器运行通过子类指针去调用子类独有的方法但是在运行的时候是会出问题的.因为父类对象中根本没有子类成员


6,编译检查
1)编译器在编译的时候,判断1个指针是否可以调用指向的对象的方法判断的准则就是指针的类型。
LLVM
我们可以很轻松的把编译器给骗过,
2)就算骗过了编译器,程序在运行的时候还会做运行检查,

我们写的程序就算编译通过了.不意味着可以完美的执行


7,我们就希望.我们可以写代码来先判断1下.对象中是否有这个方法。如果有再去执行.3 如果没有就别去执行.
1),判断对象中是否有这个方法可以执行.
- (B00L)respondsToSelector:(SEL)aSelector;最常用的是这个方法.
Person *p1 = [Person new];[p1 sayHil;
2).判断指定的对象是否为 指定类的对象或者子类对象- (B00L)isKindofClass:(Class)aClass;
B00L b1 = [s1 isKindofClass:[erson class]];
判断s1对象是否为Person对象或者Person的子类对象
3),判断对象是否为指定类的对象 不包括子类
- (B00L)isMemberofclass:(Class)aClass;[s1 isMemberofclass:[Student class]]:判断s1对象是否为1个student对象,不包括Student的子类对象
4),判断类是否为另外1个类的子类。
+ (B00L)isSubclassofClass:(Class)aClass;

///
static关键字使用
C语言中的static
a.修饰局部变量
b.修饰全局变量
C.修饰函数

2 .0C中的static关键字
a,static不能修饰属性 也不能修饰方法static可以修饰方法中的局部变量。
b,如果方法中的局部变量被static修饰,那么这个变量就会被变成静态变量存储在常量区 当方法执行完毕之后 不会回收 下次再执行这个方法的时候 直接使用 而不会再声明了

3,如果方法的返回值是当前类的对象,那么方法的返回值就写instancetype


///
1,MRC和ARC来区分
在MRC下常用修饰符:
assign/ retain/ copy/  readwrite、readonly/ nonatomic、atomic  等。
在MRC下常用修饰符:
assign/ strong/ weak/ copy/ readwrite、readonly/ nonatomic、atomic  等。

2,iOS内存管理
iOS内存管理机制的原理主要是对引用计数管理(retainCount),引用计数(retainCount)简单来说就是统计一块内存的所有权,当这块内存被创建出来的时候,它的引用计数(retainCount)从0增加到1,表示有一个对象或指针持有这块内存,拥有这块内存的所有权,如果这时候有另外一个对象或指针指向这块内存,那么为了表示这个后来的对象或指针对这块内存的所有权,引用计数(retainCount)加1变为2,之后若有一个对象或指针不再指向这块内存时,引用计数(retainCount)减1,表示这个对象或指针不再拥有这块内存的所有权,当一块内存的引用计数(retainCount)变为0,表示没有任何对象或指针持有这块内存,系统便会立刻释放掉这块内存。

属性修饰符对retainCount计数的影响。

alloc为对象分配内存,retainCount 为1 。
retain MRC下 retainCount + 1。
copy 一个对象变成新的对象,retainCount为 1, 原有的对象计数不变。
release 对象的引用计数 -1。
autorelease 对象的引用计数 retainCount - 1,如果为0,等到最近一个pool结束时释放。
不管MRC还是ARC,其实都是看reference count是否为0,如果为0那么该对象就被释放,不同的地方是MRC需要程序员自己主动去添加retain 和 release,而ARC apple已经给大家做好,自动的在合适的地方插入retain 和 release类似的内存管理代码,具体原理如下,图片摘自官方文档。

3,retain
1)一般情况下,retain用在MRC情况下,被retain修饰的对象,retainCount(引用计数+1)。
2)retain只能修饰OC对象,不能修饰非OC对象。比如CoreFoundation对象就是C语言框架,他没有引用计数,也不能用retain修饰。
3).retain一般用来修饰非NSString的NSObject类及其子类。


4,copy
1)一般情况下,copy用于对不可变容易的属性修饰中,主要是NSArray /NSDictionary/NSString, 也可以用来修饰block。
2)在MRC和ARC下都可以使用。
3)其setter方法,与retain处理流程一样,先旧值release,再copy出新的对象。


5,assign
1)在MRC 和 ARC下都可以使用。
2)一般用来修饰基础数据类型(NSInteger, CGFloat) 和 C数据类型(int ,float, double)等。它的setter方法直接赋值,不进行任何retain操作。
3)使用assgin修饰的对象,对象不会自动置为nil,出现坏内存,引用计数不加。

6,strong
1)strong表示对对象的强引用。
2)ARC下也可以用来修饰block,strong 和 weak两个修饰符默认是strong。
3)用于指针变量,setter方法对参数进行release旧值再retain新值。


5,weak
1)weak 表示对对象的弱引用,被weak修饰的对象随时可被系统销毁和回收。
2)weak比较常用的地方就是delegate属性的设置。
3)用weak修饰弱引用,不会使传入对象的引用计数加1。


6,readwrite/readonly
1)当我们用readwrite修饰的时候表示该属性可读可改,用readonly修饰的时候表示这个属性只可以读取,不可以修改,一般常用在我们不希望外界改变只希望外界读取这种情况。
2)readwrite 程序自动创建setter/getter方法,readonly 程序创建getter方法。此外还可以自定义setter/getter方法。
3)系统默认的情况就是 readwrite。

7,nonatomic/atomic
1)nonatomic非原子属性。它的特点是多线程并发访问性能高,但是访问不安全;与之相对的就是atomic,特点就是安全但是是以耗费系统资源为代价,所以一般在工程开发中用nonatomic的时候比较多。
2)系统默认的是atomic,为setter、getter方法加锁,而nonatomic 不为setter、getter方法加锁。
3)使用nonatomic要注意多线程间通信的线程安全。

8,assign和weak的本质区别
objc
eproperty (nonatomic, assign) 3MGDlog “dog; // XWGDogunsafe.unretained dog;
.unsafe_unretained的特点:
1.不是强引用,不能保住0C对象的命2.如果引用的0C对散销吸了,指针并不会被自动清空,依然指向销毁的对象《很容易产生野指针错: EXC_BAD_ACCESS)
epnoperty (nonatomic, weak) XMGDog "dog; // XMSDog " _Nullable ..weak
dogi
week的特点:
1,不是强引用,不能保住M对象的前2.如果引用的0C对象销毁了,指针会被白动清空《突为nil),不再指向销毁的对象《永运不会产生野指针 错误)

assign一般用在基本数据莞型上面,比如intdouble等weak一般用在代理对象上面,或者用来解决据环强引用的同题

 

第四节:分类-扩展-协议

分类-定义
类别、类目、category
2,写1个学生类:类中有很多个方法
吃 喝 拉 撒 睡....基本行为
学习、敲代码、写书.... 学习
玩Dota 玩LOL 玩CF....玩爬山、跑步、踢足球..... 运动
......
如果将这些方法都写在同1个类模块中,当然完全是可以的,如果全都写在一个模块中,就显的很臃肿,后期难以维护和管理
默认情况下1个类独占1个模块.这个是将所有的成员都写在这1个模块中,就很男管理
我们的想法: 那就让1个类占多个模块将功能相似的方法定义在同1个模块中这样的好处: 方便维护和管理,
如何将1个类分成多个模块呢?

3,分类:
1),顾名思义: 将1个类分为多个模块
2),如何为1个类添加分类
3),会生成1个.h 和1个,m的模块
模块的文件名:本类名+分类名.h本类名+分类名.ma.


分类的作用:
1.在于可以将我们写类分为多个模块,可以不可以为系统的类写1个分类呢?为系统自带的类写分类 这个就叫做非正式协议
2,分类的第2个作用:为1个已经存在的类添加方法

1,ARC机制垃圾回收机制的区别
GC:程序在运行的期间,有1个东西叫做垃圾回收器,不断的扫描堆中的对象是否无人使用
Person *p1 = [Person new];p1 = nil;

ARC:不是运行时,在编译的时候就在合适的地方插入retain...·..插人的代码足以让对象无人使用的时候 引用计数器为0


//

1,延展: Extension
1), 是1个特殊的分类。 所以延展也是类的一部分2),特殊之处:
a。延展这个特殊的分类没有名字,
b。只有声明没有实现和本类共享1个实现

2.延展的语法
语法;@interface 本类名 ()
aend
没有实现,和本类共享1个


3.为类添加延展的步骤
只有1个.h文件,文件名称: 本类名 取得文件名h这个文件中只有延展的声明.
@interface Person ()
dend


4,延展的基本使用
1)。延展的本质是1个分类,作为本类的一部分只不过是1个特殊的分类没有名字
2),延展只有声明,没有单独的实现,和本类共享一个实现

5,延展和分类的区别
分类有名字.延展没有名字 是1个匿名的分类23每1个分类都有单独的声明和实现,而延展只有声明 没有单独的实现 和本类共享1个实现分类中只能新增方法,而延展中任意的成员都可以写。
4)分类中可以写@property 但是只会生成getter setter的声明,延展中写property 会自动生成私有属性 也会生成getter setter的声明和实现


6,延展的应用场景
1),要为类写1个私有的@property.
生成getter、setter方法只能在类的内部访问 不能在外部访问其实,我们可以想: @property生成私有属性、生成getter setter的实现,不要声明
2).延展100%的情况下不会独占1个文件,都是将延展直接写在本类的实现文件中,这个时候,写在延展中的成员,就相当于是这个类的私有成员。只能在本类的实现中访问。外部不能访问,
3)什么时候使用延展?当我们想要为类定义私有成员的时候,就可以使用延展。将延展定义在这个类的实现文件中,
如果想要为类写1个真私有属性,虽然我们可以定义在@implementation之中,但是不要这么写 这样很不规范写1个延展.将这个私有属性定义在延展中,
如果要为类写1个私有方法,建议将声明写在延展中,实现写在本类的实现中,提供代码的阅读性
如果想要为类写1个私有的property 就直接写在延展就可以了.
4)延展天生就是来私有化类的成员的,如果类的成员只希望在类的内部访问,那么就将其定义在延展中如果类的成员允许被外界访问 定义在本类的@interface中.


//
1,协议:protocol.
作用: 专门用来声明一大堆方法(不能声明属性,也不能实现方法,只能用来写方法的声明).
只要某个类遵守了这个协议.就相当于拥有这个协议中的所有的方法声明。
2.协议的声明
@protocol 协议名称 <NSobiect>
方法的声明;
dend
新建1个协议的方式。 NewFile OC-File - protocol协议的文件名: .h 并且只有1个.h文件,
在协议中,只能用来声明方法,协议的作用:就是专门用来写方法声明的
3,类遵守协议
如果想要让1个类,拥有协议中定义的所有的方法声明.那么就让这个类遵守这个协议类只要遵守1个协议,那么这个类就拥有了这些协议中定义的所有的方法的声明了。
@interface 类名 : 父类名 <协议名称>3,类遵守协议。
如果想要让1个类,拥有协议中定义的所有的方法声明.那么就让这个类遵守这个协议类只要遵守1个协议,那么这个类就拥有了这些协议中定义的所有的方法的声明了。
@interface 类名 : 父类名 <协议名称>
@end
:表示继承,
<>表示遵守的协议
当1个类,遵守了1个协议,那么就相当于这个类拥有了协议中定义的所有的方法的声明。这个类只是拥有了这个协议中的方法的声明而已,没有实现,所以 这个类,就应该实现协议中的方法
如果类不实现协议中的方法,其实也不会报错。编译器只是会报警告,但是当创建对象,来调用这个没有实现的协议中的方法的时候,就会报错
4,类是单继承,但是协议可以多遵守
1个类只能有1个父类土但是1个类可以同时遵守多个个协议
@interface 类名 : 父类名 <协议名称1,协议名称2......>
@end
当1个类遵守了多个协议之后,就相当于这个类拥有了所有协议中定义的方法的声明那么这个类,就应该实现所有协议中的方法
如果不实现,其实也不会报错。编译器只是会报警告但是当创建对象,来调用这个没有实现的协议中的方法的时候,就会报错
当1个类遵守了某个协议就相当于这个类拥有了这份协议中的所有的方法的声明但是仅仅只是有方法的声明而已,没有是实现,要类自己去实现
如果类不实现,编译器不会报错。只是给1个警告当我们创建对象,如果不调用这个协议方法,就不会报错如果要调用这个协议方法,那就会报错.
5,@required 与 @optional
在协议中,如果方法的声明被@required修饰,那么遵守这个协议的类必须要实现这个方法,否则编译器会发出警告。在协议中,如果方法的声明被@optional修饰,那么遵守这个协议的类可以实现这个方法,也可以不实现这个方法,不实现编译器也
不会报警告.
其实,无论是@required还是@optional你都可以不实现,编译器是不会报错的,仍然可以编译 运行唯一的区别就是: 当遵守协议的类不实现协议中的方法的时候,@required会给1个警告,optional警告都木有.
这两个关键字的主要作用:在于程序员沟通,告诉遵守协议的类 哪些方法是必须要实现的,因为这些方法我会调用,默认的是@required
协议可以从另外1个协议继承,并且可以多继承,
6,协议可以从另外1个协议继承,并且可以多继承
协议可以继承另外1个协议。 A 协议 继承了 B协议,那A协议中不仅有自己的方法的声明,还有B协议中的方法的声明如果有1个类遵守了A协议,那么这个类就拥有了,A、B协议中的所有的方法的声明
协议之间继承的语法格式@protocol A协议名称 <B协议名称>
cend
代表A协议继承自B协议,A协议中既有自己的方法声明,也有B协议中的方法声明
NSOBiect: 这是1个类,是所有的OC类的基类,这个类是苹果早就定义好得NSOBject:这也是1个协议,也是苹果早就定义好得,这个协议被NSobiect类遵守所以,所有的0C对象都拥有这个协议中的所有的方法.这个协议我们也叫做基协议。
写协议的规范:任何1个协议,必须要间接的或者直接的去遵守这个NSObject基协议协议的名称可以和类的名称相同
7.@protocol类型限制
1),要求某个指针保存的是遵守指定协议的对象
《我的协议》*obi对象
id<我的协议>*obj;
8,协议与协议之间可以相互继承
1),继承的语法:
@protocol 协议名称 <父协议名称
cend
2).效果:子协议中不仅有自己的方法的声明,还有父协议中的所有的方法的声明。如果1个类遵守了某份协议,那么这个类就拥有这个协议和这个协议的父协议中的所有的方法声明。
9,介绍1个东西,NSobiect
在Foundation框架中,有1个类 叫做NSobject 是所有0C类的基类 
在Foundation框架中,有1个协议,叫做NSobiect.
类的名称可以和协议的名称一致
10,写协议的规范
要求所有的协议都必须直接的或者间接的MNSObject基协议继承,
11,协议的类型限制
1)请声明1个指针.这个指针可以指向任意的对象,但是要求指向的对象要遵守指定的协议,如果不遵守 最起码要报1个警告,
要求声明1个指针 指向1个遵守了学习协议的对象,否则最起码要给哥哥1个警告
Nsobject<协议名称> *指针名;这个时候,这个指针可以指向遵守了指定协议的任意对象,否则就会报1个警告NSObject<StudyProtocol> *obi = [Student new];
当然了完全也可以使用id指针id<协议名称> 指针名;
id<StudyProtocol> id1 = [Student new];
2),声明1个指针变量,要求这个指针变量指向的对象必须遵守多个协议。NSObject<StudyProtocol,SBProtocol> *obj1 = [Student new];id<StudyProtocol,SBProtocol> obj1 = [Student new];
3),定义1个指针,指向遵守了学习协议的学生对象
4)why?
>遵守了某个协议的类,就相当于这个类拥有了这个协议所定义的行为
>因为我要调用这个对象中的协议方法。只有类遵守了协议,这个类中一定才会有协议方法

 

 

第五节:返回类型

1,NSobject是0C中所有类的基类根据LSP NSObject指针就可以指向任意的0C对象所以.NSobject指针是1个万能指针.可以执行任意的0C对象.
缺点:如果要调用指向的子类对象的独有的方法就必须要做类型转换

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

3,NSobject和id的异同,
相同点: 万能指针 都可以执行任意的OC对象
不同点: 通过NSObject指针去调用对象的方法的时候编译器会做编译检查通过id类型的指针去调用对象的方法的时候,编译器直接通过,无论你调用什么方法
注意: id指针只能调用对象的方法 不能使用点语法如果使用点语法就会直接报编译错误如果我们要声明1个万能指针 千万不要使用NSObiect 而是使用id

4,父类中的类方法创建1个父类对象返回.
1).如果返回值写为父类类型的.那么子类来调用这个方法得到的就是父类指针.1).解决的方式: 把返回值改为id类型的
2) .方法的内部创建的对象的是 不要写死,因为写死创建的对象就固定了我们希望那1个类来调用这个方法就创建那1个类的对象,
把类名写为self 那1个类来调用这个方法 self就指的是那1个类创建的就是那1个类的对象
方法的返回值是id类型的.问题就是任意指针都可以接收这个方法的返回值,
3) .
编译器连个警告都没有.
如果方法的返回值是instancetype代表方法的返回值是当前这个类的对象,
4),使用建议
>如果方法内部是在创建当前类的对象,不要写死成类名 [类名 new] ;而是用self代替类名
>如果方法的返回值是当前类的对象,也不要写死了。而是写instancetype
5)id和instancetype的区别
>instancetype只能作为方法的返回值不能在别的地方使用id既可以声明指针变量 也可以作为参数 也可以作为返回值
>instancetype 是1个有类型的 代表当前类的对象

 

 

第六节:block类型

1,0C在C的基础之上新增了一些数据类型
BO0L
Boolean
class
nil
SEL
id
block

2.block是1个数据类型
int double float char ......
既然是1个数据类型,那么就可以声明这个数据类型的变量,所以我们完全也可以声明1个block类型的变量不同类型的变量中可以存储不同类型的数据,那么block类型的变量中可以存储什么样的数据呢?
1)。block是1个数据类型 所以我们可以声明1个block类型的变量
2),block类型的变量中专门存储1段代码,这段代码可以有参数 可以有返回值

3,block变量的声明
1)虽然block变量中是用来存储1段代码的,但是1个block变量中并不是任意的1段代码都可以存进去的1).而是有限定的,
也就是说,在声明block变量的时候,必须要指定这个block变量存储的代码段是否有参数,是否有返回值一旦指定以后,这个block变量中就只能存储这样的代码了,
声明了1个block变量,返回值是void 参数是1个int类型的,这个时候,这个block变量中就只能存储无返回值并且有1个int参数的代码段

2),声明block变量的语法格式:
返回值类型 (^block变量的名称)(参数列表);
void (^myBlock1)();表示声明了1个block类型的变量叫做myBlock1 这个变量中只能存储没有返回值没有参数的代码段,
int (^myBlock2)();
int (^myBlock3)(int num1,int num2);


4,初始化block变量
1)原理: 写1个符合block要求的代码段,存储到block变量中就可以了,
2)代码段的书写格式: 
^返回值类型(参数列表){
  代码段;
};
3)写1个无参数无返回值的代码段
 ^void()}
  NSLog(@"我爱你");
  NSLog(@"我恨你");
}
这个时候,我们就可以将这段代码使用赋值符号存储到 无返回值无参数要求的block变量中
void (^myBlock1)();
 myBlock1 = ^void(){
 NSLog(@"我爱你");
  NSLog(@"我恨你");
}
当然也可以在声明block变量的同时使用符合要求的代码段初始化
void (^myBlock1)() =^void(){
 NSLog(@"我爱你");
 NSLog(@"我恨你");
}

4),有返回值的代码段
~int(){
 int num1 = 10 + 20;
 return num1;
}
我们就可以将这段代码赋值给符合要求的block变量int 
(^myBlock2)() = ^int(){
  int num1 = 10 + 20;
  return num1;
}


5),既有参数既有返回值的代码段
^int(int num1,int num2){
 int num3= num1 + num2:
 return num3
};
所以 我们可以将这个代码赋值给符合要求的block变量
int (^myBlock3)(int num1,int num2) = ^int(int num1,int num2){
 int num3= num1 + num2;
 return num3;
}

6),注意
赋值给block变量的代码段必须要符合block变量的要求,否则就会报错


5,如何执行存储在block变量中的代码段
语法格式: block变量名();


6,关于block的简写
1),如果我们写的代码段没有返回值,那么代码段的void可以省略
    void (^myBlock1)() =^(){
      NSLog(@"我爱你");
      NSLog(@"我恨你");
    }
注意,我说的是代码段的返回值如果是void可以省略,声明block变量的返回值无论是什么不可以省略.


2),如果我们写的代码段没有参数,那么代码段的小括弧写可以省略
    int (^myBlock2)() = ^int{ 
        num1 = 10 + 20;
        return num1;
    }
再次强调: 我们说的是代码段
所以,当1个代码段既没有参数,也没有返回值的适合,就只写~
    void (^myBlock1)() = ^{
        NSLog(@"我爱你");
        NSLog(@"我恨你");
    }
    

3)声明block变量的时候,如果有指定参数可以只写参数的类型而不写参数的名称;
    int (^myBlock3)(int,int) = ^int(int num1,int num2){
        int num3= num1 + num2:
        return num3;
    }
注意:这个地方我们说的是声明block变量的时候 再写代码段的时候 类型和名称都要写

4).无论代码段是否有返回值.在写代码的时候可以不写返回值类型 省略如果在写代码段的时候,省略了返回值,这个时候系统会自动的确定返回值的类型
如果代码段中没有返回任何数据 那么它会认为这个代码段是没有返回值的如果代码中有返回数据 返回的数据是什么类型 它就会认为这个代码段是什么类型的
建议:仍然按照我们最标准的写法来写block变量和block代码段,因为这样可以提高代码的阅读性

7,简化block父量的夏杂定
1),问题: 定义block变量的时候要写好大1串啊! 类型好长
2),typedef的使用场景:将一个长类型定位为一个短类型
3),我们也可以使用typedef将长的block类型 定义为1个短类型
    typedef 返回值类型 (^新类型)(参数列表);
    typedef void (^NewType)(); 代表重新定义了1个类型叫做NewType 是1个blok类型 无参数无返回值的block类型
    
    
8。关于block块访问外部变量的问题
1),在block代码块的内部可以取定义在外部的变量的值,定义在外部的局部变量和全局变量2),在block代码块的内部可以修改全局变量的值,但是不能修改定义在外部的局部变量的值,3),如果你希望我们定义的局部变量可以允许在bLock代码的内部去修改,那么就为这个局部变量加1个 block的修饰符。


9,block的使用
1,block是1个数据类型.能不能不能作为函数的参数呢? 当然是可以的
2.如何为函数定义block类型的参数?a。就是在小括弧中声明1个指定格式的block变量就可以了b,可以使用typedef简化定义,这样看起来就不会晕了.
3,如何调用带block参数的函数呢?
a,如果要调用的函数的参数是block类型的,那么要求在调用的时候传入1个和形参block要求的代码段,
调用的时候,可以先讲代码段存储到1个block变量中,然后再传递这个block变量6也可以直接将符合要求的代码段写在小括弧中传递。
c,小技巧,通过Xcode提示可以快速的生产block代码段的框架
4.将block作为函数的参数可以实现什么样的效果?
可以将调用者自己写的1段代码 传递到 函数的内部区去执行、
5.block也可以作为函数的返回值
当将block作为函数的返回值的时候,返回值的类型就必须要使用typedef定义的短类型
6,block与函数
相同点:都是封装1段代码
不同点:
1),block是1个数据类型函数是1个函数
2).我们可以声明block类型的变量 函数就只是函数
3),block可以作为函数的参数,而函数不能直接作为函数的参数

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值