//首先回忆一下什么是函数指针
#import
void printRose( )
{
printf(" {@} \n");
printf(" | \n");
printf(" \\|/ \n");
printf(" |");
}
int main(int argc,const char * argv[])
{
printRose() ;
void (*roseP) ();
roseP = printRose;
roseP();
return 0;
}
//
// void代表指向的函数没有返回值
// ()代表指向的函数没有形参
// (*roseP )代表roseP是一个指向函数的指针
void (*roseP) ();
roseP = printRose;
roseP();
//定义一个block变量,
// block和函数一样,可以没有返回值,也没有形参
//也可以没有返回值有形参
//也可以有返回值没有形参
//也可以有返回值有形参
//所以,在定义一个block变量的时候,也需要告诉该变量将来保存的代码有没有返回值和形
// void代表b lock将来保存的代码没有返回值
// ()代表block将 来保存的代码没有形参
// (^roseBlock) 代表reseBlock是一个block变量,可以用于保存一-段block代码
//下面的写法就是先定义再初始化
void (^roseBlock) ();
roseBlock = ^{
//roseBlock = ^(){ 一般是这个样子写,没有参数可以不写(),有参数就给()里面写参数
printf(" {@} \n");
printf(" | \n");
printf(" \\|/ \n");
printf(" |");
};
//到这里就相当于定义了一个block函数,他和函数指针很想,只不过他把函数的实现全部都写在了函数内部
//要想执行block保存的代码,必须调用block才会执行
roseBlock( ) ; //这句话就是调用block函数
//定义一个变量有两种方式一种是先定义在初始化,一种是在定义的时候直接初始化
//1.
//int a;
//a = 10
//2.
//int a = 10;
//所以说上面的代码还可以这样写
void (^roseBlock) () = ^(){
//如果没有参数这个()可以省略
//还有一种写法就是假如block代表的这个函数有参数,和返回值
//void (^roseBlock) (int) = ^int(int){ 一般^后面的这个int也就是返回值一般都是省略
printf(" {@} \n");
printf(" | \n");
printf(" \\|/ \n");
printf(" |");
};
//首先先回忆一下typedef怎么使用
int sum(int value1, int value2)
{
return value1 + value2;
}
int minus(int value1, int value2)
{
return value1 - value2;
}
typedef int (*calculte)(int, int);
int main(int argc,const char * argv[])
{
//调用加法
calculte sumP = sum;
NSLog (@"sum = %i", sumP(20, 10));
I
//调用减法
calculte minusP = minus ;
NSLog (@"minus = %i", minusP(20, 10));
return 0;
}
//接着使用block和typedef的结合试试
//注意:利用typedef给block起别名, 和指向函数的指针一样,block变量的名称就是别名
typedef int (^calculteBlock)( int,int);
calculteBlock sumBlock = ^(int value1, int value2){
return value1 + value2;
};
NSLog(@"sum = %i", sumBlock(20, 10));
block的应用场景,比如现在有一系列的操作,这一系列操作里面重复的步骤很多,比如说我们打开一个文件往文件里面写东西,是不是我们打开文件和关闭文件的一系列操作都是一样的,此时我们就可以,只有打开文件之后在文件内部进行的操作不一样,我们可以把在文件内部的操作封装成一个block函数,然后再有一个函数前半部分是打开后半部分是关闭文件,此时我们可以把我们封装的block方法插入到打开和关闭中间,这个时候每次调用王往文件里面写东西这个操作的时候就可以,把文件里面写的内容当作参数传递给block,block又包含在打开关闭文件之间,所以就会减少代码的重复
//当发现代码的前面和后面都是一 样的时候,这个时候就可以使用block
void goToWork(void (^workBlock)()) //参数代表是一个block类型没有参数没有返回值
{
NSLog(@"起床") ;
NSLog(@"穿衣服");
NSLog(@"洗漱");
NSLog(@"喝早茶");
NSLog(@"驾车去上班");
//不一样
workBlock();
NSLog(@"收拾东西");
NSLog(@"驾车回家");
NSLog(@"吃晚饭");
NSLog(@"洗澡");
NSLog(@"睡觉");
}
//调用
void goToWorkInDay1( )
{
goToWork(^{
NSLog(@"认识新同事");
});
}
#import
int main(int argc, const char * argv[])
{
// 1. block中可以访问外面的变量
/*
int a=10;
void (^myBlock)() = ^{
NSLog(@"a = %i", a);
};
myBlock();
*/
// 2. block中可以定义和外界同名的变量,并且如果在block中定义了和外界同名的变量,在block中访问的是block中的变量
/*
int a=10;
void (^myBlock)() = ^{
int a = 20;
NSLog(@"a = %i", a);
};
myBlock();
*/
现在来总结一下,为啥加了_block在内部的修改就可以影响到外部,而不加就无法修改么?
可以讲OC代码在命令行通过cc --help查看帮助指令,并且通过一个参数转换成C++代码,通过对C++代码
分析后可以看出,block方法其实底层是处理成函数,然后按照函数的方式去传递参数的,在不使用_block的时
候按照的将外部的a是按照值传递的方式来传参的,所以在block函数内部对a进行修改并不会影响到外部的a,但
是在变量前加上_block之后就是按照指针传递地址的方式,所以在内部是可以修改外部的a的。
上面的地址为啥打印结果不同,我也不知道
Protocal基本概念:
注意协议他也就是一种接口
Protocal 语法格式:
协议就是由方法的声明组成的:
首先创建一个Objective-c File
接着文件类型选择Protocal。接着我们就使用了一个Xcode模板来创建了一个协议模板,协议只有.h文件
协议是什么?协议有什么用?
1.协议只有.h文件,里面只有函数的声明,没有实现,为什么没有实现呢?谁遵守我,谁去实现,
也就是可以保证实现方法的多样性,但是又可以保证接口的一致性
2.所有的类都可以遵守,也就是把我的协议当成模板,实现我模板里面的方法
//Sportprotocal.h //协议
#import
@protocol SportProtocol <NS0bject>
//方法声明列表
-( void) playFootball;
- (void) playBasketball;
- (void) playBaseball;
@end
//Person.h
#import
#import "SportProtocol. h"
@interface Person : NS0bject <SportProtocol>
@end
//Person.m
#import "Person. h" //注意包含头文件
@implementation Person
- (void)playFootball
{
NSLog(@"s",__func__) ;
}
- (void)playBasketball
{
NSLog(@"%s",__func__);
}
- (void) playBaseball
{
NSLog (@"%s",__func__ ) ;
}
@end
//main.m
#import
#import "Person. h"
#import "Student. h"
int main(int argc, const char * argv[])
{
//下面就可以直接进行调用
Person *p = [Person new] ;
[p playFootball] ;
[p playBasketball];
[p playBaseball];
return 0;
}
协议比继承的使用场景更广一点
协议和分类差不多就是在不修改原有类的基础上给这个类增加一些功能
不同的是,协议只有声明没有实现,而分类是既有声明也有实现
我们在学习分类的时候,分类只能扩充方法不能扩充属性,协议也一样
1.遵循协议的时候需要添加协议的头文件
2.只有父类遵循了某个协议,子类继承下来之后也会遵循这个协议
3.在OC中一个类可以遵守一个或者多个协议,顺便提一嘴,OC中的类只有能一个父类,这个和C++不一样
4.OC中的协议又可以遵守其它协议,只要-一个协议遵守了其它协议,那么这个协议中就会自动包含其它协议的声明
5.协议只能声明方法不能声明属性
6.我们新创建的类遵守NSObject协议,我们新创建的协议也要遵守NSObject协议
#import
@protocol SportProtocol <NS0bject>
//方法声明列表
//注意:如果没有使用任何关键字修饰协议中的方法,那么该方法默认就是required的
@required
-(void)playFpotball;
- (void) playBasketball;
- (void) playBaseball;
@end
//WifeCondition.h
#import
@protocol WifeCondition <NSObject>
//会做饭
- (void) cooking;
//会洗衣服
- (void)washing;
//有一份好工作
- (void)job;
@end
//Wife.h
#import
#import "WifeCondition. h"
@interface Wife : NSObject <WifeCondition>
@end
//Wife.m
//注意此时妻子这个类只是遵守了这个协议,这个时候就算你在赋值的时候进行了类型的限制,但是也不会在编译的时候发出警告,在运行调用的的时候还是会出错。
@implementation Wife
@end
//People.h
#import
#import "Wife. h"
@interface Person : NSObject
//媳妇
//注意:记住一点,类型限定是写在数据类型的右边的
@property (nonatomic, strong) Wife<WifeCondition> *wife; //注意此时是妻子这个对象是要遵守这个协议
@end
//People.m
#import "Person. h"
@implementation Person
@end
//main.m
#import
#import "Person. h"
#import "Wife. h"
#import "WifeCondition. h"
int main(int argc, const char * argv[])
{
Person *p = [Person new] ;
// 1. 协议的第一个应用场景,可以将协议写在数据类型的右边,明确的标注如果想给该变量赋值,那么该对象必须遵守某个协议
Wife<WifeCondition> *W = [Wife, new] ;
p.wife = w;
//注意此时有第二个需要注意的地方
//假如正如我在Wife.m里面说的,虽然妻子有类型限制,但是假如妻子并没有实现这写方法那么也是无法运行的
p.wife.cooking(); //此时我们调用妻子类所遵守的协议的方法会发现运行出错,所以我们在以后允许这种情况的时候需要进行判断,判断这个类到底有没有实现这个协议
// 注意:虽然在接受某-一个对象的时候,对这个对象进行了类型限定(限定它必须实现某个协议),但是并不意味着这个对象就真正的实现了该方法。所以每次在调用对象的协议方法时应该进行一次验证
if ([p.wife respondsToSelector :@selector(cooking)]) //如果实现了返回YES否则NO
{
[p.wife.cooking];
}
if ([p. wife respondsToSelector: @selector(washing)])
{
[p.wife.washing];
}
if ([p.wife respondsToSelector:@selector(job)])
{
[p.wife.job] ;
}
return 0;
}
//在其子类中实现下面的方法上面的便可以正常运行
//Wife.m
#import "Wife. h"
@implementation Wife
//会做饭,
- (void) cooking
{
NSLog(@"%s",__func__ );
}
//会洗衣服
- (void)washing
{
NSLog(@%"s", __func__ );
}
//有一份好工作
- (void)job
{
NSLog(@%"s", __func__ );
}
其实就是类的组合,可以更加灵活的去处理一些事情,使代码重复率降低
就不仔细说明了。
算了还是说吧。
//用代理来实习学生找房子
//Student.m
#import "Studnet. h"
@implementation Studnet
- (void) findHourse
{
NSLog(@"学生想找房子");
//通知代理帮他找房子
if ([self. delegate respondsToSelector :@selector( studentF indHourse)])
{
[self.delegate studentFindHourse] ;
}
@end
//StudentProtocal.h
#import
@protocol StudentProtocol <NS0bject>
//帮学生找房子
-(void) studentFindHourse;
@end
//Student.h
#import
#import "StudentProtocal.h"
@interface Studnet : NSObject
//找房子
- (void) findHourse;
@property (nonatomic, strong) id<StudentProtocal> delegate;//这个代理被协议限制,必须要会找房子
@end
//LinkHome.h
#import
#import "StudentProtocol.h"
@interface LinkHome : NSObject<StudentProtocol>
@end
//LinkHone.m
#import "LinkHome.h"
@implementation LinkHome
- (void) studentFindHourse
{
NSLog(@"我会帮学生找房子" );
}
@end
/如果你现在觉得上面的代理不好你可以再找代理,你只需要重新定义代理类遵守协议就可以了。更加灵活
/*
协议的编写规范:
1.一般情况下,当前协议属于谁,我们就将协议定义到谁的头文件中
2.协议的名称一般以它属于的那个类的类名开头,后面跟上protocoL或者delegate
3.协议中的方法名称一般以协议的名称protocol之前的作为开头
4. 一般情况下协议中的方法会将触发该协议的对象传递出去
5. 一般情况下一个类中的代理属于的名称叫做delegate
6.当某一个类要成为另外一个类的代理的时候,大般情况下在. h中用@protocol协议名称;告诉当
前类这是一个协议。在.m中才使用#import来真正导入协议的声明
*/
//所以应该将上面的StudentProtocal.h文件删掉,然后在Student.h文件里面这样写
#import
@class Student;
@protocol StudentProtocol <NS0bject>
- (void) studentFindHourse: (Student *)student;
@end
@interface Student : NSObject
@property (nonatomic, strong) id<StudentProtocol> delegate;
- (void) findHourse;
@end
还有一点需要注意,若是按照上面的写法我们就将之前的协议文件,写到了Student.h文件里面去,这样的
话,后续要是有的类需要协议的声明的话,如果直接包含Student.h这个类的话,开销就比较大,所以要是有的
类需要协议的声明的话就会像下面这样样子
#import
//#import "Student.h" 此时就不用包含Student.h的头文件了,因为Student.h里面包含的内容要多,没必要为了包含一个协议就全部包含进来
@protocol StudentProtocol; //只需要在这里说明这个协议就可以了
@interface LinkHome : NSObject <StudentProtocol>
@end