iOS课程观看笔记(二)---OC语言相关

开局一张图
在这里插入图片描述
围绕着OC语言相关问题,大致涉及到图片中所有的内容。

分类(Category)

你用分类都做了哪些事情?
  1. 声明私有方法
  2. 分解体积庞大的类文件
  3. 把Framework的私有方法公开

声明私有方法这个,我感觉就是将原来的写在原类中的私有方法,在分类中进行声明。
比如定义一个分类,只有头文件放到对应宿主.m里,满足私有方法的声明和使用,不暴露具体实现。

分类的特点:
  1. 运行时决议
    指的是在运行时,通过runtime将分类里面的内容添加到宿主类里面
  2. 可以为系统类添加分类
分类中都可以添加哪些内容?
  1. 实例方法
  2. 类方法
  3. 协议
  4. 属性(只声明了get和set方法,并没有实现get和set方法,也没有生成实例变量)

分类的结构:
在这里插入图片描述
在这里插入图片描述
具体代码不再具体分析,可参考iOS分类Category的本质

本文只摘抄部分上文中没有的内容,或者比较重要的内容。

这里只分析分类中的实例方法。

在编译阶段:

编译器将category里面的内容,编译成category_t类型的结构。

在运行阶段:
  1. runtime将每一个分类中的实例方法组成一个数组,然后,将这些数组再组合到一个大数组中,
  2. 然后从最后一个元素(数组类型)开始,添加到分类的instance_methods数组中,
  3. 再将拼接好的二维数组instance_methods放入宿主类的class_rw_t的methods当中。
举个例子:

假如某个类YZPerson有三个分类:YZPerson+eat.m、YZPerson+run.m、YZPerson+walk.m

首先,将每个分类里面的实例方法组合成一个数组,然后,再将三个分类组合成一个大数组。
这样,分类中的实例方法列表,就是一个二维数组。

假如,上面三个分类,第一个分类里面有2个实例方法,第二个分类里面有1个实例方法,第三个分类里面有3个实例方法,那么,该实例方法在分类存储形式如下图中的二维数组类似。
在这里插入图片描述以上是将分类合并成一个大数组,其顺序是按照编译顺序(最先编译的在数组最前面)

然后,再通过一个while循环,从数组的最后一个元素(也是数组)开始,一个一个添加到分类的instance_methods里面。

在这里插入图片描述
然后,将分类的instance_methods这个二维数组,拼接到rw的methods上,也就是拼接到原类的methods方法列表中。
并且从attachLists方法里面可以看出,是将分类实例方法列表插入到宿主类的方法列表的最前面。

在这里插入图片描述
这样,最后编译的分类里面,方法最先被调用。

属性、协议的添加方法与上面实例方法添加方法类似。

  1. 分类添加的方法可以“覆盖”原类方法(当然不是真的覆盖)
  2. 两个分类,里面有一个同名方法,最后编译的分类里面的方法被调用。
  3. 名字相同的分类会引起编译报错

关联对象

问:能否为分类添加成员变量?

不能

问:能否为分类添加属性?

id objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key)

void objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key, id _Nullable value, objc_AssociationPolicy policy)

void objc_removeAssociatedObjects(id _Nonnull object)
问:为分类添加的属性被添加到了哪里?

没有被添加到原宿主类中,而是被添加到。。。
在这里插入图片描述

在这里插入图片描述


属性、实例变量、成员变量的区别?

属性,property,是指的右@property建立的
例如:@property (copy, nonatomic) NSString *postId;
@property负责三个事情:

  1. set,get方法的声明
  2. set,get方法的实现
  3. 生成_postId的实例变量

成员变量指的是

@interface
{
	int age;
	NSObject *obj1;
}
@end

大括号中间的内容

实例变量ivar(instance variables),指的是{}中,是class类对象的一类,也就是有指针的,非基本数据类型。例如NSObject *obj1;

也就是说,成员变量 = 实例变量 + 基本数据类型变量

需要注意⚠️的是:如果既有成员变量,又有属性,则先写成员变量,再写属性

参考:OC中属性和成员变量(一)概念篇

也有资料说,成员变量 = 实例变量
属性property
实例变量ivar
成员变量的英文单词是?


扩展(Extension)

问:一般用扩展做什么?
  1. 声明私有属性
  2. 声明私有方法
  3. 声明私有成员变量
扩展的特点:
  1. 编译时决议
  2. 只以声明的形式存在,多数情况下存在于宿主类的.m文件中
  3. 不能为系统类添加扩展

代理(Delegate)

代理分三个模块:协议、委托、代理
协议,就是@property
委托,就是协议调用协议方法的地方,指定代理
代理,就是实现协议里面具体方法的地方

是一种软件设计模式
传递方式一对一
代理也可以添加属性

在这里插入图片描述

MJ说过,delegate不是代理设计模式,NSProxy是代理设计模式
于海说delegate是代理设计模式
那么,delegate是不是代理设计模式呢?

问:如何实现代理的一对多?

即,同一个代理,被多个对象监听

方法一:

将代理设置成数组、集合,而不是单单的一个weak属性值
类似:

@property (nonatomic, strong)NSMutableArray <id<TestSubViewDelegate>>* __nullable delegates;

需要注意的是,在消耗的时候,要手动销毁delegates

- (void)destory {
    [self.delegates removeAllObjects];
    self.delegates = nil;
}

iOS 实现一对多代理方案

