本文简述了Objective-C语言的语法(偏面向对象),以及一些个人见解。
Objective-C(之后称OC),顾名思义,是C语言面向对象的一种扩展,它保留了C语言的全部语法,并且兼容C语言,在这基础上建立了一套完整的面向对象编程体系。
在面向对象相关的语言层面上,可以说它是比较像Java的:单继承、有接口等。但是在代码中,它跟C++是比较相似的。
让我们来看代码吧!
#import <UIKit/UIKit.h>
#import "MyClass.h"
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
可以看出,对于库文件、自己创建的类的引用使用了“import”关键字。这里可以说OC虽然完全支持C语言,但是一些细节OC也做了相关的优化,这里不具体陈述,我们使用其推荐的即可。
还可以看到“@autoreleasepool”关键字,OC使用“@”来标识其特有的一些关键字以及特殊用法,如:“@interface”、 “@property”、 “@protocol” 等,可以说“@”在OC中无处不在。
接下来说一下OC的面向对象特性。
作为一个面向对象语言,其面向对象特性我们不得不提,我们从继承、封装以及多态来谈其面向对象特性。
首先,需要介绍其基本的面向对象语言特性,下面来看一些基本代码:
//head file:demo.h
#import <Foundation/Foundation.h>
@interface TestClass : NSObject <NSObject>//继承NSObject类(第一个),遵守NSObject协议(第二个)
@property (nonatomic) NSInteger value;
- (void)replaceTheValueWithValue:(NSInteger)value;
@end
//source file:demo.m
#import <Foundation/Foundation.h>
@implementation TestClass
- (void)replaceTheValueWithValue:(NSInteger)value {
self.value = value;
}
@end
//source file:main.m
#import <Foundation/Foundation.h>
#import "demo.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
TestClass *obj = [[TestClass alloc] init];
obj.value = 1;
NSLog(@"%ld", (long)obj.value);
[obj replaceTheValueWithValue:1];
NSLog(@"%ld", (long)obj.value);
}
}
以上代码展示了一个非常简单的类“TestClass”的定义及其简单的使用。
“Demo.h”文件给出了类的声明,并作为一个“类的说明书”供使用者参考,而“Demo.m”文件则是给出类的定义(需要实现的部分),即给出类的必要部分的实现细节。笔者前面提到的OC与C++类似的部分就在于次。
“main.m”文件则是类似于C语言一个拥有主函数的文件,它确实拥有“main”函数,作为程序的入口。其中有对象的创建、对象属性的使用、对象方法的调用,读者可以进行一下参考。
代码中如果有不明白的地方可以自行研究。
下面将简介其面向对象特性:
1.继承
OC完全支持面向对象的继承特性,并且与Java语言类似,引入了类似于“接口”的“协议”。当然,本文不探讨其真正的共同点与不同点,只是拿来做参考。
下面通过代码来学习:
//为了简化代码,将把所有内容写入一个源文件
//source code:main.m
#import <Foundation/Foundation.h>
@protocol TestProtocol <NSObject>
@optional
- (BOOL)valueFlag;
@required
- (void) replaceTheValueWithValue:(NSInteger)value;
@end
@interface TestClass : NSObject <TestProtocol>
@property (nonatomic) NSInteger value;
@end
@implementation TestClass
- (instancetype)init {
self = [self initWithValue:0];
return self;
}
- (instancetype)initWithValue:(NSInteger)value {
self = [super init];
if (self != nil) {
self.value = value;
}
return self;
}
- (void)replaceTheValueWithValue:(NSInteger)value {
self.value = value;
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
TestClass *obj = [[TestClass alloc] initWithValue:3];
obj.value = 1;
NSLog(@"%ld", (long)obj.value);
[obj replaceTheValueWithValue:1];
NSLog(@"%ld", (long)obj.value);
}
return 0;
}
这里引入了一个叫“TestProtocol”的协议,而新的“TestClass”需要遵守这个协议。其中拥有一些可选(optional)和必须(required)实现的方法。
有关继承,这里使“TestClass”继承了“NSObject”类(事实证明OC中所有的类的最终基类一定是NSObject),它提供给我们的类很多已经实现的东西(比如实例化时的内存分配方法“alloc”),OC是单继承,我们可以用协议来丰富类的方法约束,以及完成一些特殊设计模式的需求。
2.封装
说到封装,我们就不得不提面向对象语言的访问控制,这一部分看似是比较容易的(在C++中,只需要搞清楚“public”、“protected”和“private”的区别并应用到实际设计中),但是在OC(以及新的Swift)中并不是如此,下面还是通过一段代码来说明。
#import <Foundation/Foundation.h>
@interface TestClass : NSObject {
@private
NSInteger _value2;
@protected
NSInteger _value3;
@public
NSInteger _value4;
@package
NSInteger _value5;
}
@property (nonatomic) NSInteger value;
@end
@implementation TestClass
- (void)replaceTheValueWithValue:(NSInteger)value {
self.value = value;
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
TestClass *obj = [[TestClass alloc] init];
obj.value = 1;
// obj->_value2 = 1; //error
// obj->_value3 = 1; //error
obj->_value4 = 1;
obj->_value5 = 1;
[obj replaceTheValueWithValue:1];
}
return 0;
}
OC也拥有类似C++的“public”、“private”、“protected”关键字,只是表现形式不一样。而且OC增加了“@package”关键字,用以表示在该程序包(类似一个依赖库一样)内显公有,对外显私有的状态。
OC的“属性”默认访问控制模式为“@protected”,而“方法”的默认访问控制模式为“@public”,但是为什么上面的属性“value”却可以使用“.”运算符“访问”呢?因为“@property”关键字会为该属性自动生成存取方法,而方法的默认访问控制模式为“@public”,因此可以通过特殊的运算符“.”来访问。(同理大家可以试试访问方法的语法“[obj value]”和“[obj setValue:val]”是否可以用!)
综上所述,OC实现封装还是非常容易的。
3.多态
BaseClass *obj;
obj = [[SubClass1 alloc] init];
obj = [[SubClass2 alloc] init];
多态的实现其实对于OC并不难。首先我们可以创建一个普通的类(区别于虚类),然后用多个子类继承它,再写出类似上面部分的代码来实现多态。但是这样子如果“BaseClass”中需要有纯虚函数,虽然可以不写具体实现来忽略它,但是这样不是最佳的做法,因此我们可以用OC的“协议”来实现对纯虚函数的要求,以及多态的实现,下面通过代码来演示。
#import <Foundation/Foundation.h>
@protocol BaseProtocol <NSObject>
@required
- (void)call;
@end
@interface BaseClass : NSObject
@property (nonatomic, copy) NSString *name;
@end
@interface Dog : BaseClass <BaseProtocol>
//It's own interface like kill
@end
@interface Cat : BaseClass <BaseProtocol>
//It's own interface like jump
@end
@implementation BaseClass
- (instancetype)initWithName:(NSString *)name {
self = [super init];
if (self) {
self.name = name;
}
return self;
}
@end
@implementation Dog
- (void)call {
NSLog(@"%@: Wang!", self.name);
}
@end
@implementation Cat
- (void)call {
NSLog(@"%@: Miao!", self.name);
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
BaseClass <BaseProtocol> *animal;
animal = [[Dog alloc] initWithName:@"tom"];
[animal call];
animal = [[Cat alloc] initWithName:@"jerry"];
[animal call];
}
return 0;
}
可以看到,主函数中给出了运用多态的对象animal(在第57和59行调用“叫”的方法时我们不需要知道是什么在叫,只需要知道它会叫即可)而它可以实例化为其子类的对象。
如有不对敬请指出,感谢阅读!