黑马程序员--Objective-C语言基础知识--特有语法

来源:互联网 发布:手机专业相机软件 编辑:程序博客网 时间:2024/04/30 04:04

——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-

分类(Category)

一、
在不改变原来类模型的前提下,给类扩充方法,除了可以利用继承特性设计一个子类外,还可以通过设置分类来完成。
分类的格式:

分类的声明:@interface 类名(分类名称)......@end分类的实现:@implementation 类名(分类名称)......@end

示例1:

#import <Foundation/Foundation.h>@interface Person : NSObject@property int age;@property NSString *name;@property double height;- (void)run;- (void)jump;@end@implementation Person- (void)run{    NSLog(@"跑起来了");}- (void)jump{    NSLog(@"跳起来了");}@end@interface Person (Liyi)//声明Person的分类,取名为LiYi- (void)shoot;//在分类中给Person扩充一个方法shoot@end@implementation Person(LiYi)- (void)shoot//实现这个扩充的方法{   NSLog(@"LiYi shoot");}@endint main(){    Person *per = [Person new];//创建一个Person对象    [per shoot];//per调用分类中扩充的方法shoot    return 0;}

示例1的运行结果是:

2015-07-19 15:26:03.792 a.out[1028:189268] LiYi shoot

示例1展示了分类的用法。
二、
分类的好处:
1. 一个庞大的类可以分模块开发;
2. 一个庞大的类可以由多个人来编写,有利于团队合作。
三、
注意点:
1. Category可以访问原始类的实例变量,但不能添加变量,只能添加方法;
2. Category可以实现原始类的方法,但是不推荐这么做,因为它是直接替换掉原来的方法,这样做的后果是再也不能访问原来的方法;
3. 多个Category中如果实现了相同的方法,只有最后一个参与编译的分类中的实现方法才会有效。

协议(@protocol)

一、
协议可用来声明一大堆方法(不能声明成员变量),只要某个类遵守了这个协议,就相当于拥有这个协议中的所有方法声明,只要父类遵守了某个协议,就相当于子类也遵守了。
协议的格式:

协议的编写:@protocol 协议名称方法声明列表@end某个类遵守协议:@interface 类名 : 父类<协议名称>@end

一个类可以遵守多个协议。
二、
协议中有2个关键字可以控制方法是否要实现(默认是@required),大多数情况下,用于程序员之间的交流。
@required:方法前面添加这个关键字意味着这个方法必须要实现,如果不实现编译器会发出警告。
@optional:方法前面添加这个关键字表示这个方法不一定要实现。
三、
协议也可以遵守协议,一个协议可以遵守其他多个协议,多个协议之间用逗号隔开。一个协议遵守了其他协议就相当于拥有了其他协议中的方法声明。
书写格式:

@protocol 协议名称<协议1, 协议2>@end

我们知道NSObject是一个基类,最基本的类,任何其他类最终都要继承它,其实还有一个基协议叫NSObject,它是最基本的协议,通常每个新协议都要遵守NSObject协议。
四、
我们在定义变量时可以限制这个变量保存的对象遵守某个协议,如果没有遵守编译器将会发出警告。通常我们用万能指针(id)定义变量来做协议遵守的限制。
书写格式:

类名<协议名称> *变量名;id<MyProtocol> obj;

协议可以定义在单独的.h文件中,也可以定义在某个类中。如果这个协议只用在某个类中,应该把协议定义在该类中。如果这个协议用在多个类中就应该定义在单独文件中。
示例2:

#import <Foundation/Foundation.h>@protocol Grab<NSObject>@optional- (void)drive;@required- (void)potholing;@end

示例2在Gab.h文件中编写了一个协议,声明了两个方法一个不要求遵守者必须实现,一个要求遵守者必须实现。
示例3:

#import <Foundation/Foundation.h>#import "Grab.h"@interface Person : NSObject<Grab>@property(nonatomic, strong)NSString *name;@property(nonatomic, assign)int age;@property(nonatomic, assign)int height;- (void)jump;- (void)run;@end

示例3在Person.h文件中声明了一个遵守Grab协议的Person类。
示例4:

#import "Person.h"@implementation Person- (void)jump{    NSLog(@"跳跃");}- (void)run{    NSLog(@"奔跑");}- (void)potholing{    NSLog(@"开挖掘机挖坑");}@end

示例5在Person.m文件中实现了Person所遵守的协议中的potholing方法。
示例6:

#import <Foundation/Foundation.h>#import "Person.h"int main(){    id<Grab> obj = [Person new];    [obj potholing];    return 0;}

