022-block与@protocol-OC笔记
2015-10-08 21:42
363 查看
学习目标
1.【掌握】延展Extension2.【掌握】block类型
3.【掌握】block修改外部变量
4.【理解】block的应用场景
5.【掌握】协议protocol
6.【掌握】@protocol类型限制
7.【理解】代理模式Delegation
一、延展Extension
延展(Extension)是一个特殊的分类,延展没有名字(匿名分类),而且只能有方法的声明,不能有单独的方法实现,与本类共享一个方法实现。我们可以使用@property生成私有属性和对应的getter、setter方法。所以我们常常将延展写在类的实现文件中,用于私有化类的成员(属性和方法),仅限本类访问。//用于类的实现文件中
#import "Person.h"
@implementation Person
//定义延展
@interface Person ()
//延展里声明属性和方法
@end
//实现方法
@end
1 2 3 4 5 6 7 8 9 10 11 12 13 | //用于类的实现文件中 #import "Person.h" @implementationPerson //定义延展 @interfacePerson() //延展里声明属性和方法 @end //实现方法 @end |
二、block类型
block是OC中新增的一个数据类型,可以声明block变量来存储一段代码,这段代码可以有参数,也可以有返回值,也可以都没有。并且可以通过 这个block变量来执行存储在变量中的代码,同时block变量也可以作为一个参数进行传递。在使用时,我们常常会使用typedef来声明一个 block类型的别名,这样就可以把符合特定要求的代码块保存在block变量里作为方法的参数进行传递,还能简写代码。语法:
返回值类型 (^block变量名)(参数列表) = ^代码段返回值类型(参数列表) {
代码段;
};
block类型的简单使用
#import <Foundation/Foundation.h>
int main(){
@autoreleasepool {
//声明无参无返回值的block变量myBlock1
void (^myBlock1)();
//声明有两个int类型参数int类型返回值的block变量myBlock2
int (^myBlock2)(int num1, int num2);
//block变量的参数可以只写参数类型省略参数名
// int (^myBlock2)(int, int);
//无参数无返回值的代码段赋值给myBlock1
myBlock1 = ^void() {
NSLog(@"代码段内容1");
};
//有两个int类型参数int类型返回值的代码段赋值给myBlock2
myBlock2 = ^int(int num1, int num2) {
return num1 +num2;
};
int (^myBlock3)(int num1,int num2);
//代码段的返回值类型可以省略,但是最终还是要返回符合要求的数据
myBlock3 = ^(int num1, int num2) {
return num1 +num2;
};
//代码段的返回值类型可以省略,但是最终还是要返回符合要求的数据
void (^myBlock4)() = ^{
NSLog(@"代码段内容4");
};
//使用block变量
myBlock1();
int result1 = myBlock2(1,2);
int result2 = myBlock3(1,2);
myBlock4();
NSLog(@"result1 = %d",result1);
NSLog(@"result2 = %d",result2);
}
/* 输出
代码段内容1
代码段内容4
result1 = 3
result2 = 3
*/
return 0;
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | #import <Foundation/Foundation.h> intmain(){ @autoreleasepool{ //声明无参无返回值的block变量myBlock1 void(^myBlock1)(); //声明有两个int类型参数int类型返回值的block变量myBlock2 int(^myBlock2)(intnum1,intnum2); //block变量的参数可以只写参数类型省略参数名 // int (^myBlock2)(int, int); //无参数无返回值的代码段赋值给myBlock1 myBlock1=^void(){ NSLog(@"代码段内容1"); }; //有两个int类型参数int类型返回值的代码段赋值给myBlock2 myBlock2=^int(intnum1,intnum2){ returnnum1+num2; }; int(^myBlock3)(intnum1,intnum2); //代码段的返回值类型可以省略,但是最终还是要返回符合要求的数据 myBlock3=^(intnum1,intnum2){ returnnum1+num2; }; //代码段的返回值类型可以省略,但是最终还是要返回符合要求的数据 void(^myBlock4)()=^{ NSLog(@"代码段内容4"); }; //使用block变量 myBlock1(); intresult1=myBlock2(1,2); intresult2=myBlock3(1,2); myBlock4(); NSLog(@"result1 = %d",result1); NSLog(@"result2 = %d",result2); } /* 输出 代码段内容1 代码段内容4 result1 = 3 result2 = 3 */ return0; } |
#import <Foundation/Foundation.h>
//只能存储符合要求的代码段
typedef void (^MyBlock1)();
typedef int (^MyBlock2)(int num1, int num2);
int main(){
@autoreleasepool {
MyBlock1 block2 = ^ {
NSLog(@"无参数无返回值");
};
MyBlock2 block1 = ^(int num1, int num2) {
return num1 + num2;
};
block2();
int result = block1(1,2);
NSLog(@"result = %d",result);
/*
无参数无返回值
result = 3
*/
}
return 0;
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | #import <Foundation/Foundation.h> //只能存储符合要求的代码段 typedefvoid(^MyBlock1)(); typedefint(^MyBlock2)(intnum1,intnum2); intmain(){ @autoreleasepool{ MyBlock1block2=^{ NSLog(@"无参数无返回值"); }; MyBlock2block1=^(intnum1,intnum2){ returnnum1+num2; }; block2(); intresult=block1(1,2); NSLog(@"result = %d",result); /* 无参数无返回值 result = 3 */ } return0; } |
三、block访问外部变量
block可以访问外部变量(block外部),但无法改变外部变量的值。如果想要被修改,需要用__block修饰。#import <Foundation/Foundation.h>
//只能存储符合要求的代码段
typedef void (^MyBlock)();
int main(){
@autoreleasepool {
//使用__block修饰一个变量
__block int num = 20;
MyBlock sumBlock = ^ {
NSLog(@"num = %d",++num);//更改被__block修饰的外部变量的值
};
/*
num = 21
*/
}
return 0;
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | #import <Foundation/Foundation.h> //只能存储符合要求的代码段 typedefvoid(^MyBlock)(); intmain(){ @autoreleasepool{ //使用__block修饰一个变量 __block intnum=20; MyBlocksumBlock=^{ NSLog(@"num = %d",++num);//更改被__block修饰的外部变量的值 }; /* num = 21 */ } return0; } |
四、block的应用场景
block作为方法的参数,可以将调用者的代码段传递到函数内部去使用#import <Foundation/Foundation.h>
//只能存储符合要求的代码段
typedef void (^MyBlock)();
//程序员每天发生的事情
void days(void (^myBlock)());
//将每天上班发生的事情写到block
void workDay(int day);
int main(){
@autoreleasepool {
//循环传入天数调用函数
for (int i = 1; i <= 5; i ++) {
workDay(i);
}
}
return 0;
}
void workDay(int day){
MyBlock myBlock;
switch (day) {
case 1:
myBlock = ^{
NSLog(@"看美女......");
};
break;
case 2:
myBlock = ^{
NSLog(@"看代码......");
};
break;
case 3:
myBlock = ^{
NSLog(@"写代码......");
};
break;
case 4:
myBlock = ^{
NSLog(@"测试代码......");
};
break;
case 5:
myBlock = ^{
NSLog(@"因美女少离职......");
};
break;
default:
break;
}
//调用函数传入block
days(myBlock);
}
//程序员每天发生的事情
void days(void (^myBlock)()){
NSLog(@"起床");
NSLog(@"吃饭");
NSLog(@"坐车");
myBlock();
NSLog(@"下班");
NSLog(@"坐车");
NSLog(@"回家");
NSLog(@"吃饭");
NSLog(@"睡觉");
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | #import <Foundation/Foundation.h> //只能存储符合要求的代码段 typedefvoid(^MyBlock)(); //程序员每天发生的事情 voiddays(void(^myBlock)()); //将每天上班发生的事情写到block voidworkDay(intday); intmain(){ @autoreleasepool{ //循环传入天数调用函数 for(inti=1;i<=5;i++){ workDay(i); } } return0; } voidworkDay(intday){ MyBlockmyBlock; switch(day){ case 1: myBlock=^{ NSLog(@"看美女......"); }; break; case 2: myBlock=^{ NSLog(@"看代码......"); }; break; case 3: myBlock=^{ NSLog(@"写代码......"); }; break; case 4: myBlock=^{ NSLog(@"测试代码......"); }; break; case 5: myBlock=^{ NSLog(@"因美女少离职......"); }; break; default: break; } //调用函数传入block days(myBlock); } //程序员每天发生的事情 voiddays(void(^myBlock)()){ NSLog(@"起床"); NSLog(@"吃饭"); NSLog(@"坐车"); myBlock(); NSLog(@"下班"); NSLog(@"坐车"); NSLog(@"回家"); NSLog(@"吃饭"); NSLog(@"睡觉"); } |
#import <Foundation/Foundation.h>
typedef void (^ReturnBlock)();
//block作为函数返回值,一般开发中很少用到
ReturnBlock test(){
return ^{
NSLog(@"内容");
};
}
int main(){
@autoreleasepool {
//调用函数
ReturnBlock test1 = test();
}
return 0;
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #import <Foundation/Foundation.h> typedefvoid(^ReturnBlock)(); //block作为函数返回值,一般开发中很少用到 ReturnBlocktest(){ return^{ NSLog(@"内容"); }; } intmain(){ @autoreleasepool{ //调用函数 ReturnBlocktest1=test(); } return0; } |
五、协议protocol
在OC中使用@protocol定义一个协议,遵守此协议的类就拥有了这个协议所有的方法声明。协议跟其他面向对象语言的接口有些类似,只是不一定会实现协议里的方法。//youProtocol.h文件
#import <Foundation/Foundation.h>
@protocol youProtocol <NSObject>
//被@required修饰,必须实现的方法,不实现编译时也不报错,用于程序员直接的交流
@required
- (void)shout;
//被@optional修饰,可选实现的方法,不实现编译时也不报错,用于程序员直接的交流
@optional
- (void)drive;
@end
//myProtocol.h文件
#import <Foundation/Foundation.h>
@protocol myProtocol <NSObject>
@required
- (void)sayHi;
- (void)song;
@optional
- (void)paly;
- (void)eat;
@end
//Dog.h文件
#import <Foundation/Foundation.h>
#import "myProtocol.h"
@interface Dog : NSObject <myProtocol,youProtocol>
@end
//Dog.m文件
#import "Dog.h"
@implementation Dog
//继承了两个协议,6个方法只实现了3个必须实现的
- (void)sayHi {
NSLog(@"狗汪汪。。");
}
- (void)song {
NSLog(@"狗唱。。");
}
- (void)shout {
NSLog(@"狗叫。。");
}
@end
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | //youProtocol.h文件 #import <Foundation/Foundation.h> @protocolyouProtocol<NSObject> //被@required修饰,必须实现的方法,不实现编译时也不报错,用于程序员直接的交流 @required -(void)shout; //被@optional修饰,可选实现的方法,不实现编译时也不报错,用于程序员直接的交流 @optional -(void)drive; @end //myProtocol.h文件 #import <Foundation/Foundation.h> @protocolmyProtocol<NSObject> @required -(void)sayHi; -(void)song; @optional -(void)paly; -(void)eat; @end //Dog.h文件 #import <Foundation/Foundation.h> #import "myProtocol.h" @interface Dog : NSObject<myProtocol,youProtocol> @end //Dog.m文件 #import "Dog.h" @implementationDog //继承了两个协议,6个方法只实现了3个必须实现的 -(void)sayHi{ NSLog(@"狗汪汪。。"); } -(void)song{ NSLog(@"狗唱。。"); } -(void)shout{ NSLog(@"狗叫。。"); } @end |
总结:
1.协议通过<>进行实现,并且只能在类的声明上遵守协议,一个类可以同时遵守多个协议,中间通过逗号分隔。2.一个协议可以遵守自另一个协议,例如上面myProtocol就遵守自NSObject(基协议),如果需要遵守多个协议中间使用逗号分隔。
3.如果父类遵守了某个协议,子类继承了父类所有成员,相当于子类也遵守了这个协议,子类不实现也不会警告。
4.协议中不能定义属性,只能声明方法,不要在协议中使用@property定义属性,协议的主要作用是集中声明方法。
5.我们可以通过关键字@required和@optional分别修饰必须实现的方法和可选实现的方法,如果不修饰则默认是@required。
六、@protocol类型限制
要求某个指针保存的是遵守了指定协议的对象,比如id<协议名> obj;,这个obj指针只能指向遵守了“<协议名>”这个协议的对象。之所以要求对象要遵守某个协议,是因为我们可能会在程序中调用协议中的方法,如果没有遵守并实现其对应方法调用时就会报错。一般调用时我们都会使用respondsToSelector:方法来判断遵守了指定协议的对象是否实现了其对应的方法。//假如有Person类,如果Person没有同时遵守sportProtocol和playProtocol协议就报错
id<sportProtocol,playProtocol> obj = [[Person alloc] init];
//父类<协议名> *指针名;
//这里的指针只能指向遵守了指定协议的对象或子类对象,这里是多态。
1 2 3 4 5 | //假如有Person类,如果Person没有同时遵守sportProtocol和playProtocol协议就报错 id<sportProtocol,playProtocol>obj=[[Personalloc] init]; //父类<协议名> *指针名; //这里的指针只能指向遵守了指定协议的对象或子类对象,这里是多态。 |
七、代理模式Delegation
事实上在OC中协议的更多作用是用于约束一个类必须实现某些方法,而从面向对象的角度而言这个类跟其他面向对象语言的接口(interface)并不一定存在某种自然关系,可能是两个完全不同意义上的事物,这种模式我们称之为代理模式(Delegation)。在Cocoa框架中大量采用这种模式实现数据和UI的分离,而且基本上所有的协议都是以Delegate结尾。现在假设需要设计一个按钮,在按钮中定义按钮的代理,同时使用协议约束这个代理(事件的触发者)必须实现协议中的某些方法,当按钮被点击就检查代理是否实现了这个方法,如果实现了则调用这个方法。
//JFButtonDelegate.h文件
#import <Foundation/Foundation.h>
@class JFButton;
@protocol JFButtonDelegate <NSObject>
@required
-(void)onClick:(JFButton *)button;
@end
//MyListener.h文件
#import <Foundation/Foundation.h>
#import "JFButtonDelegate.h"
@interface MyListener : NSObject <JFButtonDelegate>
@end
//MyListener.m文件
#import "MyListener.h"
#import "JFButton.h"
@implementation MyListener
//实现协议中声明的方法
-(void)onClick:(JFButton *)button{
NSLog(@"调用代理的监听方法,被点击的按钮是:%@",button);
}
@end
//JFButton.h文件
#import <Foundation/Foundation.h>
#import "JFButtonDelegate.h"
@interface JFButton : NSObject
//代理属性,同时约定作为代理的对象必须实现JFButtonDelegate协议
//id可以表示任何一个OC对象类型,类型后面的 <协议名> 用于约束作为这个属性的对象必须实现该协议。
@property (nonatomic,strong) id<JFButtonDelegate> delegate;
//点击方法
-(void)click;
@end
//JFButton.m文件
#import "JFButton.h"
@implementation JFButton
-(void)click{
NSLog(@"调用按钮对象的点击方法");
//使用respondsToSelector方法可以判断self.delegate实例是否实现了onClick:方法
if([self.delegate respondsToSelector:@selector(onClick:)]){
//如果确认onClich:方法已经实现,则作为JFButton的监听
[self.delegate onClick:self];
}
}
@end
//main.m文件
#import <Foundation/Foundation.h>
#import "JFButton.h"
#import "MyListener.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
//实例化按钮和监听对象
JFButton *button = [[JFButton alloc]init];
MyListener *listener = [[MyListener alloc]init];
//降遵守了协议的监听对象作为按钮的代理
button.delegate = listener;
//按钮调用点击方法
[button click];
/*
调用按钮对象的点击方法
调用代理的监听方法,被点击的按钮是:<JFButton: 0x100114710>
*/
}
return 0;
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | //JFButtonDelegate.h文件 #import <Foundation/Foundation.h> @classJFButton; @protocolJFButtonDelegate<NSObject> @required -(void)onClick:(JFButton*)button; @end //MyListener.h文件 #import <Foundation/Foundation.h> #import "JFButtonDelegate.h" @interface MyListener : NSObject<JFButtonDelegate> @end //MyListener.m文件 #import "MyListener.h" #import "JFButton.h" @implementationMyListener //实现协议中声明的方法 -(void)onClick:(JFButton*)button{ NSLog(@"调用代理的监听方法,被点击的按钮是:%@",button); } @end //JFButton.h文件 #import <Foundation/Foundation.h> #import "JFButtonDelegate.h" @interface JFButton : NSObject //代理属性,同时约定作为代理的对象必须实现JFButtonDelegate协议 //id可以表示任何一个OC对象类型,类型后面的 <协议名> 用于约束作为这个属性的对象必须实现该协议。 @property(nonatomic,strong)id<JFButtonDelegate>delegate; //点击方法 -(void)click; @end //JFButton.m文件 #import "JFButton.h" @implementationJFButton -(void)click{ NSLog(@"调用按钮对象的点击方法"); //使用respondsToSelector方法可以判断self.delegate实例是否实现了onClick:方法 if([self.delegate respondsToSelector:@selector(onClick:)]){ //如果确认onClich:方法已经实现,则作为JFButton的监听 [self.delegate onClick:self]; } } @end //main.m文件 #import <Foundation/Foundation.h> #import "JFButton.h" #import "MyListener.h" intmain(intargc,constchar*argv[]){ @autoreleasepool{ //实例化按钮和监听对象 JFButton*button=[[JFButtonalloc]init]; MyListener*listener=[[MyListeneralloc]init]; //降遵守了协议的监听对象作为按钮的代理 button.delegate=listener; //按钮调用点击方法 [button click]; /* 调用按钮对象的点击方法 调用代理的监听方法,被点击的按钮是:<JFButton: 0x100114710> */ } return0; } |
相关文章推荐
- 剑指offer—二叉搜索树的第k个结点
- 卡榫函数
- on the way to c++
- UVa 893 - Y3K Problem
- 南阳oj A+BproblemII Java 解决 题目103
- Django中关于url的用法
- 021-ARC与分类-OC笔记
- 020-OC内存管理-OC笔记
- 缩放图片
- LA 3667 Ruler 两种不同形式的搜索来解决
- DEDECMS修改文章TAG 给TAG加链接 去掉TAG字数限制
- c#遍历checkBox控件,并用MessageBox显示出来
- 卡榫函数实现API
- Leetcode87: Find Minimum in Rotated Sorted Array
- 019-OC特有语法-OC笔记
- 理解依赖注入(Dependency Injection)
- 剑指offer—把二叉树打印成多行
- break 和continue以及return的区别
- leetcode 21:Merge Two Sorted Lists(15-10-9)
- Generate Parentheses