【IOS学习基础】NSObject.h学习
一、<NSObject>协议和代理模式
1.在NSObject.h头文件中,我们可以看到
- // NSObject类是默认遵守<NSObject>协议的
- @interface NSObject <NSObject> {
- Class isa OBJC_ISA_AVAILABILITY;
- }
- // 往上翻看到NSObject协议的声明
@protocol NSObject
/*
中间一大堆方法的声明
*/
@end
然后我就产生疑问了,为什么我们自己定义的协议是这样,后面加上了<NSObject>。为什么我们自己声明的协议需要遵守<NSObject>协议?
我们都知道,遵守一个协议之后就拥有的该协议中的所有方法的声明。
- @protocol MyProtocol <NSObject>
- /*
- 中间一大堆方法的声明
- */
- @end
2.代理模式
1> 代理模式:其实就是把自己的事情交给自己的代理去办。A去做一件事,但是他做不到或者他不想做,那么A就去请一个代理B,并且A还要定义一份协议(协议中声明要做的事),只要B遵守了这份协议(表示B有能力帮A完成这些事),就按照这份协议把事情交给B去做。
有一句话:谁想做什么事,谁就定义协议,并设置一个代理;谁想帮做什么事,谁就遵守协议并实现方法。
2> 下面以一个例子来说明:
有一个boss类
- //
- // Boss.h
- // 代理模式
- //
- // Created by Silence on 16/1/26.
- // Copyright © 2016年 Silence. All rights reserved.
- //
- #import <Foundation/Foundation.h>
- // boss有一份协议<ZhuLiDelegate>
- @protocol ZhuLiDelegate <NSObject>
- // 助理扫地的方法
- -(void)zhuLiSaoDi;
- @end
- @interface Boss : NSObject
- // 老板有一个助理Deegate,并且这个助理是遵守了ZhuLiDelegate协议的
- // 注意:这里我用的strong修饰的这个delegate(即Boss拥有一个助理的属性Delegate,并且是强引用),但是我并没有在Proxy类中声明一个Boss的属性(即助理有一个老板的属性),老板与助理之间并没有造成循环引用的问题。
- @property (nonatomic,strong)id<ZhuLiDelegate> delegate;
- // 老板想要扫地
- -(void)saoDi;
- @end
- //
- // Boss.m
- // 代理模式
- //
- // Created by Silence on 16/1/26.
- // Copyright © 2016年 Silence. All rights reserved.
- //
- #import "Boss.h"
- @implementation Boss
- // 老板想要扫地(实现)
- -(void)saoDi
- {
- // 老板想要扫地,但是不想自己扫,所以他先查看自己的助理会不会扫地
- if ([self.delegate respondsToSelector:@selector(zhuLiSaoDi)])
- {
- // 如果助理会扫地,那么老板就叫助理去扫地(调用助理的zhuLiSaoDi方法)
- [self.delegate zhuLiSaoDi];
- }
- }
- // 说明:respondsToSelector方法,判断某一个对象是否能够响应该方法
- @end
有一个助理类Proxy
- //
- // Proxy.h
- // 代理模式
- //
- // Created by Silence on 16/1/26.
- // Copyright © 2016年 Silence. All rights reserved.
- //
- #import <Foundation/Foundation.h>
- #import "Boss.h"
- // 助理类遵守Boss类定义的协议ZhuLiDelegate
- @interface Proxy : NSObject<ZhuLiDelegate>
- // 那么就表示Proxy有了该协议下的方法声明
- @end
- //
- // Proxy.m
- // 代理模式
- //
- // Created by Silence on 16/1/26.
- // Copyright © 2016年 Silence. All rights reserved.
- //
- #import "Proxy.h"
- @implementation Proxy
- // 实现协议中的方法
- -(void)zhuLiSaoDi
- {
- NSLog(@"助理去扫地!!!");
- }
- @end
主函数中
- #import <Foundation/Foundation.h>
- #import "Proxy.h"
- int main(int argc, const char * argv[]) {
- @autoreleasepool {
- Proxy *proxy = [[Proxy alloc] init];
- Boss *boss = [[Boss alloc] init];
- // 老板找一个助理对象作为自己的代理
- boss.delegate = proxy;
- // 老板想扫地(调用的自己扫地方法,实际是在里面调用的助理扫地的方法)
- [boss saoDi];
- }
- return ;
- }
- // 打印
- -- ::33.817 代理模式[:] 助理去扫地!!!
以上就是整个代理模式的实现了。
3.继续回到 1.中的那个问题,为什么自己定义的协议要遵守<NSObject>协议?
然后我尝试把上面的<zhuLiDelegate>协议声明处的<NSObject>删掉,编译之后出现错误,如下;
注意:respondsToSelector方法是在<NSObject>协议中声明的方法。
这里我们发现self.delegate对象不识别这个方法了,回到delegate声明处:
- @property (nonatomic,strong)id<ZhuLiDelegate> delegate;
// 这个delegate遵守了<ZhuLiDlegate>协议,但是我们把<ZhuLiDlegate>协议遵守<NSObject>协议的地方删除了,也就表示self.delegate失去了<NSObject>协议中的所有方法声明,所以就导致方法不可识别了
// 另外,还需明白协议是可以继承了,既然所有NSObject类默认遵守了<NSObject>协议,那么就表示所有继承自NSObject的对象都拥有<NSObject>协议中的方法。除非你像上面一样遵守自己的协议,并且自己的协议并不遵守基协议<NSObject>,这样你的对象就无法调用了<NSObject>中的方法了。
二、<NSObject>协议的方法
1.我们都知道,协议中只能声明一大堆方法,但是我们可以看到<NSObject>中有这么几个“属性”
- @property (readonly) NSUInteger hash;
- @property (readonly) Class superclass;
- @property (readonly, copy) NSString *description;
- @optional
- @property (readonly, copy) NSString *debugDescription;
实际上这些和在分类中增加属性一样,这里只会为你生成相应的set、get方法,并不会生成相应的成员变量(实例变量)
拿上面的代理模式做测试,我在<zhuLiDelegate>协议,我在其中加了一个name的“属性”(始终记住:生成set、get方法)
然后在遵守该协议的Proxy类中实现了该set、get方法(具体参考我的上一篇文章”【ios学习基础】OC类的相关”中的在分类实现添加属性),在这里还是贴上代码
- // 假如没有实现相应set、get方法,用.属性访问会崩溃。
#import "Proxy.h"- #import <objc/runtime.h>
- static void *strKey = &strKey;
- @implementation Proxy
- -(void)setName:(NSString *)name
- {
- objc_setAssociatedObject(self, &strKey, name, OBJC_ASSOCIATION_COPY);
- }
- -(NSString *)name
- {
- return objc_getAssociatedObject(self, &strKey);
- }
- // 实现协议中的方法
- -(void)zhuLiSaoDi
- {
- NSLog(@"助理去扫地!!!");
- }
- @end
主函数中
- #import <Foundation/Foundation.h>
- #import "Proxy.h"
- int main(int argc, const char * argv[]) {
- @autoreleasepool {
- NSObject *obj = [[NSObject alloc] init];
- Proxy *proxy = [[Proxy alloc] init];
- Boss *boss = [[Boss alloc] init];
- // 老板找一个助理对象作为自己的代理
- boss.delegate = proxy;
- proxy.name = @"协议:name属性";
- NSLog(@"%@",proxy.name);
- // 老板想扫地(调用的自己扫地方法,实际是在里面调用的助理扫地的方法)
- [boss saoDi];
- }
- return ;
- }
- // 打印
- -- ::03.625 代理模式[:] 协议:name属性
- -- ::03.627 代理模式[:] 助理去扫地!!!
2.方法介绍
1> - (BOOL)isEqual:(id)object; 比较两个对象的地址是否相等
2> - (id)performSelector:(SEL)aSelector; 调用sSelectopr方法
- (id)performSelector:(SEL)aSelector withObject:(id)object; 调用sSelectopr方法,传一个参数
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2; 调用sSelectopr方法,传两个参数
这里简单介绍一下SEL类型:
我们都知道,每一个继承自NSObject对象都有一个isa指针,指向“类的方法列表”(类也有存储空间,既也有地址,一个类在存储空间中仅此一份)
- @interface NSObject <NSObject> {
- Class isa OBJC_ISA_AVAILABILITY;
- }
SEL就是对方法的一种包装。包装的SEL类型数据它对应相应的方法地址,找到方法地址就可以调用方法。在内存中每个类的方法都存储在类对象中,每个方法都有一个与之对应的SEL类型的数据,根据一个SEL数据就可以找到对应的方法地址,进而调用方法。
SEL类型的定义: typedef struct objc_selector *SEL
SEL的使用:SEL S1 = @selector(test); 将test方法包装成SEL对象(无参)
SEL S2= @selector(test:); 将test方法包装成SEL对象(有参)
SEL S3 = NSSelectorFromString(@"test"); 将一个字符串方法转换成为SEL对象
3> - (BOOL)isProxy; 判断一个实例是否继承自NSObject,如果返回NO就是继承自NSObject,反之返回YES
4> - (BOOL)isKindOfClass:(Class)aClass; 判断对象是否属于aClass及其子类
- (BOOL)isMemberOfClass:(Class)aClass; 判断对象是否属于aClass类
- (BOOL)conformsToProtocol:(Protocol *)aProtocol; 检查某个对象是否遵守了aProtocol协议
- (BOOL)respondsToSelector:(SEL)aSelector; 判断对象是否能响应aSelector方法
5> 内存管理相关
- (instancetype)retain OBJC_ARC_UNAVAILABLE;
- (oneway void)release OBJC_ARC_UNAVAILABLE;
- (instancetype)autorelease OBJC_ARC_UNAVAILABLE;
- (NSUInteger)retainCount OBJC_ARC_UNAVAILABLE;
三、NSObject的方法
1.初始化相关
+ (void)load; 类的头文件被引入就会调用
+ (void)initialize; 类或其子类的第一个方法被调用之前调用
- (instancetype)init 初始化
+ (instancetype)new OBJC_SWIFT_UNAVAILABLE("use object initializers instead"); 创建一个对象
+ (instancetype)allocWithZone:(struct _NSZone *)zone OBJC_SWIFT_UNAVAILABLE("use object initializers instead"); alloc方法内部调用该方法,返回分配的存储空间zone
+ (instancetype)alloc OBJC_SWIFT_UNAVAILABLE("use object initializers instead"); 分配存储空间
- (void)dealloc OBJC_SWIFT_UNAVAILABLE("use 'deinit' to define a de-initializer"); 对象销毁时调用
2.方法相关
+ (BOOL)instancesRespondToSelector:(SEL)aSelector; 判断类是否有aSelector方法
+ (BOOL)conformsToProtocol:(Protocol *)protocol; 判断类是否遵守此协议
- (IMP)methodForSelector:(SEL)aSelector; 根据一个SEL,得到该方法的IMP(函数指针)
+ (IMP)instanceMethodForSelector:(SEL)aSelector; 类方法,返回的是类方法的真正的函数地址
- (void)doesNotRecognizeSelector:(SEL)aSelector; 处理接收者无法识别的消息
- (id)forwardingTargetForSelector:(SEL)aSelector; 当某个对象不能接受某个selector时,将对该selector的调用转发给另一个对象
3.+ (BOOL)isSubclassOfClass:(Class)aClass; 判断一个类是否是其子类
+ (NSUInteger)hash; 如果isEqual判断两个对象相等,那么两个对象的hash返回值也一定相等。反之hsah值相等,isEqual未必认为两者一样。
+ (Class)superclass; 获得父类类对象
+ (Class)class OBJC_SWIFT_UNAVAILABLE("use 'aClass.self' instead"); 获得本类类对象
+ (NSString *)description; NSLog打印格式。
+ (NSString *)debugDescription; 打断点时看到的格式。
【IOS学习基础】NSObject.h学习的更多相关文章
- Python学习---基础函数的学习
1.1. 基础函数 函数是指将一组语句的集合通过一个名字(函数名)封装起来,要想执行这个函数,只需调用其函数名即可. 灌输一个概念:Python中函数就是对象,函数和我们之前的[1,2,3],'abc ...
- Redis学习---基础学习[all]
什么是NoSQL型数据库 NoSQL数据库---NoSQL数据库的分类 Redis学习---NoSQL和SQL的区别及使用场景 Redis学习---负载均衡的原理.分类.实现架构,以及使用场景 什么是 ...
- 转 iOS Core Animation 动画 入门学习(一)基础
iOS Core Animation 动画 入门学习(一)基础 reference:https://developer.apple.com/library/ios/documentation/Coco ...
- 【IOS学习基础】内存管理
1.内存几大区域 1> 栈区:局部变量(基本数据类型.指针变量). 2> 堆区:程序运行的过程中动态分配的存储空间(创建的对象). 3> BSS段:没有初始化的全局变量和静态变量. ...
- 零基础如何系统学习Java Web
零基础如何系统学习Java Web? 我来给你说一说 你要下决心,我要转行做开发,这样你才能学成. 你要会打字,我公司原来有一个程序员,打字都是两个手一指禅,身为程序员你一指禅怎么写出的代码,半个 ...
- JavaScript学习基础部分
JavaScript学习基础 一.简介 1.JavaScript 是因特网上最流行的脚本语言,并且可在所有主要的浏览器中运行,比方说 Internet Explorer. Mozilla.Firefo ...
- 20165206学习基础和C语言基础调查
- 技能 我的一项可以拿的出手的技能是萨克斯.但不敢说有多厉害,更不敢说比大多数人更好,只能说是还可以.我学萨克斯有5年左右的时间吧,这5年里印象最深刻的还是前两年.前两年主要是基础训练.我从最基础的 ...
- 20165318 预备作业二 学习基础和C语言基础调查
20165318 学习基础和C语言基础调查 技能学习经验 我们这一代人,或多或少的都上过各种兴趣班,舞蹈钢琴画画书法,我也是如此.可这些技能中,唯一能拿的出手的就是舞蹈了.按照<优秀的教学方法- ...
- 20165226 学习基础和C语言基础调查
心得体会 驱动迭代 学习是一个老师与学生互动的过程,二者关系又恰如健身教练与学员,在进行基础知识的培训后还需借助工具加强相关方面的训练.学习提升的过程离不开学生在实践中发现问题并在老师的帮助下解决问题 ...
随机推荐
- 64bit ubuntu14.04编译PlatinumKit出现的arm-linux-androideabi-g++: not found错误解决方法
编译命令:scons target=arm-android-linux build_config=Release 出现错误: scons: Reading SConscript files ...** ...
- (转)android多国语言适配
android多国语言文件夹 android多国语言文件夹文件汇总如下:(有些语言的书写顺序可能跟中文是相反的) 中文(中国):values-zh-rCN 中文(台湾):values-zh-rTW 中 ...
- android 多语言适配
建好android项目后,默认的是有个values文件,该文件下面默认的有strings.xml. 做多语言适配的时候,就需要在创建好的项目中,右键单击res文件夹,选择创建新的xml文件. 然后点击 ...
- Extjs 6 MVC开发模式(二)
1.Extjs MVC开发模式 在JS的开发过程中,大规模的JS脚本难以组织和维护,这一直是困扰前端开发人员的头等问题.Extjs为了解决这种问题,在Extjs4.x版本中引入了MVC开发模式,开始将 ...
- Follow-up letter to information seeking meeting, e-mail version
Subject: (logical to recipient!) Thank you for meeting Tuesday, Nov. 23 November 26, 20XY Mr. Jame ...
- SQL-LINQ-Lambda 语法对照
SQL LINQ Lambda SELECT *FROM Employees from e in Employees select e Employees .Select (e => e) ...
- Delphi 常用API 函数(好多都没见过)
AdjustWindowRect 给定一种窗口样式,计算获得目标客户区矩形所需的窗口大小AnyPopup 判断屏幕上是否存在任何弹出式窗口ArrangeIconicWindows 排列一个父窗口的最小 ...
- 【关于360极速浏览器的xx极速模式自动切换到兼容模式】
原理上是可以的. 1 360基于Chromium 开源浏览器内核,它本身就是一个壳子.. 2 7.0之后的极速浏览器,不支持 它官方的那个声明标记.<meta name=”renderer” ...
- 深入理解MFC子类化
子类化,通俗来讲就是用自己的窗口处理函数来处理特定消息,并将自己其他消息还给标准(默认)窗口处理函数.在SDK中,通过SetWindowLong来指定一个自定义窗口处理函数:SetWindowLong ...
- C#调用HTTP接口
string url = "http://www.***.com?id=123&username=test&userpwd=*****"; WebRequest w ...