示例6在main.m文件中应用了遵守Grab这个协议的Person类,我们设定了一个万能指针限制其所指的对象必须遵守Grab协议,Person类的对象是符合要求的。
示例6的运行结果是:
2015-07-19 16:37:08.251 a.out[1148:296322] 开挖掘机挖坑
五、
代理设计模式:
有时候有些事情不想自己做,就想找个人帮忙,这个人就是我们的代理对象。代理设计首先得拥有某个代理对象属性,然后要保证代理人遵守协议,最后利用代理人实现协议内容。
我们用代理设计模式模拟这么一个场景:一个人想挖个坑但是自己不想挖,就像找一个能开挖掘机的司机帮他干这个活,这个司机就是他的代理人,协议就是这个人必须得开挖掘机挖。
示例7:

@protocol Grab<NSObject>@required- (void)drivingExcavator;@end

写一份协议,找得人得遵守这个协议:开挖掘机。
示例8:

#import <Foundation/Foundation.h>#import "Grab.h"@interface Person : NSObject@property(nonatomic, strong)id<Grab> obj;//找的代理人限制其必须遵守我定的协议- (void)potholing;@end#import "Person.h"@implementation Person- (void)potholing{    [_obj drivingExcavator];}@end

这个人找的司机必须得是遵守协议的,找到司机后就可以帮他挖坑了。
示例9:

@interface Driver : NSObject@end@implementation Driver- (void)drivingExcavator{    NSLog(@"驾驶挖掘机去挖坑");}@end

司机实现了协议中的方法,亦即他开挖掘机挖坑。
示例10:

#import <Foundation/Foundation.h>#import "Person.h"#import "driver.h"int main(){    Person *person = [Person new];    Driver *driver = [Driver new];    person.obj = driver;    [person potholing];    return 0;}

示例10模拟了我们设定的场景:想挖坑的人找了个开挖掘机的人帮他把坑挖了。
运行结果是:

2015-07-19 17:47:12.172 a.out[1467:384592] 驾驶挖掘机去挖坑

类的本质

一、
其实类也是一个对象,是Class类型的对象,简称“类对象”。类就是以类对象的形式存储在内存中的。
二、
+load方法:在程序启动的时候会加载所有的类和分类,并自动调用所有类和分类的+load方法,先加载父类再加载子类也就是先调用父类的+load方法再调用子类的+load方法。此外先加载原始类再加载子类。不管程序运行过程中有没有用到这个类,都会调用+load加载。
+initialize方法:在第一次使用某个类时(比如创建对象等),就会调用一次+initialize方法,一个类只会调用一次+initialize方法,先调用父类的再调用子类的。
获取类对象的方法:

方法1Class c = [Person class];方法2:Person *p = [Person new];Class c = [p class];

类对象调用类方法。

输出对象

我们要打印输出某个类通常使用NSLog函数和“%@”格式转换符,在输出对象时会调用- description方法,这个方法继承自基类,它的返回值是一个字符串,输出的就是这个方法默认返回的字符串。
示例11:

#import <Foundation/Foundation.h>@interface Student : NSObject@property(nonatomic, assign)int num;@property(nonatomic, assign)double height;@end@implementation Student@endint main(){    Student *stu = [Student new];//创建对象stu    NSLog(@"%@", stu);//输出对象stu    return 0;}

示例11的运行结果是:2015-07-19 18:16:07.759 a.out[1512:402681] <Student: 0x7fbf3b412940>
默认情况下输出的是对象的类名以及它在内存中的存储地址。
我们可以重写- description方法来输出我们自己想要的效果。
示例12:

#import <Foundation/Foundation.h>@interface Student : NSObject@property(nonatomic, assign)int num;@property(nonatomic, assign)double height;@end@implementation Student- (NSString *)description{    return [NSString stringWithFormat:@"这个学生的学号是%d身高是%.2f", _num, _height];/*重写description方法输出对象的具体属性*/}@endint main(){    Student *stu = [Student new];    stu.num = 9;    stu.height = 1.82;    NSLog(@"%@", stu);    return 0;}

示例12的运行结果是:这个学生的学号是9身高是1.82

代码块(Block)

Block封装了一段代码,可以在任何时候执行。Block可以作为函数参数或者函数返回值,而其本身又可以带参数或返回值。苹果官方建议尽量多用Block。在多线程、异步任务、集合遍历、集合排序、动画转场用的很多。
Block的定义方式:

int (^MySum)(int, int) = ^(int a, int b){    return a + b;};//不同于函数代码块定义结束一定要加“;”号,不加编译器会报错

以上我们定义了一个叫MySum得block对象,它带有两个int型参数,返回int型值。
block可以访问局部变量,但是不能修改局部变量。
示例13:

#import <Foundation/Foundation.h>int main(){    int a = 5, b = 9;    int (^Sum)(int, int) = ^(int x, int y){        return x + y;    };    int sum = Sum(a, b);    NSLog(@"sum = %d", sum);    return 0;}

示例13展示了代码块的简单应用,运行结果是:

sum = 14
0 0
原创粉丝点击