20、深入理解Objective - C:从基础到项目实践

深入理解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代码进行交互,都能更加得心应手。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值