iOS - 类与对象
类方法
OC 中类的方法只有实例方法和静态方法两种:
@interface Controller: NSObject
/// 静态方法
+ (void)thisIsAStaticMethod;
/// 实例方法
- (void)thisIsAnInstanceMethod;
@end
OC 中的方法只要声明在 @interface 里,就可以认为是公有的。实际上,OC 没有像 Java,C++ 中的那种绝对的 私有及保护 的成员方法,仅仅可以对调用者隐藏某些方法。
声明和实现都写在 @implementation 里的方法,类的外部是看不到的。
可以使用 分类(Category) 和 类扩展(Extension)来实现私有方法。
// AClass.h
@interface AClass: NSObject
- (void)sayHello;
@end
// AClass.m
@interface AClass (private)
- (void)privateSayHello1;
@end
@interface AClass ()
- (void)privateSayHello2;
@end
@implementation AClass
- (void)sayHello
{
[self privateSayHello];
}
- (void)privateSayHello1
{
NSLog(@"privateSayHello1");
}
- (void)privateSayHello2
{
NSLog(@"privateSayHello2");
}
@end
使用这种方法外部就不能直接调用到 privateSayHello1
和 privateSayHello2
方法。
类变量
Apple 推荐现代的 Objective-C 中使用 @property
来实现成员变量,使用 @property
声明的变量可以使用 实例名.变量名
来获取和修改。
@property
可以看做一种语法糖,使用 @property
此声明
@interface AClass: NSObject
@property (nonatomic, copy) NSString *name;
@end
可等价于
@interface AClass: NSObject
{
NSString *_name;
}
- (NSString *)name;
- (void)setName:(NSString *)name;
@end
@implementation AClass
- (NSString *)name
{
return _name;
}
- (void)setName:(NSString *)name
{
_name = name;
}
@end
@property
会自动生成 getter
和 setter
方法,同时进行自动内存管理
@property
可声明的属性修饰符有如下几种:
readwrite
可读写,需要生成getter
和setter
方法.readonly
只读,只会生成getter
方法,不会生成setter
方法,不希望属性在类外改变时使用。assign
赋值属性,setter
方法将传入的参数赋值给实例变量。strong
持有特性,setter
方法将传入的参数先保留,再赋值,传入参数的引用计数会 +1。copy
拷贝特性,setter
方法将传入对象复制一份;需要完全一份新的变量时使用。nonatomic
和atomic
,决定编译器生成的getter
和setter
方法是否是原子操作。
默认的 @property
是
基本数据类型: readwrite
、assign
、atomic
;
对象: readwrite
、strong
、atomic
;
Protocol
OC 是单继承的,OC 中的类可以实现多个 protocol
来实现类似 C++ 中多重继承的效果。
protocol
类似 JAVA 中的 interface
,定义了一个方法列表,这个方法列表中的方法可以使用 @required
@optional
标注,以表示该方法是否客户类必须要实现的方法。
当 protocol
中含有 property
时,编译器不会进行自动 synthesize
的,需要手动处理:在实现这个 protocol
的时候要么再次声明 property
要么手动 synthesize
Category
Category
是一种很灵活的扩展原有类的机制,使用 Category
不需要访问原有类的代码,也无需集成,Category
提供了一种简单的方式,来实现类的相关方法的模块化,把不同的方法分配到不同的类文件中。
在使用 Category
时需要注意的是,如果有多个 Category
均实现了同一个方法,那么这些方法在运行时只有一个会被调用,具体哪个会被调用是不确定的,因此建议在使用 Category
时在函数命名方法加上前缀。
Extension
Extension
可以认为是一种匿名的 Category
,Extension
与 Category
有如下区别:
- 使用
Extension
必须有原有类的源码 Extension
声明的方法必须在类的主@implementation
区间内实现,可以避免使用有名Category
带来的多个不必要的@implementation
。Extension
可以在类中添加新的属性和实例变量,Category
不可以。Extension
和Category
里添加的方法必须要有实现。(没有实现编译器会给出警告)Category
一般用来给类添加私有的变量和方法,在类的内部使用。
如果在类中添加全局变量
一个简单直接的做法是在 .m 文件中使用 static
变量。由于 static
变量在编译器就是确定的,因此对于 NSObject
对象来说,初始化的值只能是 nil, 如何进行类似 init 的初始化呢? 可以通过重载 initialize
方法。
static NSOperationQueue *_queue = nil;
@implementation XWPerson
- (void)initialize
{
if (!_queue) {
_queue = [[NSOperationQueue alloc] init];
}
}
@end
为什么这里要判断是否为 nil
呢?因为 initialize
方法可能会调用多次。
有一种方法是声明 static
函数,下面代码来自 AFNetworking,声明了一个当前文件范围可用的队列:
static dispatch_queue_t url_session_manager_creation_queue() {
static dispatch_queue_t af_url_session_manager_creation_queue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
af_url_session_manager_creation_queue = dispatch_queue_create("com.alamofire.networking.session.manager.creation", DISPATCH_QUEUE_SERIAL);
});
return af_url_session_manager_creation_queue;
}
除此以外,还可以通过编译器的 __attribute_
特性来实现初始化:
__attribute__((constructor))
static void initialize_Queue() {
_personOperationQueue = [[NSOperationQueue alloc] init];
}
类的初始化
Objective-C 是建立在 Runtime 基础上的语言,类也不例外。OC 中类的初始化也是动态的,在 OC 中绝大部分类都继承自 NSObject
,它有两个非常特殊的类方法 +load
和 +initialize
,用于类的初始化。
+load
+load
方法是当类或分类被添加到 Objective-C runtime 时被调用的,实现这个方法可以让我们在类加载的时候执行一些类相关的行为。子类的 +load
方法会在它所有的父类的 +load
方法之后执行,而分类的 +load
方法会在它的主类 +load
方法之后执行。
+initialize
+initialize
方法是在类或它的子类收到第一条消息之前被调用的,这里所指的消息包括实例方法和类方法的调用。也就是说 +initialize
方法是以懒加载的方式被调用的。如果程序一直没有给某各类或它的子类发送消息,那么这个类的 +initialize
方法是永远不会被调用的。
如果一个分类实现了 +initialize
方法,那么就会对这个类中的实现造成覆盖。
更多干货文章
博客:www.qiuxuewei.com
微信公众号:@开发者成长之路
一个没有鸡汤只有干货的公众号