NSObject 是大部分OC类的父类,继承NSObject,可以获取访问运行时的接口,并且具备了OC对象的基本功能。
NSObject 实现了
初始化类
初始化类,提供了两个API, + initialize 和 + load 。
+ initialize
在类收到第一条消息的时候初始化这个类。(即第一次使用这个类的时候,调用这个方法。如果项目中没有使用这个类,这个方法不会被调用)
方法声明
1 | + (void)initialize |
讨论
在类或者子类收到第一条消息前,运行时向每个类发送 initialize消息。父类在子类之前收到这个消息。运行时向这些类发送 initialize消息是线程安全的。也就是当第一个线程向一个类发送消息的时候,其他的线程如果想这个类发送消息就会被阻塞,知道第一个线程的消息结束。
如果子类没有实现 initialize 方法,父类中的 initialize 方法可能会被调用多次。如果你想让父类中的 initialize 方法只调用一次,可以使用下面的结构:1
2
3
4
5+ (void)initialize {
if (self == [ClassName self]) {
// ... do the initialization ...
}
}
因为 initialize 是以阻塞的方式调用的,所以要控制好该方法中的工作量。尽量避免在该方法中使用线程锁,因为这可能导致死锁。除此之外,尽量减少在该方法中执行复杂的操作。
附加
每个类只会调用一次 initialize 方法,如果你想独立的初始化一个类或者该类的类目,那么应该实现+ load 方法。
+ load
当类或者是类目被添加到运行时的时候,就会调用该方法。如果想对类做一些特别的处理,那么可以实现 +load方法。
方法声明
1 | + (void)load; |
讨论
初始化方法调用的顺序:
- 父类的 + load 方法先于子类的+load 方法调用;
- 子类的 +load 方法先于类目的调用
这里有一部分官方文档没看懂,先记记录调用顺序吧
创建、拷贝和销毁对象
+ alloc
为接收该消息者创建一个实例。
示例
1 | TheClass *newObject = [[TheClass alloc] init]; |
讨论
- 如果你想要初始化类中的某些东西,不要重写这个方法,应该去重写 init 方法
- 因为历史原因, alloc 方法会调用 allocWithZone: 这个方法
+ allocWithZone:
为接收该消息者创建一个实例。
方法声明
1 | + (instancetype)allocWithZone:(struct _NSZone *)zone; |
参数 zone 被忽略了已经
示例
1 | TheClass *newObject = [[TheClass allocWithZone:nil] init]; |
讨论
- 如果你想要初始化类中的某些东西,不要重写这个方法,应该去重写 init 方法
- 这个方法的存在是历史遗留问题, memory zone 在 OC 中已经被丢弃了。
- init
当一个对象分配到一块内存之后,这个方法用来初始化这个对象,并将初始化之后的对象返回
方法声明
1 | - (instancetype)init; |
返回值
两种情况:
- 初始化之后的对象;
- nil ,如果在初始化的过程中出现未知异常,该方法可能会返回 nil
讨论
init 方法是和 alloc(或者 *allocWithZone:) 方法一起调用的。1
SomeClass *object = [[SomeClass alloc] init];
一个对象如果没有通过(init) 方法初始化是不可以被使用的(也就是不可以接受消息)。
如果想要自定义这个方法的实现,首先要调用父类的 init 方法,然后再去做你自己的初始化以及返回一个新的对象。如果这个新对象没有初始化成功,那么必须返回 nil。比如,一个 BuiltInCamera 在一部没有摄像头的手机上运行时,初始化方法只能返回 nil。
实例
1 | - (instancetype)init { |
在 Xcode 中写 init 方法的时候 ,只需要输入 init 几个字母之后,就可以看到完整的方法提示了,注意不要输入 “-” 否则提示就不会出现了。
- copy
调用该方法,会返回一个 copyWithZone 方法的结果
方法声明
1 | - (id)copy; |
返回值
这个返回值是 NSCopying 协议中的 copyWithZone: 方法的返回值
讨论
这个方法很方便的让类可以实现 NSCopying协议。 如果调用 copy方法的类没有实现 NSCopying协议,那么就会抛出异常。1
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Whale copyWithZone:]: unrecognized selector sent to instance 0x60400000ecc0'
Whale 这个类没有实现 copyWithZone: 这个方法,解决方法就是:
1 | // Whale.h |
当然,如果缺少了 @interface Whale : NSObject
值得注意的是, NSObject 类并不支持 NSCopying 协议,但是子类必须支持此协议,并且实现 copyWithZone: 这个方法,当然前提是你如果想要调用 copy 方法。
如果子类不是直接继承了 NSObject 类,那么在实现copyWithZone: 方法的时候,首先要调用父类的copyWithZone: 方法,将父类的实现合并到子类中。例如, Whale 继承自 NSObject 那么它就不需要调用父类的 copyWithZone: ,但是 AliceWhale 继承自 Whale 类,那么就必须先调用父类的*copyWithZone:方法 ,合并父类的copy 内容:
1 |
|
-copyWithZone:
没啥好说的
This method exists so class objects can be used in situations where you need an object that conforms to the NSCopying protocol. For example, this method lets you use a class object as a key to an NSDictionary object. You should not override this method.
这一段话以后再去理解吧,没看懂。
- mutableCopy
返回 mutableCopyWithZone: 的返回值
具体使用逻辑和 copy 类似,只是一个是不可变的一个是可变的
- dealloc
取消对象占用的内存的时候调用这个方法。
讨论
- 在该方法的实现中释放一些资源;
- 不能直接调用此方法,该方法是runtime调用具体参见Advanced Memory Management Programming Guide
- ARC 下不能 [super dealloc]
- MRC 下必须调用 [super dealloc],而且必须写在方法最后
识别类
这里主要有三个类方法
- + class , 返回类对象
- + superclass, 返回父类类对象
- + isSubclassOfClass: , 返回一个 Boolean 值,
+ class
返回 类对象
讨论
要引用一个类,必须通过类的名字,初次之外其他的情况我们可以通过这个方法来获取一个类对象。
+ superclass
返回父类的类对象
+isSubclassOfClass:
验证调用该方法的类是否继承自参数给定的类(包括父类的父类也会返回YES)
检测实例方法
+ instancesRespondToSelector:
返回一个BOOL值,来表示实例是否能够相应方法
方法声明
1 | + (BOOL)instancesRespondToSelector:(SEL)aSelector; |
讨论
该方法检测调用类中是否实现了某个实例方法(aSelector),而不是类方法,如果要检测类方法,那么可以调用协议
如果只是在 .h 文件中声明了该方法,而没有在 .m 中去实现这个方法,检测结果为 NO。
检测协议
返回一个BOOL值, 来表示调用该方法的类是否实现了对应的协议(参数)
声明
1 | + (BOOL)conformsToProtocol:(Protocol *)protocol; |
使用示例
1 | BOOL canJoin = [MyClass conformsToProtocol:@protocol(Joining)]; |
讨论
MyClass 实现了 AffiliationRequests 和 AffiliationRequests 协议
1
2@interface MyClass : NSObject <AffiliationRequests, Normalization>
@end协议之间的合并 AffiliationRequests 合并了 Joining 协议
1
2@protocol AffiliationRequests <Joining>
@end如果 MyClass 实现了AffiliationRequests协议,同样实现了 Joining, conformsToProtocol 返回结果为YES
- conformsToProtocol: 该方法不会检测程序有没有实现协议中的方法,只会检测在类的声明后面有没有
。 - 即使你实现了协议的方法,但是没有在类的声明后面通过尖括号包含协议的名字,那么该方法返回仍为NO
获取方法信息
- methodForSelector:
返回方法实现的地址
方法声明
1 | - (IMP)methodForSelector:(SEL)aSelector; |
IMP 方法(函数)指针,也就是实现方法的地址
参数 aSelector必须合法,在传入作为参数之前,可以通过 responseToSelector 检查该参数指向的方法是否已经实现了
1 | MethodInfor *mObj = [MethodInfor new]; |
讨论
方法的调用者如果是 类 那么 aSelector 必须是类方法;
方法的调用者如果是 实例 那么 aSelector 必须是实例方法
+ instanceMethodForSelector:
返回实例方法实现的地址
方法声明
1 | + (IMP)instanceMethodForSelector:(SEL)aSelector; |
IMP 方法(函数)指针,也就是实现方法的地址
参数 aSelector必须合法,在传入作为参数之前,可以通过 instanceMethodForSelector 检查该参数指向的方法是否已经实现了
示例
1 | if ([MethodInfor instancesRespondToSelector:@selector(sayHello)] ) { |
讨论
该方法只用来检测 类 中的实例有没有实现实例方法,不能检测类方法。检测类方法使用 methodForSelector: 。
+ instanceMethodSignatureForSelector:
返回方法签名对象NSMethodSignature
方法声明
1 | + (NSMethodSignature *)instanceMethodSignatureForSelector:(SEL)aSelector; |
示例
1 | NSMethodSignature *sayHelloSignature = [MethodInfor instanceMethodSignatureForSelector:@selector(sayHello)]; |
讨论
该方法会返回一个方法的签名对象,如果类的对象不能响应这个方法,那么会返回一个 nil 对象。
- methodSignatureForSelector:
返回方法签名对象NSMethodSignature
方法声明
1 | - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector; |
示例
1 | NSMethodSignature *sayHelloSignature = [MethodInfor instanceMethodSignatureForSelector:@selector(sayHello)]; |
讨论
该方法会返回一个方法的签名对象,如果类的对象不能响应这个方法,那么会返回一个 nil 对象。
该方法针对类和对象都可以。
这里与
This method is used in the implementation of protocols. This method is also used in situations where an NSInvocation object must be created, such as during message forwarding. If your object maintains a delegate or is capable of handling messages that it does not directly implement, you should override this method to return an appropriate method signature. 没看懂,等了解了 NSInvocation 之后再来看看。