Objective-C
简单了解一下,目的是能看懂 Objective-C 的代码。
以前对 Objective-C 有偏见,觉得它的语法其丑无比,但最近稍微深入了解了一下 Objective-C 的语法,觉得其实它的写法还是蛮有意思的,一次调用就像是在写一个人人都能看懂的英文句子一样,这不正是 Ruby 想要达到的目的吗?
References
Note 1
Note for Objective-C 入门教程
Objective-C - C 的超集
Objective-C 是 C 的超集,因此它兼容任何 C 的语法。它在 C 的基础上扩展了面向对象编程。
这里的内容只针对 Objective-C 面向对象这部分,如果不涉及类和对象的语法,基本和 C 一样。
文件名
- .h - 头文件,声明接口
- .m - 源代码,可以包含 Objective-C 和 C 的实现代码
- .mm - 源代码,除 Objective-C 和 C 代码外,还可以包括 C++ 代码。
在源代码中包含头文件时,可以用 C 的 #include
编译选项,但更推荐用 Objective-C 的 #import
,它作用和 #include
一样,但可以确保相同的文件只被包含一次。
语法
Objective-C 的面向对象语法源于 Smalltalk 消息传递风格。所有其他非面向对象的语法,包括变量类型,预处理器,流程控制,函数声明与调用皆与 C 语言完全一致。
消息传递
Objective-C 最大的特色是承自 Smalltalk 的消息传递模型 (message passing),此机制与今日 C++ 式之主流风格差异甚大。Objective-C 里,与其说对象互相调用方法,不如说对象之间互相传递消息更为精确...
C / C++ 的方法调用在编译时就决定了,而 Objective-C 的消息在运行时动态决定,典型的鸭子模型。(所以 Objective-C 综合了静态语言和动态语言的特点。)
字符串
C 语言的字符串用起来不方便,Objective-C 一般使用 NSString 对象处理字符串,和其它高级语言处理字符串一样方便。只需要在一般的字符串前面加 @
符号。
NSString* myString = @"My String\n";
NSString* anotherString = [NSString stringWithFormat:@"%d %s %@", 1, "String", @"NSString"];
// 从一个 C 语言字符串创建 Objective-C 字符串
NSString* fromCString = [NSString stringWithCString:"A C string"
encoding:NSASCIIStringEncoding];
类
Objective-C 的类的说明和 C++ 一样,包含两部分:
- 类的声明 (interface),写在 .h 中
- 类的实现 (implementation),写在 .m 中
类的声明
类的声明中只包括公开的变量和方法。
语法:
@interface MyClass : NSObject
{
int count;
id data;
NSString* name;
}
- (id)initWithString:(NSString*)aName;
+ (MyClass*)createMyClassWithString:(NSString*)aName
andString:(NSString*)bName;
@end
- 类的声明用 @interface 编译选项开始,@end 结束
- 类名之后是继承自的父类,用冒号隔开
- 类的实例成员定义在大括号中
- 类的实例方法前用
-
声明 - 类的类方法前用
+
声明 ()
中声明的是方法的返回类型,以及参数的类型 (后面有更详细的说明)
强类型与弱类型
MyClass *myObject1; // 强类型声明
id myObject2; // 弱类型声明
弱类型 id 一般用于集合中,且隐含了指针类型,类似 void*
。
类的实现
类的实现中,包括公开方法的实现,以及私有变量和方法。
语法:
@implementation MyClass
{
int memberVar3; // 私有成员变量
}
+ (MyClass*)createMyClassWithString:(NSString*)aName
andString:(NSString*)bName {
// ...
}
- (id)initWithString:(NSString*)aName {
// ...
}
// 其它私有方法
// ...
@end
创建对象
Objective-C 创建对象需通过 alloc 以及 init 两个消息。alloc 的作用是分配内存,init 则是初始化对象。 init 与 alloc 都是定义在 NSObject 里的方法。
MyObject *my = [[MyObject alloc] init];
在 Objective-C 2.0 里,若创建对象不需要参数,则可直接使用 new。
MyObject *my = [MyObject new];
若要自己定义初始化的过程,可以重写 init 方法。
方法
方法声明语法:
- (void)insertObject:(id)anObject atIndex:(NSUInteger)index
-
表示这是一个类的实例方法,+
表示类方法(void)
表示方法的返回类型(id)
(NSUInteger)
表示参数类型andObject
index
表示参数名insertObject:
atIndex:
方法签名关键字,包括冒号,如果没有冒号,说明这个方法不需要参数。整个方法实际的名字是这些方法签名关键字的级联,比如这里,方法实际名字是insertObject:atIndex:
。
发消息 (即方法调用)
Objective-C 用 []
来表示方法调用 (不知道是不是仅此一家)。
[myArray insertObject:anObj atIndex:0];
允许 []
的嵌套。
[[myAppObject getArray] insertObject:[myAppObject getObjectToInsert] atIndex:0];
属性
简单地说,属性就是帮你自动生成 setter 和 getter 方法用的。类似 ruby 的 attr_writer
attr_reader
attr_accessor
用法。
(这一小节写得不好)
@property BOOL flag;
@property (copy) NSString *nameObject; // 赋值时拷贝对象
@property (readonly) UIView *rootView; // 只声明 getter 方法
每个可读属性将指定一个与属性同名的方法。上例中 rootView 将会得到一个 rootView 的方法。
- (UIView*)rootView {
// 类似实现
return self._rootView;
}
每个可写属性将指定一个形如 setPropertyName:
的方法,在其中属性名的第一个字母被转换为大写。上例中 nameObject 将会得到一个 setNameObject:
方法。
- (void)setNameObject:(NSString*)nameObject {
// 类似实现
self._nameObject = [NSString stringWithFormat:@"%@", nameObject];
}
如果要对一个属性同时产生 getter 和 setter 访问方法,可以用 @synthesize
声明。同时,如果用 @dynamic
声明,则表示 getter 和 setter 访问方法由开发者自己实现。
对属性的访问,具有多种形式,可以用传统的 .
号,还可以用 C++ 的 ->
,还可以用 valueForKey:
setValue:forKey:
等形式。
Person *aPerson = [[Person alloc] initWithAge: 53];
aPerson.name = @"Steve";
// 等于
[aPerson setName: @"Steve"];
NSLog(@"Access by message (%@), dot notation(%@), property name(%@) and direct instance variable access (%@)",
[aPerson name], aPerson.name, [aPerson valueForKey:@"name"], aPerson->name);
类或协议的属性可以被动态的读取。略。
枚举
// 使用NSEnumerator
NSEnumerator *enumerator = [thePeople objectEnumerator];
Person *p;
while ( (p = [enumerator nextObject]) != nil ) {
NSLog(@"%@ is %i years old.", [p name], [p age]);
}
// 使用依次枚举
for ( int i = 0; i < [thePeople count]; i++ ) {
Person *p = [thePeople objectAtIndex:i];
NSLog(@"%@ is %i years old.", [p name], [p age]);
}
// 使用快速枚举
for (Person *p in thePeople) {
NSLog(@"%@ is %i years old.", [p name], [p age]);
}
协议 - Protocol
Swift 中也有这个概念,是一样的意思,简单地理解成类似接口的概念。
声明语法:
@protocol Locking
- (void)lock;
- (void)unlock;
@end
用 @protocol 开始,用 @end 结束。
某个类表示遵循此协议:
@interface SomeClass : SomeSuperClass <Locking>
@end
实现此协议:
@implementation SomeClass
- (void)lock {
// ...
}
- (void)unlock {
// ...
}
@end
Category
为已存在类扩展方法,对应应该就是 Swift 中的 extension。
其余略。
Note - 2
Note for Objective-C 语法总结
对象
Objective-C 和其它语言最大的不同的一点在于,方法调用不用 .
号,而是用 []
,仅此一家了吧,而且几乎每个参数都有名字,参数之间也不用 ,
分隔,而直接用空格分隔,参数不用 ()
包围,而是跟在参数名字的 :
号。
当然,在 Objective-C 里,也不叫方法调用,而是叫给某个对象发消息,这个和 Ruby 是相同的思想。这决定了 Objective-C 的语言具有一种运行时的动态性。
来看看 Objective-C 如何调用对象的方法 (我们还是按传统地叫法称呼之)。
Party *partyInstance = [Party alloc];
[partyInstance init];
[partyInstance addAttendee: somePerson
withDish: deviledEggs];
感受到了吗? partyInstance addAttendee somePerson withDish deviledEggs
,是不是一句完整的句子。
上面同时演示了如何创建对象,一般需要 alloc 和 init 两个方法,alloc 为对象分配内存,但还没有初始化值,所以还不能正常工作,init 方法调用后,对象被初始化,才有了值,才可以正常工作。
对象的销毁:
partyInstance = nil;
值得注意的是,Objective-C 中可以给空对象发消息,因此不需要做判空操作。(这就体现了 Objective-C 的动态性)
常见对象
NSObject <---- NSArray <---- NSMutableArray
<---- NSString <---- NSMutableString
NSObject 是所有类的父类。
1 - NSString
- NSString 表示不可变可符串
- NSMutableString 表示可变字符串
字符串表示:
NSString *myString = @"Hello, world";
字符串内容用 @
符号开头。
格式化输出:
int a = 1;
float b = 2.5;
char c = 'A';
NSString *d = @"Hello";
NSLog(@"Integer: %d, Float: %f, Char: %c, String: %@", a, b, c, d);
其余略。
5.4 关于 autoreleasepool 和 ARC
ARC 即 Automatic Reference Counting,自动引用计数。是相对于 MRC (Manual Reference Counting,手动引用计数) 而言的。
若使用 @autoreleasepool 关键字括起来,当括号里方法执行完时,实例对象会自动释放。
@autoreleasepool {
BNRItem *item = [BNRItem someItem];
}
iOS 使用 ARC 和 autoreleasepool 机制实现内存管理。