NSObject對象簡介
NSObject是大部分Objective-C類繼承體系的根類。這個類遵循NSObject協議,提供了一些通用的方法,對象通過繼承NSObject,可以從其中繼承訪問運行時的接口,并讓對象具備Objective-C對象的基本能力。偷懶了??原文地址
1、加載及初始化類
/** import導入類或分類時調用該方法, 整個生命周期內都只執行一次而已 */
+ (void)load {}
/** 每次調用類時調用一次, 如果子類沒有實現該方法則會調用父類方法 */
+ (void)initialize {}
load
和initialize
區別在于:
load
是只要類所在文件被引用就會被調用。(import就會調用)
initialize
是在類或者其子類的第一個方法被調用前調用。但是如果子類沒有實現initialize
方法則會調用父類的方法,因此作為父類的initialize
方法可能會調用多次。(調用對象的第一個方法前如alloc
)
2、分配內存空間及初始化對象
ZMStudent *student = [ZMStudent new]; //常用
ZMStudent *student2 = [[ZMStudent alloc] init]; //常用
ZMStudent *student3 = [[ZMStudent allocWithZone:nil] init]; //不常用
創建新對象時,首先調用alloc
為對象分配內存空間,再調用init
初始化對象,如[[NSObject alloc] init]
;[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) = (voidvoid *)rumImp;
runFunc(student, runSel);
// 調用有參無返回值方法
IMP readImp = [[student class] instanceMethodForSelector:readSel];
void (*speakFunc)(id, SEL, NSString *) = (voidvoid *)readImp;
speakFunc(student, readSel, @"Hello World");
// 調用有參有返回值方法
IMP sumImp = [student methodForSelector:sumSel];
NSNumber *(*sumFunc)(id, SEL, NSNumber *, NSNumber *) = (voidvoid *)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 nil];
NSMutableArray *sourceArrayM = [NSMutableArray arrayWithObjects:@"M", @"M", nil nil];
// 兩個copy
NSArray *copyArrayI = [sourceArrayI copy];
NSArray *copyArrayM = [sourceArrayM copy];
// 兩個mutableCopy
NSMutableArray *mutableArrayI = [sourceArrayI mutableCopy];
NSMutableArray *mutableArrayM = [sourceArrayM mutableCopy];
1、一個對象使用copy
和mutabelCopy
方法可以創建對象的副本
copy - 需要先實現NSCoppying
協議,創建的是不可變副本(如NSString、NSArray、NSDictionary
)
2、mutabelCopy
- 需要先實現NSMutabelCopying
協議,創建的是可變副本,(如NSMutabelString、NSMutabelArray、NSMutabelDictionary
)
深復制:內容拷貝,源對象和副本指向的是不同的兩個對象。源對象引用計數器不變,副本計數器設置為1
3、淺復制:指針拷貝,源對象和副本指向的是同一個對象。對象的引用計數器+1,其實相當于做了一次retain
操作
4、只有不可變對象創建不可變副本(copy
)才是淺拷貝,其他都是深拷貝
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