Objective-C中的@符号特殊用法全集
1. @interface 和 @implementation
在Objective - C中,@interface
用于声明一个类,定义类的属性和方法。它就像是一个蓝图,告诉编译器这个类长什么样,有哪些成员。而@implementation
则是对@interface
中声明的方法进行具体实现。
// 定义一个简单的类
@interface Person : NSObject {
NSString *name;
NSInteger age;
}
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) NSInteger age;
- (void)sayHello;
@end
@implementation Person
@synthesize name;
@synthesize age;
- (void)sayHello {
NSLog(@"Hello, my name is %@ and I'm %ld years old.", name, (long)age);
}
@end
在上述代码中,@interface
部分声明了一个Person
类,它继承自NSObject
,有两个实例变量name
和age
,同时使用@property
声明了两个属性,还声明了一个实例方法sayHello
。@implementation
部分使用@synthesize
为属性生成了存取方法,并实现了sayHello
方法。
2. @property
@property
是Objective - C中用于定义属性的关键字。它简化了实例变量的存取方法的创建。通过@property
,可以指定属性的特性,如内存管理策略、原子性等。
2.1 内存管理特性
- nonatomic:指定该属性的存取方法不是线程安全的。在多线程环境下,可能会出现数据竞争问题,但由于它不进行线程同步,所以性能较高。
- atomic:默认值,属性的存取方法是线程安全的。通过锁机制保证同一时间只有一个线程能访问属性,但这会带来性能开销。
2.2 读写特性
- readwrite:默认值,属性同时拥有读方法(getter)和写方法(setter)。
- readonly:属性只有读方法,没有写方法,适用于一些只需要读取的属性,如视图的边界等。
2.3 内存管理策略
- assign:用于简单数据类型,如
NSInteger
、CGFloat
等。它直接赋值,不涉及内存管理。 - strong:用于对象类型,会增加对象的引用计数。当一个对象被赋值给一个
strong
类型的属性时,该对象的引用计数加1。 - weak:同样用于对象类型,但不会增加对象的引用计数。当对象的引用计数变为0并被销毁时,指向该对象的
weak
属性会自动被设置为nil
,从而避免野指针问题。 - copy:用于对象类型,会创建对象的副本。常用于
NSString
等不可变类型,以防止属性值被意外修改。
@interface Car : NSObject
@property (nonatomic, assign) NSInteger wheelCount;
@property (nonatomic, strong) NSString *brand;
@property (nonatomic, weak) id<CarDelegate> delegate;
@property (nonatomic, copy) NSString *model;
@end
上述代码中,wheelCount
使用assign
,因为它是简单数据类型;brand
使用strong
来持有NSString
对象;delegate
使用weak
以避免循环引用;model
使用copy
确保属性值的不可变性。
3. @synthesize 和 @dynamic
3.1 @synthesize
@synthesize
用于告诉编译器为@property
生成存取方法。在现代的Objective - C中,即使不写@synthesize
,编译器也会自动为@property
生成默认的存取方法。但在某些情况下,我们可能需要手动使用@synthesize
来指定实例变量。
@interface Dog : NSObject
@property (nonatomic, strong) NSString *name;
@end
@implementation Dog
@synthesize name = _dogName;
@end
在上述代码中,通过@synthesize name = _dogName
,指定了name
属性对应的实例变量为_dogName
。这样,编译器生成的存取方法会操作_dogName
这个实例变量。
3.2 @dynamic
@dynamic
与@synthesize
相反,它告诉编译器不要自动生成存取方法。这在一些情况下很有用,比如当存取方法是在运行时动态创建的,或者属性是通过其他方式(如KVO)来实现的。
@interface DynamicPropertyClass : NSObject
@property (nonatomic, strong) NSString *dynamicProperty;
@end
@implementation DynamicPropertyClass
@dynamic dynamicProperty;
@end
在上述代码中,使用@dynamic
声明dynamicProperty
,意味着开发者需要自己在运行时提供存取方法,否则访问该属性会导致运行时错误。
4. @protocol 和 @optional
4.1 @protocol
@protocol
用于定义协议,它是一种特殊的接口,定义了一组方法声明,但不包含方法的实现。任何类都可以声明遵循某个协议,从而承诺实现协议中的方法。
@protocol AnimalProtocol <NSObject>
- (void)eat;
- (void)move;
@end
@interface Cat : NSObject <AnimalProtocol>
@end
@implementation Cat
- (void)eat {
NSLog(@"Cat is eating.");
}
- (void)move {
NSLog(@"Cat is moving.");
}
@end
在上述代码中,@protocol AnimalProtocol
定义了eat
和move
两个方法。Cat
类声明遵循AnimalProtocol
,并实现了协议中的方法。
4.2 @optional
在协议中,默认所有方法都是必须实现的。但通过@optional
关键字,可以将协议中的某些方法标记为可选的,遵循该协议的类可以选择是否实现这些方法。
@protocol NewAnimalProtocol <NSObject>
- (void)eat;
@optional
- (void)fly;
@end
@interface Bird : NSObject <NewAnimalProtocol>
@end
@implementation Bird
- (void)eat {
NSLog(@"Bird is eating.");
}
@end
在上述代码中,@protocol NewAnimalProtocol
中的fly
方法通过@optional
标记为可选方法。Bird
类遵循该协议,但只实现了eat
方法,没有实现fly
方法,这是允许的。
5. @selector
@selector
用于获取一个方法的选择器。选择器是一个表示方法的唯一标识符,它是一个SEL
类型的值。@selector
在消息传递、目标 - 动作机制等方面有广泛应用。
@interface Calculator : NSObject
- (NSInteger)add:(NSInteger)a b:(NSInteger)b;
@end
@implementation Calculator
- (NSInteger)add:(NSInteger)a b:(NSInteger)b {
return a + b;
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Calculator *calc = [[Calculator alloc] init];
SEL addSelector = @selector(add:b:);
NSInteger result = ((NSInteger (*)(id, SEL, NSInteger, NSInteger))objc_msgSend)(calc, addSelector, 3, 5);
NSLog(@"The result is %ld", (long)result);
}
return 0;
}
在上述代码中,通过@selector(add:b:)
获取了Calculator
类中add:b:
方法的选择器,然后使用objc_msgSend
函数进行消息发送,实现了方法调用。
6. @try、@catch 和 @finally
Objective - C中的@try
、@catch
和@finally
用于异常处理。@try
块中包含可能会抛出异常的代码,@catch
块用于捕获并处理异常,@finally
块中的代码无论是否发生异常都会执行。
int main(int argc, const char * argv[]) {
@autoreleasepool {
@try {
NSArray *array = @[@1, @2, @3];
NSInteger value = [array objectAtIndex:5];
} @catch (NSException *exception) {
NSLog(@"Caught exception: %@", exception);
} @finally {
NSLog(@"This is finally block.");
}
}
return 0;
}
在上述代码中,@try
块中访问数组越界会抛出异常,@catch
块捕获并打印异常信息,@finally
块中的代码始终会执行。
7. @class
@class
用于向前声明一个类。它告诉编译器某个类的存在,但不包含类的详细定义。这在减少编译依赖、避免循环引用等方面非常有用。
// ForwardDeclaration.h
@class AnotherClass;
@interface ForwardDeclaration : NSObject
@property (nonatomic, strong) AnotherClass *anotherObject;
- (void)doSomethingWithAnotherClass;
@end
// ForwardDeclaration.m
#import "ForwardDeclaration.h"
#import "AnotherClass.h"
@implementation ForwardDeclaration
- (void)doSomethingWithAnotherClass {
AnotherClass *obj = [[AnotherClass alloc] init];
[obj someMethod];
}
@end
在上述代码中,ForwardDeclaration.h
中使用@class AnotherClass
向前声明了AnotherClass
,这样ForwardDeclaration.h
就不需要导入AnotherClass.h
,减少了编译依赖。在ForwardDeclaration.m
中,由于需要使用AnotherClass
的具体实现,所以导入了AnotherClass.h
。
8. @encode
@encode
用于获取一个类型的编码字符串。这个编码字符串可以表示基本数据类型、对象类型、结构体等。它在运行时类型检查、消息传递等方面有重要应用。
NSString *intEncoding = [NSString stringWithUTF8String:@encode(int)];
NSString *objectEncoding = [NSString stringWithUTF8String:@encode(NSObject *)];
NSString *structEncoding = [NSString stringWithUTF8String:@encode(CGPoint)];
NSLog(@"Int encoding: %@", intEncoding);
NSLog(@"Object encoding: %@", objectEncoding);
NSLog(@"Struct encoding: %@", structEncoding);
上述代码中,@encode(int)
获取int
类型的编码字符串,@encode(NSObject *)
获取NSObject
指针类型的编码字符串,@encode(CGPoint)
获取CGPoint
结构体类型的编码字符串,并将其打印出来。
9. @synchronized
@synchronized
用于实现简单的线程同步。它通过一个锁对象来保证在同一时间只有一个线程能执行被保护的代码块。
@interface SynchronizedClass : NSObject
@property (nonatomic, assign) NSInteger counter;
- (void)incrementCounter;
@end
@implementation SynchronizedClass
- (void)incrementCounter {
@synchronized(self) {
self.counter++;
}
}
@end
在上述代码中,@synchronized(self)
块中的代码在同一时间只有一个线程能执行,从而保证了counter
属性的线程安全递增。
10. @defs
@defs
是一个很少使用的关键字,它用于获取协议定义的方法列表。通常在运行时通过objc_protocol_list
等函数结合@defs
来获取协议的详细信息。
@protocol MyProtocol <NSObject>
- (void)protocolMethod;
@end
struct objc_protocol_list *protocolList = objc_copyProtocolList(&count);
for (NSUInteger i = 0; i < count; i++) {
Protocol *protocol = protocolList[i];
if (strcmp(protocol_getName(protocol), "MyProtocol") == 0) {
struct objc_method_description *methodList = protocol_copyMethodDescriptionList(protocol, YES, YES, &methodCount);
for (NSUInteger j = 0; j < methodCount; j++) {
struct objc_method_description method = methodList[j];
SEL selector = method.name;
const char *types = method.types;
NSLog(@"Method: %@, Types: %s", NSStringFromSelector(selector), types);
}
free(methodList);
}
}
free(protocolList);
上述代码通过objc_copyProtocolList
获取所有协议列表,然后查找MyProtocol
协议,并使用protocol_copyMethodDescriptionList
获取协议中方法的描述列表,包括方法选择器和类型编码。虽然@defs
本身没有直接在代码中体现,但它在运行时获取协议方法列表的机制中起到了作用。
11. @end
@end
用于结束@interface
或@implementation
块,或者结束@protocol
的定义。它明确标记了类、协议定义部分的结束。
@interface SampleClass : NSObject
// 类的声明部分
@end
@implementation SampleClass
// 类的实现部分
@end
@protocol SampleProtocol <NSObject>
// 协议的声明部分
@end
在上述代码中,@end
分别结束了SampleClass
的声明、实现以及SampleProtocol
的定义。
12. @compatibility_alias
@compatibility_alias
用于定义兼容性别名。它允许为一个类型定义一个别名,主要用于兼容旧版本的代码或提供更简洁的类型名称。
@compatibility_alias OldNSString NSString;
OldNSString *str = @"Hello, using alias";
在上述代码中,@compatibility_alias OldNSString NSString
为NSString
定义了一个别名OldNSString
,后续代码中可以使用OldNSString
来代替NSString
。
13. @selectorName
@selectorName
是@selector
的一种变体,它用于获取方法选择器的字符串表示形式。这在一些需要将方法选择器以字符串形式传递或存储的场景中很有用。
@interface MethodSelectorClass : NSObject
- (void)exampleMethod;
@end
@implementation MethodSelectorClass
- (void)exampleMethod {
NSLog(@"This is an example method.");
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
MethodSelectorClass *obj = [[MethodSelectorClass alloc] init];
SEL selector = @selector(exampleMethod);
NSString *selectorName = NSStringFromSelector(selector);
NSLog(@"Selector name: %@", selectorName);
}
return 0;
}
在上述代码中,通过NSStringFromSelector
函数将@selector(exampleMethod)
获取的选择器转换为字符串形式并打印出来。虽然不是直接使用@selectorName
,但这展示了获取方法选择器字符串表示的常见用法。
14. @block
@block
用于定义一个块(block)。块是Objective - C中的一种匿名函数,可以捕获其定义时的上下文变量。块在异步编程、回调等场景中广泛应用。
void (^simpleBlock)(void) = ^{
NSLog(@"This is a simple block.");
};
simpleBlock();
int number = 10;
void (^captureBlock)(void) = ^{
NSLog(@"The number is %d", number);
};
number = 20;
captureBlock();
在上述代码中,simpleBlock
是一个简单的块,直接打印一条信息。captureBlock
捕获了number
变量,尽管在块定义后number
的值发生了改变,但块内部捕获的是定义时number
的值,所以打印的是10
。
15. @autoreleasepool
@autoreleasepool
用于创建一个自动释放池。在Objective - C中,当对象发送autorelease
消息时,对象会被放入最近的自动释放池中。当自动释放池被销毁时,池中的所有对象都会发送release
消息。
int main(int argc, const char * argv[]) {
@autoreleasepool {
for (NSUInteger i = 0; i < 100000; i++) {
NSString *string = [[NSString alloc] initWithFormat:@"Number %lu", (unsigned long)i];
// 这里string发送autorelease消息后会放入当前的自动释放池
}
}
// 自动释放池结束,其中的对象会被释放
return 0;
}
在上述代码中,@autoreleasepool
块内创建了大量的NSString
对象,这些对象在发送autorelease
消息后会被放入自动释放池。当@autoreleasepool
块结束时,池中的对象会被释放,避免了内存峰值过高。
通过对Objective - C中@
符号这些特殊用法的深入理解和掌握,开发者能够更高效、更灵活地编写Objective - C代码,充分发挥Objective - C语言的特性和优势,无论是在开发iOS应用、Mac应用还是其他基于Objective - C的项目中,都能应对各种复杂的编程场景。