iOS 基础篇 - 《NSObject对象详解》

NSObject对象简介

NSObject是大部分Objective-C类继承体系的根类。这个类遵循NSObject协议,提供了一些通用的方法,对象通过继承NSObject,可以从其中继承访问运行时的接口,并让对象具备Objective-C对象的基本能力。偷懒了😄原文地址

1、加载及初始化类


/** import导入类或分类时调用该方法, 整个生命周期内都只执行一次而已 */
+ (void)load {} 

/** 每次调用类时调用一次, 如果子类没有实现该方法则会调用父类方法 */
+ (void)initialize {}

loadinitialize区别在于:
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是函数指针,指向方法的地址。SELIMP是一一对应的关系,因此我们可以通过修改对应关系达到运行时方法交换的目的。
创建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、一个对象使用copymutabelCopy方法可以创建对象的副本
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  
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,904评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,581评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,527评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,463评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,546评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,572评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,582评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,330评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,776评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,087评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,257评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,923评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,571评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,192评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,436评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,145评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,127评论 2 352

推荐阅读更多精彩内容

  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,698评论 0 9
  • OC语言基础 1.类与对象 类方法 OC的类方法只有2种:静态方法和实例方法两种 在OC中,只要方法声明在@int...
    奇异果好补阅读 4,266评论 0 11
  • 目录 Objective-C Runtime到底是什么 Objective-C的元素认知 Runtime详解 应用...
    Ryan___阅读 1,935评论 1 3
  • 广州 广州夜宵主要分为大排档、酒楼和小龙虾。 大排档主要代表店铺为东海生鲜。东海生仙的招牌菜中的香口菜有炸傻鳝蟛蜞...
    mamao阅读 404评论 0 0
  • 第一部分 「引」 「神龛」,是供奉神像或神主的小阁子。 今天这篇文章是关于阴阳师的。 关注《阴阳师》手游的人,可能...
    小郭hhhh阅读 747评论 9 6