深入理解Objective - C:从基础到项目实践
1. 枚举(Enumerations)
在Objective - C中,枚举的定义以
typedef
关键字开头,接着是
enum
,枚举名称位于定义末尾。每个枚举项包含在花括号内,并用逗号分隔。例如:
typedef enum {
PrimaryColorRed,
PrimaryColorGreen,
PrimaryColorBlue,
} PrimaryColor;
PrimaryColor color = PrimaryColorBlue;
这里有几个要点:
- 枚举的每个项通常以枚举名称开头,这是一种常见的约定,便于代码补全并展示所有可能的值。因为在Objective - C中,不能通过枚举名称本身指定特定的枚举值,每个项都是独立的关键字。
- 枚举不能有方法、关联值,且除整数外不能表示其他值。每个枚举项都有一个数值,如果未指定,从0开始,每项递增1。也可以手动指定值,后续项会继续递增:
typedef enum {
PrimaryColorRed,
PrimaryColorGreen = 10,
PrimaryColorBlue,
} PrimaryColor;
在上述代码中,
PrimaryColorRed
为0,
PrimaryColorBlue
为11。
2. 类(Classes)
Objective - C的类与Swift的类有相似之处,都能包含方法和属性、使用继承和初始化,但外观差异较大。Objective - C的类分为接口和实现两部分。
2.1 基本类定义
以
Contact
类为例,Swift版本如下:
class Contact {
var firstName: String = "First"
var lastName: String = "Last"
}
Objective - C版本:
@interface Contact : NSObject {
NSString *firstName;
NSString *lastName;
}
@end
@implementation Contact
@end
-
接口声明以
@interface关键字开始,以@end结束,方括号内是属性列表。这些属性与结构体的属性类似,但可以包含Objective - C对象。不过,通常不这样写属性,因为使用属性会自动生成。 -
类继承自
NSObject,这是Objective - C中所有类的基类,它提供了很多功能。 -
实现部分以
@implementation关键字开始,后跟类名,最后以@end结束。
2.2 初始化器(Initializers)
Objective - C不允许为属性或属性指定默认值,因此需要实现一个初始化器来设置默认值:
@implementation Contact
- (id)init {
self = [super init];
if (self) {
firstName = @"First";
lastName = @"Last";
}
return self;
}
@end
初始化器是一种特殊的方法,按惯例以
init
开头。返回类型通常为
id
,便于子类重写初始化器。初始化器的一般流程如下:
1. 调用父类的初始化器
[super init]
并将结果赋值给
self
。
2. 检查
self
是否为
nil
,若不为
nil
,则设置默认值。
3. 返回
self
。
2.3 属性(Properties)
为了让
Contact
类的
firstName
和
lastName
属性能从外部访问,需要将它们定义为公共属性:
@interface Contact : NSObject {
}
@property NSString *firstName;
@property NSString *lastName;
@end
属性定义在
@interface
内的花括号外,若没有要定义的内容,花括号可省略。属性会自动生成同名但开头带下划线的属性。初始化时可以这样设置值:
@implementation Contact
- (id)init {
self = [super init];
if (self) {
_firstName = @"First";
_lastName = @"Last";
}
return self;
}
@end
也可以使用
self
设置值:
@implementation Contact
- (id)init {
self = [super init];
if (self) {
self.firstName = @"First";
self.lastName = @"Last";
}
return self;
}
@end
属性还可以指定引用类型(弱引用或强引用)、读写权限:
@interface SteeringWheel : NSObject
@property (weak, readonly) Car *car;
@end
这里
weak
表示弱引用,
readonly
表示只读属性。
2.4 方法(Methods)
方法分为实例方法和类方法:
-
实例方法
:以减号
-
开头,例如:
@implementation Contact
- (NSArray *)addToInviteeList:(NSArray *)invitees includeLastName:(BOOL)include {
NSMutableArray *copy = [invitees mutableCopy];
if (include) {
NSString *newString = [self.firstName stringByAppendingFormat:@" %@", self.lastName];
[copy addObject:newString];
} else {
[copy addObject:self.firstName];
}
return copy;
}
@end
每个参数由公共标签、冒号、括号内的类型和内部名称组成,参数之间用空格或换行分隔。
-
类方法
:以加号
+
开头,例如:
@implementation Contact
+ (void)printAvailablePhonePrefixes {
NSLog(@"+1");
}
@end
调用类方法时可直接在类上调用:
[Contact printAvailablePhonePrefixes];
3. 继承(Inheritance)
Objective - C的类可以像Swift一样继承其他类,方法和属性会从父类继承,也可以在子类中重写方法。但编译器对规则的强制程度较低,例如不强制指定方法重写,也不强制初始化器的调用规则。
4. 类别(Categories)
类别类似于Swift的扩展,可向现有类添加新方法。例如:
@interface Contact (Helpers)
- (NSString *)fullName;
@end
@implementation Contact (Helpers)
- (NSString *)fullName {
return [self.firstName stringByAppendingFormat:@" %@", self.lastName];
}
@end
类别名放在类名后的括号内,每个类别必须有唯一的名称。类别也可以添加属性,但需要自己定义getter和setter方法,类似于计算属性。
5. 协议(Protocols)
Objective - C的协议定义与Swift类似:
@protocol StringContainer
@property (readonly) NSInteger count;
- (void)addString:(NSString *)string;
- (void)enumerateStrings:(void(^)(NSString *))handler;
@end
类实现协议的方式如下:
@interface StringList : NSObject <StringContainer>
@property NSMutableArray *contents;
@end
@implementation StringList
- (NSInteger)count {
return [self.contents count];
}
- (void)addString:(NSString *)string {
if (self.contents == nil) {
self.contents = [NSMutableArray new];
}
[self.contents addObject:string];
}
- (void)enumerateStrings:(void (^)(NSString *))handler {
for (NSString *string in self.contents) {
handler(string);
}
}
@end
要注意,不能直接定义一个变量为协议类型,通常使用
id
类型并要求其实现特定协议:
id<StringContainer> container = [StringList new];
6. 块(Blocks)
块是Objective - C中与Swift闭包对应的概念,语法较复杂。例如:
int (^doubleClosure)(int) = ^(int input){
return input * 2;
};
doubleClosure(2);
块的定义和使用要点如下:
- 变量名前的括号内以
^
开头,变量类型包含返回类型和参数类型。
- 块实现以
^
开头,后跟参数和花括号内的实现。
- 可以定义接受块作为参数的函数或方法:
id firstInArrayPassingTest(NSArray *array, BOOL(^test)(id)) {
for (id element in array) {
if (test(element)) {
return element;
}
}
return nil;
}
在方法中使用块时,参数名的位置会有所不同。
块的内存管理方面,默认参数是强引用,若要使用弱引用,需在块外创建弱变量:
@interface Ball : NSObject
@property int xLocation;
@property (strong) void (^onBounce)();
@end
@implementation Ball
@end
Ball *ball = [Ball new];
__weak Ball *weakBall = ball;
ball.onBounce = ^{
NSLog(@"%d", weakBall.xLocation);
};
7. 项目中的Objective - C代码
Objective - C代码在项目中分为头文件(扩展名为
.h
)和实现文件(扩展名为
.m
)。
7.1 代码暴露问题
与Swift不同,在Objective - C中,必须明确表示要访问另一个文件中的代码。
7.2 头文件(Header files)
头文件可被其他文件包含,应只包含类型的接口。例如:
#import <Foundation/Foundation.h>
#import "Car.h"
@interface SteeringWheel : NSObject
@property (weak) Car *car;
@end
任何文件都可以导入头文件,这相当于将头文件的代码插入到导入的文件中。但要注意,头文件只能包含可安全导入的代码,若放入实现代码,每次导入头文件都会产生重复实现。
综上所述,Objective - C有其独特的语法和编程模式,理解这些概念对于开发Objective - C项目和与Swift代码交互都非常重要。通过掌握枚举、类、继承、类别、协议、块等知识,以及头文件和实现文件的使用,能更好地进行Objective - C编程。
深入理解Objective - C:从基础到项目实践
8. 总结与对比
为了更清晰地理解Objective - C与Swift的差异,下面通过表格进行对比总结:
| 特性 | Objective - C | Swift |
| — | — | — |
| 枚举 | 以
typedef enum
定义,项通常以枚举名开头,只能表示整数 | 更灵活,可关联值、有方法 |
| 类 | 分为接口(
@interface
)和实现(
@implementation
)两部分,初始化需手动设置默认值 | 语法简洁,可直接设置默认值 |
| 继承 | 编译器对规则强制程度低 | 编译器强制规则更严格 |
| 类别 | 类似扩展,可添加方法和属性(需自定义getter和setter) | 扩展功能强大,可添加属性和方法 |
| 协议 | 用
@protocol
定义,使用
id<协议名>
指定实现协议的变量 | 用
protocol
定义,可直接定义协议类型变量 |
| 块 | 语法复杂,默认强引用,弱引用需额外处理 | 闭包语法简洁,内存管理更直观 |
下面是一个展示Objective - C类定义和使用流程的mermaid流程图:
graph LR
classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px
A([开始]):::startend --> B(定义枚举):::process
B --> C(定义类接口 @interface):::process
C --> D(定义类实现 @implementation):::process
D --> E(实现初始化器 - init):::process
E --> F(定义属性 @property):::process
F --> G(定义方法 - 实例方法或 + 类方法):::process
G --> H(实现继承):::process
H --> I(使用类别添加功能):::process
I --> J(定义和实现协议):::process
J --> K(使用块):::process
K --> L(项目中使用头文件和实现文件):::process
L --> M([结束]):::startend
9. 实际应用建议
在实际开发中,使用Objective - C时可以遵循以下建议:
-
代码结构
:合理使用头文件和实现文件,将接口和实现分离,提高代码的可维护性。头文件只包含必要的接口信息,避免放入实现代码。
-
初始化器
:在初始化器中,确保正确调用父类的初始化器,并检查
self
是否为
nil
,以避免潜在的错误。
-
属性管理
:根据需要选择合适的属性修饰符,如
weak
、
strong
、
readonly
等,避免内存泄漏和不必要的修改。
-
方法设计
:对于长方法调用,合理换行,提高代码的可读性。同时,注意实例方法和类方法的区别和使用场景。
-
块的使用
:使用块时,要注意内存管理,特别是避免循环引用。如果可能,使用弱引用避免潜在的内存问题。
10. 与Swift的交互
在现代开发中,很多项目会同时使用Objective - C和Swift代码。以下是一些交互的要点:
-
桥接头文件
:在Swift项目中使用Objective - C代码,需要创建桥接头文件,在其中导入Objective - C的头文件。例如,在
ProjectName-Bridging-Header.h
中导入:
#import "Contact.h"
- Swift调用Objective - C :在Swift代码中可以直接使用导入的Objective - C类和方法。例如:
let contact = Contact()
contact.firstName = "John"
contact.lastName = "Doe"
-
Objective - C调用Swift
:在Objective - C文件中导入
ProjectName-Swift.h文件,就可以使用Swift类和方法。例如:
#import "ProjectName-Swift.h"
SwiftClass *swiftObject = [[SwiftClass alloc] init];
11. 未来展望
虽然Swift已经成为苹果开发的主流语言,但Objective - C仍然在许多旧项目中广泛使用。随着技术的发展,Objective - C可能不会有太多新的特性和改进,但它的稳定性和成熟度使其在一些特定场景下仍然具有价值。同时,理解Objective - C对于维护旧项目、与现有代码交互以及深入理解苹果开发的底层原理都非常有帮助。
总之,Objective - C是一门具有深厚历史和独特魅力的编程语言。通过深入学习和实践,开发者可以更好地掌握这门语言,为苹果平台的开发工作打下坚实的基础。无论是独立开发Objective - C项目,还是与Swift代码进行交互,都能更加得心应手。
超级会员免费看
19

被折叠的 条评论
为什么被折叠?



