一、例如,有一个数组:
#import
int main(int argc, const char * argv[])
{
char *countries[] =
{
“Nepal”,
“Cambodia”,
“Afghanistan”,
“China”,
“Singapore”,
“Bangladesh”,
“India”,
“Maldives”,
“South Korea”,
“Bhutan”,
“Japan”,
“Sikkim”,
“Sri Lanka”,
“Burma”,
“North Korea”,
“Laos”,
“Malaysia”,
“Indonesia”,
“Turkey”,
“Mongolia”,
“Pakistan”,
“Philippines”,
“Vietnam”,
“Palestine”
};
return 0;
}
1.这是1个字符串数组,每一个元素都是char *类型的字符串
2.现在我要干嘛呢,我要写1个类,数组类,给这个数组类提供1个方法,将1个字符串数组进行排序
3.来1个类,叫做TestArray,这个类,到目前为止,是不是只要提供1个方法就可以了,排序的方法,对谁排序,对字符串数组排序,要不要返回值,不要,因为数组是地址传递,我把数组传进来以后,我里面一改顺序,是不是外面也跟着改了吧,名字sort,有没有参数,有,是不是应该把那个数组传过来啊,数组的类型,char 星 数组:char *[]
- (void)sortWithCountries:(char *[])countries;
我们说过,这是我们类的方法,参数的类型写在小括弧里面,参数的名字写在外面
在TestArray.m文件里实现1下,现在这个方法要做的事情非常简单,就是对这个数组进行排序,这个countries参数是不是就是数组啊,怎么排序,是不是冒泡排序或者选择排序啊,都可以,你喜欢哪个就用哪个,例如冒泡排序:
- (void)sortWithCountries:(char *[])countries
{
int len = sizeof(countries) / sizeof(char *);
for(int i=0;i
}
把一个数组作为函数的参数的时候,结果就会丢掉这个数组的长度,所以这个sizeof(countries)是几,是8啊
所以你能不能这样去算长度:int len = sizeof(countries) / sizeof(char *);
不能,不能怎么办
是不是要在函数外面算好,然后传进来啊,
- (void)sortWithCountries:(char *[])countries andLength:(int)len;
在TestArray.m文件中实现1把:
- (void)sortWithCountries:(char *[])countries andLength:(int)len{
//
}
然后再使用冒泡排序:
- (void)sortWithCountries:(char *[])countries andLength:(int)len{
for(int i = 0 ; i < len - 1 ; i++)
{
for(int j = 0 ; j < len - 1 - i ; j++){
//下标为j的和下标为j+1的进行比较
//j j+1
//countries[j] countries[j+1]
//只要小于或者大于我就调换位置吧
//那么怎么比较呢
//是不是strcmp啊
//那我是不是应该把那个String.h引进来啊
int res = strcmp(countries[j],countries[j+1]);
if ( res > 0 )
//如果res大于0,说明什么,说明j是不是比j+1大,那我是不是要把j和j+1调换位置啊
if(res > 0 )
{
char *temp = countries[j];
countries[j] = countries[j+1];
countries[j+1] = temp;
}
}
}
}
在main.m文件中试一下
TestArray *arr = [TestArray new];
[arr sortWithCountries:countries andLength:sizeof(countries)/8];
return 0;
}
//这个时候我就可以算数组的长度了,计算数组长度的时候,直接除以8就可以了,因为每一个元素是个char指针,而char指针占8个字节,我是不是直接除以8就可以了
//然后打印出来试一下
这个时候,这个方法执行完了以后,这个数组已经排好序了吧
for(int i = 0;i < sizeof(countries)/8;i++)
{
NSLog(@“%s”,countries[i]);
}
//你发现,是不是就排序好了啊
二、我们现在的需求是,写1个数组类,为这个数组类提供1个方法,为1个国家字符串数组进行排序
1.第一个问题,现在是按字母顺序来排序的,谁让你用字母顺序来排序的,万一产品经理要我们用字符串的长度来排呢,或者用国家的面积来排呢,或者用国家的经济实力来排呢,所以这个时候,这个方法这么写,写死了
int res = strcmp(countries[j], countries[j+1]);
这里比较j和j+1的时候
最开始的做法:比较j和j+1这两个字符串,我们直接比较的字母顺序,也就是ASCII码
但是这么写的话,就写死了
现在,重点就是比较j和j+1,这两个字符串的大小,我们现在比的是它们的ASCII码,也就是字母顺序,这时候调用者的需求是,这个比较规则可不可以由调用者自己说了算
想法:比较这两个字符串的大小,不要方法的内部自己写代码去比,
因为不管写什么都是写死的,
j和j+1这两个字符串,我让调用者自己写1段代码来比
所以这个地方需要执行调用者写的1段代码,来比较j和j+1的大小
首先,应该想个办法,让调用者把代码传到这儿来执行吧
怎么传,block
让调用者把代码放在block里面,传到这儿来执行
大家思考一下,那个block的签名应该怎样做的,有没有返回值,肯定有,要比较两个字符串的大小,你得告诉我谁大谁小吧,你得告诉我第一个是否比第二个大吧,所以返回值什么类型的,BOOL类型
BOOL (^)
写1个block来存储1段代码,这段代码做的事情:比较j 和 j+1 两个字符串的大小,返回结果。
所以这段代码有没有返回值,有,BOOL类型的,你告诉我第一个是否比第二个大
BOOL (^取个名字)
BOOL (^compareBlock)()
有没有参数,有,几个参数,两个参数,什么类型的,是不是字符串啊!
BOOL (^compareBlock)(char *country1,char *country2);
所以,我就希望调用者写这么1段代码传给我,这段代码做的事情做什么事情,比较country1和country2的大小,传到我这儿来执行,就可以了
所以,这时候我是不是应该给它加个参数啊,加个block,那么这个block怎么写呢
- (void)sortWithCountries:(char *[])countries andLength:(int)len andCompareBlock:();
这个compareBlock是不是应该写上刚才我们那个符合要求的代码段吧
这个block怎么写呢
- (void)sortWithCountries:(char *[])countries andLength:(int)len andCompareBlock:();
第一个,我们刚刚说过,我们这个方法的小括弧里面写上参数类型啊,变量名、参数名写在外面啊
- (void)sortWithCountries:(char *[])countries andLength:(int)len andCompareBlock:(BOOL (^)(char *country1,char *country2))compareBlock;
返回值,BOOL类型,小括号,尖尖,尖尖里面需要写变量名字吗,不需要,我们说过啊,名字写在小括号外面啊,再来个小括号,两个参数,最后来一个名字compareBlock
(BOOL (^)(char *country1,char *country2))compareBlock;
这个时候你看啊,我们说过嘛,函数的小括号里面,是不是应该写上参数的类型啊,参数的名字写在小括号外面吧,本来我的block的名字应该写在这个地方对不对,就是尖尖右边,(BOOL (^),但我们是方法,名字写在外面就可以了
如果你感到蒙圈怎么办,是不是用typedef把它简化一下啊
typedef BOOL (^NewType)(char *country1,char *country2);
- (void)sortWithCountries:(char *[])countries andLength:(int)len andCompareBlock:(NewType)compareBlock;
这个时候,比较j和j+1这两个字符串的大小,就不要自己去比了
int res = strcmp(countries[j],countries[j+1]);
谁去比,是不是让block里面的代码去比啊,compareBlock里面的代码去比,而这个block里面的代码要谁写,是不是让调用者自己写啊
上面那句代码,换成
compareBlock(countries[j],countries[j+1]);
因为这个block有两个参数嘛,两个字符串嘛,那我就把这两个字符串传过去啊
返回值是什么类型的,BOOL类型的,拿BOOL类型的变量接一下
BOOL res = compareBlock(countries[j],countries[j+1]);
if(res == YES)
如果res == YES,就说明什么呢,就说明j比j+1大,那我就干嘛呢,我就交换位置
char *temp = countries[j];
countries[j] = countries[j+1];
countries[j+1] = temp;
但是有个问题啊,j和j+1到底怎么比的,是不是这个block里面的代码块来决定的啊,compareBlock,这里面的代码块来决定的啊,而这个block代码块是谁写的,是不是调用者一会儿要传进来的啊
我们在main.m文件里试一下:
肯定不是这样写了吧:
TestArray *arr = [TestArray new];
[arr sortWithCountries:countries andLength:sizeof(countries)/8];
因为加了参数了吧
[arr sortWithCountries:countries andLength:sizeof(countries)/8 andCompareBlock:^BOOL (char *country1,char *country2)compareBlock];
第一个,把这个数组传进去,第二个,把数组的长度传进去,第三个,传什么?
是不是传1个代码段进去啊
回车,回车以后是不是自动生成这个block代码段啊
[arr sortWithCountries:countries andLength:sizeof(countries)/8 andCompareBlock:^BOOL (char *country1,char *country2){
code;
}];
也就是说,这里面,你只要写代码就可以了,你判断country1是不是比country2大就可以了
这个时候,怎么比,是比ASCII码,还是比字符串的长度,随便比,你想怎么比,你就写代码就可以了
如果你想比ASCII码,你就country1和country2去比ASCII码就可以了,如果你想比字符串的长度,你就写上比字符串的长度就可以了
如果我们想比字符串的长度,就这样写:
[arr sortWithCountries:countries andLength:sizeof(countries)/8 andCompareBlock:^BOOL (char *country1,char *country2){
int res = (int)strlen(country1) - (int)strlen(country2);//这是取到第一个字符串的长度,再减去第二个字符串的长度,我一减,假如等于res吧
当然它这个返回值是什么类型的,是size_t类型的是不是,那我把它强转一下,强转成int类型
如果res大于0了,说明什么,是不是说明第一个字符串的长度比第二个字符串的长度大啊,就return YES,否则我就return NO
if(res > 0 )
{
return YES;
}
return NO;
}];
TestArray.m里面的方法,写成如下这样:
for(int i = 0 ; i < len -1 ; i++)
{
for(int j = 0 ; j < len - 1 - i ; j++)
{
int res = compareBlock(countries[j],countries[j+1]);
if(res > 0 )
{
char *temp = countries[j];
countries[j] = countries[j+1];
countries[j+1] = temp;
}
}
}
运行一下,字符串就以长度从小到大来排序了
如果另一个人来调用这个方法,他不想以长度来排序,他想以字母顺序来排,怎么办,是不是只要改一下block里面的代码就可以了
[arr sortWithCountries:countries andLength:sizeof(countries)/8 andCompareBlock:^BOOL(char *country1, char *country2) {
int res = strcmp(country1,country2);
return res > 0 ;
}];
strcmp(country1,country2)这个比的是ASCII码,对不对,拿到1个结果,int res
return res > 0;
如果res大于0,返回的就是YES,否则就是NO
有一个很有意思的问题,我TestArray里面的方法有没有改过,没有改过,我调用两次这个方法,结果不一样,它们排序的标准是不一样的
三、总结
1.什么时候block可以作为方法、函数的参数
当方法的内部需要去执行一段代码,需要执行一个功能,例如咱们这个例子里,
BOOL res = compareBlock(countries[j],countries[j+1]);
这个地方是不是要去比较j和j+1的大小
但是这个功能具体的实现,到底怎么比的,函数的内部不确定
那么这个时候,就使用block,让调用者将这个功能的具体实现传递进来