不过,最好使用NSMapTable NSHashTable NSPointerArray这些数据类型
因为,这几个数据类型,里面存放的元素都是weak指针引用的,销毁的时候不需要手动销毁

iOS开发-NSMapTable NSHashTable NSPointerArray的使用

方法二:

利用消息转发机制
iOS:利用消息转发机制实现多播委托


通知(NSNotification)

通知是使用观察者模式来实现的用于跨层传递消息的机制
传递方式:一对多

在这里插入图片描述

问:如何实现通知机制?

在这里插入图片描述

在通知中心,有一个Notification_Map字典,里面存储着以通知名称为key,观察者组成的数组作为value


KVO

KVO是Key-value observing的缩写
KVO是Objective-C对观察者设计模式的一种实现
Apple使用了isa混写(isa-swizzling)来实现KVO

在这里插入图片描述
isa混写技术:
指的就是将isa指针动态重新指向新的类

setter方法重写的方法:
在这里插入图片描述
在这里插入图片描述

问:通过kvc设置value,kvo能否生效?

可以
KVC改变属性值,会进入属性值的setter方法,从而触发KVO

问:通过成员变量直接赋值value,kvo能否生效?

不可以
但,可以通过手动修改setter方法,触发KVO

[self willChangeValueForKey:@"name"];
[super setName:name];
[self didChangeValueForKey:@"name"];

使用setter方法改变值,KVO会生效
使用setValue:forKey:改变值,KVO会生效
成员变量直接修改需手动添加某些代码,KVO才会生效


KVC

KVC是Key-value coding的缩写。
里面有两个重要的方法:

- (id)valueForKey:(NSString *)key;
- (void)setValue:(nullable id)value forKey:(NSString *)key;
问:KVC是否有违面向对象思想

KVC只要知道某个对象的私有变量名称key,可以在外部将其value进行修改,也就是有违面向对象思想的。

valueForKey:的系统实现流程:

在这里插入图片描述
valueForKey:说白了就是通过一个key找到对应的value
找value可以是通过getter方法找,也可以通过直接找对应的实例变量
也就是,一个是方法,一个是实例变量,两种方法
图中
Accessor Method is exist?
就是通过getter方法找,有的话直接调用

Instance Var is exist?
就是有没有对应的实例变量,有的话调用

问:Accessor Method如何找方法呢?

通过getKey、key(正好是getter方法)、isKey三个方法,如果有则YES

这三个都是方法

问:+(BOOL)accessInstanceVariablesDirectly的作用?

系统给我们提供了一个方法:
+(BOOL)accessInstanceVariablesDirectly,默认是返回YES

在返回YES的基础上,才会调用Instance Var is exist,判断是否存在相应的实例变量或者相似的实例变量

如果我们重写该方法,使其返回NO,那么直接报没有找到对应的值处理;不再进行实例变量的判断。

问:Instance Var is exist?判断规则是什么?

通过查找是否存在:_key、_isKey、key、isKey四个实例变量,如果有则YES

这四个都是实例变量

问:如果valueForKey:没有找到对应的value,会怎样?

如果valueForKey:没有找到对应的value,会调用valueForUndefinedKey:
从而造成崩溃

YZPerson *person1 = [[YZPerson alloc] init];
NSLog(@"person1.age = %@", [person1 valueForKey:@"age"]);

在这里插入图片描述

setValue:forKey:的系统实现流程:

在这里插入图片描述里面的具体方法与流程与valueForKey:类似,不再进行具体分析。


属性关键字

问:属性关键字可以分为哪几类?

读写权限:readonly、readwrite(默认)
原子性:atomic(默认)、nonatomic
引用计数
setter\getter

atomic可以保证获取和赋值是线程安全的
但是,并不能保证在使用过程中是线程安全的

引用计数:
retain\strong
assign\unsafe_unretain
weak
copy

问:assign和weak的区别有哪些?

assign
可以修饰基本数据类型,如int、BOOL等
可以修饰对象类型,但不改变其引用计数器
当使用assign修饰对象,在对象释放的时候,会产生悬垂指针

weak
不可以修饰基本数据类型
修饰对象类型,但不改变其引用计数器
当使用weak修饰对象,在对象释放的时候,会自动置为nil

问:关于空指针、野指针、悬垂指针的区别

空指针:指针指向的地址为空的指针
野指针:指针创建时未初始化。指针变量刚被创建时不会自动成为NULL指针,它会随机指向一个内存地址。
垂悬指针:指针所指向的对象已经被释放或者回收了,但是指向该对象的指针没有作任何的修改,仍旧指向已经回收的内存地址。

更多学习关于悬垂指针
按照上面的说法,我们之前很多说的野指针错误,严格说来并不是野指针错误,而是悬垂指针

问:浅拷贝、深拷贝

在这里插入图片描述
浅拷贝并没有新建一块内存
浅拷贝对原来的对象引用计数+1

深拷贝会新建一块内存,与原来的内存里面内容一样
深拷贝对原来的对象引用计数不变

在这里插入图片描述
如果赋值过来是NSMutableArray,copy后其实是NSArray,如果对其进行添加或者删除操作,会造成程序崩溃

在这里插入图片描述

问:为何会做 _obj != obj 的判断?不做可以吗?

如果不做,则先进行[_obj release];也就是原来的对象可能已经被释放掉了。再进行[obj retain]操作,会造成程序异常。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值