深入探索 NSPredicate 与不同语言过渡到 Objective - C 的要点
1. NSPredicate 基础与数组交集操作
NSPredicate 是一个强大的工具,可用于过滤对象。例如,我们可以通过以下代码来过滤数组:
NSArray *names = ...; // 假设这里有一个 names 数组
NSPredicate *predicate = ...; // 定义一个谓词
NSArray *results = [names filteredArrayUsingPredicate: predicate];
下面通过一个具体的例子来展示如何求两个数组的交集:
NSArray *names1 = [NSArray arrayWithObjects:
@"Herbie", @"Badger", @"Judge", @"Elvis", nil];
NSArray *names2 = [NSArray arrayWithObjects:
@"Judge", @"Paper Car", @"Badger", @"Phoenix", nil];
NSPredicate *predicate = [NSPredicate predicateWithFormat: @"SELF IN %@", names1];
NSArray *results = [names2 filteredArrayUsingPredicate: predicate];
NSLog (@"%@", results);
输出结果为:
(
Judge,
Badger
)
其工作原理是,谓词包含了第一个数组的内容,类似于
SELF IN {"Herbie", "Badger", "Judge", "Elvis"}
。第二个数组会根据这个谓词进行过滤,只有同时存在于两个数组中的字符串才会出现在结果数组中。
2. 字符串操作相关的谓词
除了基本的数组过滤,NSPredicate 还提供了一些字符串特定的操作符,如下表所示:
| 操作符 | 含义 |
| ---- | ---- |
| BEGINSWITH | 检查一个字符串是否以另一个字符串开头 |
| ENDSWITH | 检查一个字符串是否以另一个字符串结尾 |
| CONTAINS | 检查一个字符串是否包含另一个字符串 |
例如:
-
"name BEGINSWITH 'Bad'"
可以匹配 “Badger”。
-
"name ENDSWITH 'vis'"
可以匹配 “Elvis”。
-
"name CONTAINS udg"
可以匹配 “Judge”。
不过,这些匹配是区分大小写和重音符号的。例如,
"name BEGINSWITH 'HERB'"
不会匹配 “Herbie”,
"name BEGINSWITH 'Hérb'"
也不会匹配。为了放宽匹配规则,可以使用
[c]
(不区分大小写)、
[d]
(不区分重音符号)或
[cd]
(两者都不区分)。例如,
"name BEGINSWITH[cd] 'HERB'"
可以匹配 “Herbie”。
3. LIKE 操作符与正则表达式
有时候,上述的字符串匹配操作不够强大,这时可以使用
LIKE
操作符。在
LIKE
操作符中,
?
匹配一个字符,
*
匹配任意数量的字符。例如:
-
"name LIKE '*er*'"
可以匹配任何中间包含 “er” 的名字,等同于
CONTAINS
。
-
"name LIKE '???er*'"
可以匹配 “Paper Car”,但不能匹配 “Badger”。
此外,如果需要更强大的字符串匹配能力,可以使用
MATCHES
操作符,它接受一个正则表达式。不过,正则表达式虽然强大,但计算成本较高。如果谓词中有更简单的操作,如基本的字符串操作符和比较操作符,建议先执行这些操作,再使用
MATCHES
,这样可以提高性能。
4. 从其他语言过渡到 Objective - C
许多程序员从其他语言转向 Objective - C 和 Cocoa 时会遇到困难,因为它的行为与其他流行语言不同。以下是从不同语言过渡到 Objective - C 的要点:
4.1 从 C 语言过渡
Objective - C 是在 C 语言的基础上增加了一些面向对象的特性。Objective - C 程序员可以使用 C 语言的标准库,如
malloc()
和
free()
处理动态内存,
fopen()
和
fgets()
处理文件。
当调用使用回调的 ANSI C 库时,不能直接让其调用 Objective - C 方法,因为回调要求的 C 函数签名与实现 Objective - C 方法的函数签名不同。不过,大多数回调库允许提供用户数据或指针。可以使用 Objective - C 对象的地址作为上下文指针,在回调函数中,将上下文指针转换为对象类型并发送消息。
以下是一个示例:
// 创建一个 TreeWalker 对象
TreeWalker *walker = [[TreeWalker alloc] init];
// 创建 XML 解析器
XMLParser parser;
parser = XMLParserLoadFile ("/tmp/badgers.xml");
// 设置解析器的回调函数,并使用 TreeWalker 对象作为上下文
XMLSetParserCallback (parser, elementCallback, walker);
回调函数如下:
void elementCallback (XMLParser *parser, XMLElement *element, userData *context)
{
TreeWalker *walker = (TreeWalker *) context;
[walker handleNewElement: element inParser: parser];
}
4.2 从 C++ 语言过渡
C++ 有很多 Objective - C 缺乏的特性,如多重继承、命名空间、操作符重载、模板、类变量、抽象类和标准模板库等。不过,Objective - C 有一些特性和技术可以替代或模拟这些特性。
例如,可以使用类别(categories)和协议(protocols)来实现类似多重继承或抽象基类的功能。但如果使用多重继承是为了引入额外的实例变量,可以使用组合(composition)和存根方法(stub methods),也可以通过重写
forwardInvocation:
方法来模拟多重继承。
C++ 和 Objective - C 在方法调度机制上也有很大差异。C++ 使用基于虚函数表(vtable)的机制来确定调用的代码,这种方式速度快且安全,但缺乏灵活性。而 Objective - C 使用运行时函数在类结构中查找要调用的代码,速度较慢,但更灵活。
在 Objective - C 中,对象只需要有方法实现就可以被调用,这使得任意对象都可以成为其他对象的数据源或委托。不过,这种灵活性也使得 Objective - C 的类型安全性不如 C++。
Objective - C 没有类变量,可以使用文件作用域的全局变量和访问器来模拟。例如:
@interface Blarg : NSObject
{
}
+ (int) classVar;
+ (void) setClassVar: (int) cv;
@end
#import "Blarg.h"
static int g_cvar;
@implementation Blarg
+ (int) classVar
{
return (g_cvar);
}
+ (void) setClassVar: (int) cv
{
g_cvar = cv;
}
@end
5. Objective - C++
Xcode 中的 Clang 编译器支持一种混合语言 Objective - C++,它允许自由混合 C++ 和 Objective - C 代码,但有一些小的限制。通过使用
.mm
文件扩展名,编译器会将代码视为 Objective - C++。
常见的开发场景是将应用的核心逻辑放在可移植的 C++ 库中,将用户界面用平台的原生工具包编写。Objective - C++ 为这种开发方式提供了很大的便利,既可以获得 C++ 的性能和类型安全性,又可以使用 Objective - C 的动态特性和 Cocoa 工具包。
需要注意的是,Objective - C 和 C++ 的对象层次结构不能混合,不能让 C++ 类继承自 NSView,也不能让 Objective - C 类继承自
std::string
。
综上所述,无论是使用 NSPredicate 进行对象过滤,还是从其他语言过渡到 Objective - C,都需要理解其特性和机制,根据具体需求选择合适的方法和技术。
深入探索 NSPredicate 与不同语言过渡到 Objective - C 的要点
6. 不同语言过渡的总结与对比
从前面的内容可以看出,从 C 和 C++ 过渡到 Objective - C 各有特点,下面通过表格进行总结对比:
| 对比项 | 从 C 过渡 | 从 C++ 过渡 |
| ---- | ---- | ---- |
| 语言基础 | 在 C 基础上增加面向对象特性,可使用 C 标准库 | 缺乏 C++ 部分特性,但有替代方案 |
| 回调机制 | 不能直接用 Objective - C 方法作回调,可利用对象地址作上下文指针 | 无此方面问题,但方法调度机制不同 |
| 多重继承 | 不涉及此问题 | 可用类别、协议、组合、重写方法模拟 |
| 类型安全性 | 与 C 类似,需注意指针操作等 | 相比 C++ 类型安全性较低 |
| 类变量 | 不涉及,C 一般用全局变量 | 用文件作用域全局变量和访问器模拟 |
通过这个表格,我们可以更清晰地看到不同语言过渡到 Objective - C 时的差异,有助于开发者根据自己的编程背景更好地适应 Objective - C。
7. NSPredicate 操作的流程梳理
为了更好地理解 NSPredicate 的使用,下面用 mermaid 流程图展示其基本操作流程:
graph TD
A[定义数组] --> B[定义谓词]
B --> C[使用谓词过滤数组]
C --> D[获取过滤结果]
D --> E{结果判断}
E -- 有结果 --> F[处理结果]
E -- 无结果 --> G[结束操作]
这个流程图展示了使用 NSPredicate 过滤数组的基本步骤:
1. 首先定义要过滤的数组。
2. 接着定义一个合适的谓词,根据不同的需求选择不同的操作符,如
BEGINSWITH
、
LIKE
等。
3. 使用定义好的谓词对数组进行过滤。
4. 获取过滤后的结果。
5. 判断结果是否存在,如果有结果则进行相应的处理,如果没有结果则结束操作。
8. 关于 Objective - C 特性的深入理解
Objective - C 有一些独特的特性,在实际使用中需要深入理解。
8.1 消息发送与 nil 对象
在 Objective - C 中,可以向 nil 对象发送消息,这是一个很特别的特性。消息发送给 nil 对象时,操作会被忽略,返回值根据方法的返回类型有所不同:
- 如果方法返回指针类型(如对象指针),返回值为 nil,可以安全地链式调用。
- 如果方法返回与指针大小相同或更小的 int 类型,返回值为 0。
- 如果方法返回 float 或结构体类型,结果未定义。
这种特性可以避免每次发送消息前都检查对象是否为 NULL,但也可能掩盖错误,导致难以追踪的 bug。例如:
SomeClass *obj = nil;
id result = [obj someMethod]; // result 为 nil
8.2 类的继承与方法重写
Objective - C 中,创建新类时通常会继承自 NSObject 或现有 Cocoa 类,与 C++ 多个独立根的对象层次结构不同。而且在 Objective - C 中,重写方法不需要重新声明或标记为虚拟,任何方法都可以在子类或类别中重写。这有两种不同的观点:
- 一种认为重新声明可以让读者了解类对父类的修改。
- 另一种认为这只是实现细节,类的使用者无需关注,且避免了新方法重写时所有依赖类的重新编译。
9. 实战案例分析
下面通过一个简单的实战案例来综合运用前面提到的知识。假设我们有一个存储联系人姓名的数组,需要过滤出以 “John” 开头的姓名,并且不区分大小写。
// 定义联系人姓名数组
NSArray *contacts = [NSArray arrayWithObjects: @"John Smith", @"Jane Doe", @"Johnny Walker", @"Sam Johnson", nil];
// 定义谓词
NSPredicate *predicate = [NSPredicate predicateWithFormat: @"SELF BEGINSWITH[cd] 'John'"];
// 过滤数组
NSArray *filteredContacts = [contacts filteredArrayUsingPredicate: predicate];
// 输出过滤结果
NSLog (@"%@", filteredContacts);
在这个案例中,我们首先定义了一个联系人姓名数组,然后使用
BEGINSWITH[cd]
谓词来过滤出以 “John” 开头且不区分大小写的姓名,最后输出过滤结果。
10. 总结与展望
通过对 NSPredicate 的学习,我们了解到它是一个强大的对象过滤工具,利用不同的操作符可以实现各种复杂的过滤需求。在从其他语言过渡到 Objective - C 时,需要理解其独特的特性和机制,如回调处理、多重继承模拟、方法调度等。
对于开发者来说,掌握 Objective - C 的这些知识可以更好地进行 iOS 和 OS X 编程。同时,Objective - C++ 的出现为开发提供了更多的选择,可以结合 C++ 的性能和 Objective - C 的动态特性。未来,随着技术的发展,我们可以期待 Objective - C 在更多领域发挥作用,并且与其他语言的融合会更加深入。
在实际开发中,开发者应不断实践,根据具体项目需求灵活运用这些知识和技术,提高开发效率和代码质量。
超级会员免费看
84

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



