NSObject是大部分Objective-C類繼承體系的根類。這個類遵循NSObject協定,提供了一些通用的方法,對象通過繼承NSObject,可以從其中繼承通路運作時的接口,并讓對象具備Objective-C對象的基本能力。下面我們就詳細的介紹NSObject。
原文位址:http://blog.csdn.net/zeng_zhiming/article/details/70225456
一、使用詳解
首先建立兩個測試類
建立ZMPerson繼承NSObject
//===============ZMPerson.h檔案==================
#import <Foundation/Foundation.h>
@interface ZMPerson : NSObject
@property (nonatomic, copy) NSString *name; //!< 姓名
@property (nonatomic, assign) NSInteger age; //!< 年齡
/** 穿衣 */
- (void)dressing;
/** 吃東西 */
- (void)eating;
/** 睡覺 */
- (void)sleeping;
/** 跑 */
- (void)running;
@end
//===========ZMPerson.m檔案==============
#import "ZMPerson.h"
@implementation ZMPerson
/** 穿衣 */
- (void)dressing
{
NSLog(@"-->Person dressing");
}
/** 吃東西 */
- (void)eating
{
NSLog(@"-->Person eating");
}
/** 睡覺 */
- (void)sleeping
{
NSLog(@"-->Person sleeping");
}
/** 跑 */
- (void)running
{
NSLog(@"-->Person running");
}
@end
建立ZMStudent繼承ZMPerson
//===============ZMStudent.h檔案==================
#import "ZMPerson.h"
@interface ZMStudent : ZMPerson
@property (nonatomic, strong) NSString *school; //!< 學校
@property (nonatomic, strong) NSString *grade; //!< 年級
/** 寫字 */
- (void)writing;
/** 讀書 */
- (void)readingWithText:(NSString *)text;
/** 加法運算 */
- (NSNumber *)sumWithNum:(NSNumber *)num1 num2:(NSNumber *)num2;
@end
//===============ZMStudent.m檔案==================
#import "ZMStudent.h"
@implementation ZMStudent
/** 寫字 */
- (void)writing
{
NSLog(@"-->Student writing");
}
/** 讀書 */
- (void)readingWithText:(NSString *)text
{
NSLog(@"-->Student reading:%@", text);
}
/** 加法運算 */
- (NSNumber *)sumWithNum:(NSNumber *)num1 num2:(NSNumber *)num2;
{
return @([num1 floatValue] + [num2 floatValue]);
}
@end
1、加載及初始化類
/** 運作時加載類或分類調用該方法, 每個類隻會調用一次 */
+ (void)load {
}
/** 類執行個體化使用前需要先初始化, 一個類調用一次, 如果子類沒有實作該方法則會調用父類方法 */
+ (void)initialize {
}
`load`和`initialize`差別在于:`load`是隻要類所在檔案被引用就會被調用,而`initialize`是在類或者其子類的第一個方法被調用前調用。是以如果類沒有被引用進項目,就不會有`load`調用;但即使類檔案被引用進來,但是沒有使用,那麼`initialize`也不會被調用;`load`每個類隻會調用一次,`initialize`也隻調用一次,但是如果子類沒有實作`initialize`方法則會調用父類的方法,是以作為父類的`initialize`方法可能會調用多次。
2、配置設定記憶體空間及初始化對象
ZMStudent *student = [ZMStudent new];
ZMStudent *student2 = [[ZMStudent alloc] init];
ZMStudent *student3 = [[ZMStudent allocWithZone:nil] init];
建立新對象時,首先調用`alloc`為對象配置設定記憶體空間,再調用`init`初始化對象,如`[[NSObject alloc] init]`;而`new`方法先給新對象配置設定空間然後初始化對象,是以`[NSObject new]`等同于`[[NSObject alloc] init]`;關于`allocWithZone`方法,官方文檔解釋該方法的參數是被忽略的,正确的做法是傳nil或者NULL參數給它。
3、給對象發送消息(執行方法)
(1)直接調用
// 調用無參無傳回值方法
[student running];
// 調用有參無傳回值方法
[student readingWithText:@"Hello World!"];
// 調用有參有傳回值方法
NSNumber *sum = [student sumWithNum:@(2) num2:@(3)];
我們通常都采用這種直接調用的方式,給對象發消息執行方法。這種方式調用編譯時會自動校驗方法、參數、傳回值是否正确。是以我們必須在頭檔案中聲明方法的使用。
(2)使用`performSelector`執行
// 先判斷對象是否能調用方法,再執行調用方法
if ([student respondsToSelector:@selector(running)]) {
// 調用無參無傳回值方法
[student performSelector:@selector(running)];
}
if ([student respondsToSelector:@selector(readingWithText:)]) {
// 調用有參無傳回值方法
[student performSelector:@selector(readingWithText:) withObject:@"Hello World"];
}
if ([student respondsToSelector:@selector(sumWithNum:num2:)]) {
// 調用有參有傳回值方法
NSNumber *sum = [student performSelector:@selector(sumWithNum:num2:) withObject:@(2) withObject:@(8)];
}
使用`performSelector:`是運作時系統負責去找方法,在編譯時候不做任何校驗;是以在使用時必須先使用`respondsToSelector:`檢查對象是否能調用方法,否則可能出現運作崩潰。`performSelector:`常用于調用運作時添加的方法,即編譯時不存在,但是運作時候存在的方法。另外需要注意的是`performSelector:`系統提供最多接受兩個參數的方法,而且參數和傳回都是`id`類型,并不支援基礎資料類型(如:int, float等)。
(3)使用IMP指針調用
// 建立SEL
SEL runSel = @selector(running);
SEL readSel = NSSelectorFromString(@"readingWithText:");
SEL sumSel = NSSelectorFromString(@"sumWithNum:num2:");
// 調用無參無傳回值方法
IMP rumImp = [student methodForSelector:runSel];
void (*runFunc)(id, SEL) = (void *)rumImp;
runFunc(student, runSel);
// 調用有參無傳回值方法
IMP readImp = [[student class] instanceMethodForSelector:readSel];
void (*speakFunc)(id, SEL, NSString *) = (void *)readImp;
speakFunc(student, readSel, @"Hello World");
// 調用有參有傳回值方法
IMP sumImp = [student methodForSelector:sumSel];
NSNumber *(*sumFunc)(id, SEL, NSNumber *, NSNumber *) = (void *)sumImp;
NSNumber *sum3 = sumFunc(student, sumSel, @(6), @(6));
`SEL` 是方法的索引。IMP是函數指針,指向方法的位址。`SEL`與`IMP`是一一對應的關系,是以我們可以通過修改對應關系達到運作時方法交換的目的。
建立`SEL`對象兩種方法:
1、使用`@selector()`建立
2、使用`NSSelectorFromString()`建立
擷取方法`IMP`指針兩種方法:
1、`- (IMP)methodForSelector:(SEL)aSelector;` 執行個體方法
2、`+ (IMP)instanceMethodForSelector:(SEL)aSelector;` 類方法
4、複制對象
// 兩個源數組
NSArray *sourceArrayI = [NSArray arrayWithObjects:@"I", @"I", nil];
NSMutableArray *sourceArrayM = [NSMutableArray arrayWithObjects:@"M", @"M", nil];
// 兩個copy
NSArray *copyArrayI = [sourceArrayI copy];
NSArray *copyArrayM = [sourceArrayM copy];
// 兩個mutableCopy
NSMutableArray *mutableArrayI = [sourceArrayI mutableCopy];
NSMutableArray *mutableArrayM = [sourceArrayM mutableCopy];
`copy`拷貝為不可變對象,`mutableCopy`拷貝為可變變量,`copy`和`mutableCopy`都可了解為複制了一個新對象。雖然`copy`對靜态對象隻是引用計數加1,但是并不影響我們對複制前後的對象進行使用。需要注意的是對于容器對象而言,這兩個方法隻是複制了容器本身,對容器中包含的對象隻是簡單的指針引用,并沒有深層複制。
5、擷取Class
// 擷取類
Class curClass1 = [student class];
Class curClass2 = [ZMStudent class];
// 擷取父類
Class supClass1 = [student superclass];
Class supClass2 = [ZMStudent superclass];
6、判斷方法
// 初始化對象
ZMPerson *person = [ZMPerson new];
ZMStudent *student = [ZMStudent new];
ZMStudent *student2 = student;
// 判斷對象是否繼承NSObject
if ([student isProxy]) {
NSLog(@"student對象是繼承NSObject類");
}
// 判斷兩個對象是否相等
if ([student isEqual:student2]) {
NSLog(@"student對象與student2對象相等");
}
// 判斷對象是否是指定類
if ([person isKindOfClass:[ZMPerson class]]) {
NSLog(@"person對象是ZMPerson類");
}
// 判斷對象是否是指定類或子類
if ([student isKindOfClass:[ZMPerson class]]) {
NSLog(@"student對象是ZMPerson類的子類");
}
// 判斷是否是另一個類的子類
if ([ZMStudent isSubclassOfClass:[ZMPerson class]]) {
NSLog(@"ZMStudent類是ZMPerson類的子類");
}
// 判判斷對象是否遵從協定
if ([student conformsToProtocol:@protocol(NSObject)]) {
NSLog(@"student對象遵循NSObject協定");
}
// 判斷類是否遵從給定的協定
if ([ZMStudent conformsToProtocol:@protocol(NSObject)]) {
NSLog(@"ZMStudent類遵循NSObject協定");
}
// 判斷對象是否能夠調用給定的方法
if ([student respondsToSelector:@selector(running)]) {
NSLog(@"student對象可以調用‘running’方法");
}
// 判斷執行個體是否能夠調用給定的方法
if ([ZMStudent instancesRespondToSelector:@selector(running)]) {
NSLog(@"ZMStudent類可以調用‘running’方法");
}
二、NSObject.h詳解
//
// NSObject.h
// ZMHeaderFile
//
// Created by ZengZhiming on 2017/4/17.
// Copyright © 2017年 菜鳥基地. All rights reserved.
//
// 詳解 NSObject.h
// Version iOS 10.3
//
#ifndef _OBJC_NSOBJECT_H_
#define _OBJC_NSOBJECT_H_
#if __OBJC__
#include <objc/objc.h>
#include <objc/NSObjCRuntime.h>
@class NSString, NSMethodSignature, NSInvocation;
#pragma mark - 協定部分
@protocol NSObject
/** 判斷兩個對象是否相等, 如相等傳回YES, 否則傳回NO */
- (BOOL)isEqual:(id)object;
/** 擷取對象hash值, 兩對象相等hash值也相等 */
@property (readonly) NSUInteger hash;
/** 擷取對象的父類 */
@property (readonly) Class superclass;
/** 擷取目前對象的類 */
- (Class)class OBJC_SWIFT_UNAVAILABLE("use 'type(of: anObject)' instead");
/** 擷取目前對象 */
- (instancetype)self;
/** 發送指定的消息給對象, 傳回消息執行結果(相當于方法調用) */
- (id)performSelector:(SEL)aSelector;
/** 發送帶一個參數的消息給對象, 傳回消息執行結果(相當于方法調用) */
- (id)performSelector:(SEL)aSelector withObject:(id)object;
/** 發送帶兩個參數的消息給對象, 傳回消息執行結果(相當于方法調用) */
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
/** 判斷對象是否繼承NSObject */
- (BOOL)isProxy;
/** 判斷對象是否是給定類或給定類子類的執行個體 */
- (BOOL)isKindOfClass:(Class)aClass;
/** 判斷對象是否是給定類的執行個體 */
- (BOOL)isMemberOfClass:(Class)aClass;
/** 判斷對象是否遵從給定的協定 */
- (BOOL)conformsToProtocol:(Protocol *)aProtocol;
/** 判斷對象是否能夠調用給定的方法 */
- (BOOL)respondsToSelector:(SEL)aSelector;
/** 對象引用計數加1, 在MRC下使用 */
- (instancetype)retain OBJC_ARC_UNAVAILABLE;
/** 對象引用計數減1, 在MRC下使用 */
- (oneway void)release OBJC_ARC_UNAVAILABLE;
/** 對象引用計數以推遲方式自動減1, 在MRC下使用 */
- (instancetype)autorelease OBJC_ARC_UNAVAILABLE;
/** 擷取對象引用計數, 在MRC下使用 */
- (NSUInteger)retainCount OBJC_ARC_UNAVAILABLE;
/** 擷取對象存儲空間, 在MRC下使用 */
- (struct _NSZone *)zone OBJC_ARC_UNAVAILABLE;
/** 擷取對象描述資訊 */
@property (readonly, copy) NSString *description;
@optional
/** 擷取對象在調試器中的描述資訊 */
@property (readonly, copy) NSString *debugDescription;
@end
#pragma mark - 類部分
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0)
OBJC_ROOT_CLASS
OBJC_EXPORT
@interface NSObject <NSObject> {
Class isa OBJC_ISA_AVAILABILITY;
}
/** 運作時加載類或分類調用該方法, 每個類隻會調用一次 */
+ (void)load;
/** 類執行個體化使用前需要先初始化, 一個類調用一次, 如果子類沒有實作該方法則會調用父類方法 */
+ (void)initialize;
/** 初始化對象 */
- (instancetype)init
#if NS_ENFORCE_NSOBJECT_DESIGNATED_INITIALIZER
NS_DESIGNATED_INITIALIZER
#endif
;
/** 為新對象配置設定記憶體空間并初始化, 等于[[NSObject alloc] init] */
+ (instancetype)new OBJC_SWIFT_UNAVAILABLE("use object initializers instead");
/** 為新對象配置設定記憶體空間, 參數傳nil */
+ (instancetype)allocWithZone:(struct _NSZone *)zone OBJC_SWIFT_UNAVAILABLE("use object initializers instead");
/** 為新對象配置設定記憶體空間 */
+ (instancetype)alloc OBJC_SWIFT_UNAVAILABLE("use object initializers instead");
/** 釋放對象, 當對象的引用計數為0時會調用此方法 */
- (void)dealloc OBJC_SWIFT_UNAVAILABLE("use 'deinit' to define a de-initializer");
/** 垃圾回收器調用此方法前處理它所使用的記憶體。 */
- (void)finalize OBJC_DEPRECATED("Objective-C garbage collection is no longer supported");
/** 複制為不可變對象 */
- (id)copy;
/** 複制為可變對象 */
- (id)mutableCopy;
/** 在指定的記憶體空間上複制為不可變對象, 在MRC下使用 */
+ (id)copyWithZone:(struct _NSZone *)zone OBJC_ARC_UNAVAILABLE;
/** 在指定的記憶體空間上複制為可變對象, 在MRC下使用 */
+ (id)mutableCopyWithZone:(struct _NSZone *)zone OBJC_ARC_UNAVAILABLE;
/** 判斷執行個體是否能夠調用給定的方法 */
+ (BOOL)instancesRespondToSelector:(SEL)aSelector;
/** 判斷類是否遵從給定的協定 */
+ (BOOL)conformsToProtocol:(Protocol *)protocol;
/** 擷取指向方法實作IMP的指針 */
- (IMP)methodForSelector:(SEL)aSelector;
/** 擷取指向執行個體方法實作IMP的指針 */
+ (IMP)instanceMethodForSelector:(SEL)aSelector;
/** 找不到函數實作的将調用此方法抛出異常 */
- (void)doesNotRecognizeSelector:(SEL)aSelector;
/** 傳回消息被第一個轉發的對象, 對象沒有找到SEL的IML時就會執行調用該方法 */
- (id)forwardingTargetForSelector:(SEL)aSelector OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/** methodSignatureForSelector:傳回不為nil則調用該方法, 可以重寫該方法将SEL轉發給另一個對象 */
- (void)forwardInvocation:(NSInvocation *)anInvocation OBJC_SWIFT_UNAVAILABLE("");
/** 擷取方法簽名, 對象沒有找到SEL的IML時就會執行調用該方法, 可以重寫該方法抛出一個函數的簽名,再由forwardInvocation:去執行 */
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector OBJC_SWIFT_UNAVAILABLE("");
/** 擷取執行個體方法簽名 */
+ (NSMethodSignature *)instanceMethodSignatureForSelector:(SEL)aSelector OBJC_SWIFT_UNAVAILABLE("");
/** 允許弱引用标量, 對于所有allowsWeakReference方法傳回NO的類都絕對不能使用__weak修飾符 */
- (BOOL)allowsWeakReference UNAVAILABLE_ATTRIBUTE;
/** 保留弱引用變量, 在使用__weak修飾符的變量時, 當被指派對象的retainWeakReference方法傳回NO的情況下, 該變量将使用“nil” */
- (BOOL)retainWeakReference UNAVAILABLE_ATTRIBUTE;
/** 判斷是否是另一個類的子類 */
+ (BOOL)isSubclassOfClass:(Class)aClass;
/** 動态解析一個類方法 */
+ (BOOL)resolveClassMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/** 動态解析一個執行個體方法, 對象沒有找到SEL的IML時就會執行調用該方法, 可以重寫該方法給對象添加所需的SEL */
+ (BOOL)resolveInstanceMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/** 擷取對象hash值, 兩對象相等hash值也相等*/
+ (NSUInteger)hash;
/** 擷取對象的父類 */
+ (Class)superclass;
/** 擷取類 */
+ (Class)class OBJC_SWIFT_UNAVAILABLE("use 'aClass.self' instead");
/** 擷取對象描述資訊 */
+ (NSString *)description;
/** 擷取對象在調試器中的描述資訊 */
+ (NSString *)debugDescription;
@end
#endif
#endif
原文位址: http://blog.csdn.net/zeng_zhiming/article/details/70225456