OC语言特性

分类

用分类做了哪些事情

  • 声明私有方法
  • 分解体积庞大的类文件
  • 把framework的私有方法公开化

特点

  • 运行时决议:编好分类文件,并没有分类中添加的内容附加到相应的宿主类,而是在运行时通过RunTime真实的添加到相应的宿主类
  • 可以为系统类添加分类

分类中都可以添加哪些内容?

  • 实例方法
  • 类方法
  • 协议
  • 属性(分类中定义属性,实际上只是生成了对应的getter、setter方法)
    Runtime的源码分类的结构体:struct _category_t

分类的调用栈(Category)

  • _objc_int -> map_2_images -> map_images_nolock -> _read_images -> remethodizeClass
    • images是镜像
    • _read_images 加载一些可执行文件到内存当中进行处理
    • remethodizeClass
分类_objc_init调用栈.png

如果两个分类添加同一个函数名称的函数,哪一个函数最终生效?

取决于编译顺序,最后生成分类的函数会生效,前面的将被覆盖掉。

总结:

  • 分类添加的方法可以”覆盖”原有方法
    ① 实际上是runtime在查找这个类的方法时,会查找分类一个宿主类的所有方法所在数组
    ② 而这个数组的分类方法是在宿主类前面的,所以说分类方法的调用有优先宿主类
    ③ 而并不是覆盖宿主类的方法。

  • 同名分类方法谁能生效取决于编译顺序
    最后被编译的分类,会优先生效

  • 名字相同的分类会引起编译报错
    生成具体分类,会把添加的分类名称,以下划线的形式拼接到宿主类中,这样在添加过程中,如果名称一样会报错。

能否给分类添加”成员变量”?

不能在分类的声明和定义实现的时候直接为分类添加成员变量,但是可以通过关联对象的技术来为分类添加”成员变量”,来达到分类可以添加”成员变量的效果”。
关联对象的3个API:

objc_setAssociatedObject(<#id  _Nonnull object#>, <#const void * _Nonnull key#>, <#id  _Nullable value#>, <#objc_AssociationPolicy policy#>)
objc_getAssociatedObject(<#id  _Nonnull object#>, <#const void * _Nonnull key#>)
objc_removeAssociatedObjects(<#id  _Nonnull object#>)

① 设定一个value值,通过key值建立一个映射关系,然后通过policy策略,关联到object上
② 根据指定的key到object中获取关联的值
③ 根据对象,移除所有相关它的对象

我们用关联对象技术来实现为分类添加”成员变量”,这个成员变量会被添加到哪里?

① 我们为分类所添加的成员变量肯定不是在宿主类中的。
② 关联对象由AssociationsManager管理并在AssociationHashMap存储。
③ 我们创建的每一个对象的关联对象实际上都存储在了AssociationHashMap这样的容器中。
④ 所有对象的关联内容都在同一个全局容器中
⑤ 不同对象的关联对象的值都会放到全局容器中
⑥ 假如想消除一个关联对象时,可以传入value为nil操作来实现

关联对象

关联对象本质

关联对象由AssociationsManager管理并在AssociationHashMap存储。
我们创建的每一个对象的关联对象实际上都存储在了AssociationHashMap这样的容器中。
所有对象的关联内容都在同一个全局容器中

利用关联对象添加成员变量

1. - (void)setName:(NSString *)name { objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
2. - (NSString *)name
{
    // 隐式参数
    // _cmd == @selector(name)
    return objc_getAssociatedObject(self, _cmd);
}
(void)removeAssociatedObjects{
    // 移除所有关联对象
    objc_removeAssociatedObjects(self);
}
关联对象的本质.png
关联对象的Json形式.png

扩展

类扩展一般都在宿主类的.m文件中。

一般用扩展做什么?

  • 声明私有属性
  • 声明私有方法(没有多大作用,方便阅读)
  • 声明私有成员变量
    声明私有属性、声明私有成员变量是有区别的

特点

  1. 分类是运行时决议、扩展是编译时决议
  2. 分类可以有声明和实现,而扩展只有声明的形式,实现是直接在宿主类的
  3. 可以为系统类添加分类,但是不能为系统类添加扩展

代理

  • 准确的说是一种软件设计模式
  • iOS 当中以@protocol形式体现
  • 传递方式一对一

代理的工作流程

三个角色:

  • 委托方
    ① 要求代理方需要实现的接口,也就是协议
    ② 调用代理方遵从协议的方法
    ③ 可能返回一个处理结果
  • 协议
    ① 可以定义方法(必须实现和可选类型)
    ② 可以定义属性
  • 代理方
    按照协议实现方法


    代理的实现流程.png

代理方和委托方以什么关系存在?

一般声明为weak来规避循环引用


代理循环引用.png

通知实现机制

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

如何实现通知的机制?假如说自己实现这种机制如何实现?

  • 实现一个实现一个全局Map
  • key为notificationName
  • value为一个Serrvers(因为一对多), servers中的每个元素应该能明确哪一个对象,哪一个方法

通知传递消息一对多的流程

通知一对多的流程.png

通知和代理的区别

  • 代理使用代理模式,而通知是由观察者模式实现的
  • 代理是一对一,而通知是一对多

KVO

什么是KVO?

  • KVO是key-value observing的缩写
  • KVO是Objective-C对观察者设计模式的又一实现
  • KVO是Objective-C对观察者设计模式的又一实现

KVO的实现机制

KVOisa混写.png
  • 系统调用addObserver添加KVO的时候
  • 会想被观察者对象的isa指向其名为NSKVONotifying_<ObjectClass>子类
  • NSKVONotifying_<ObjectClass>中重写了setter方法
  • 重写的setter方法负责通知观察者

重写setter方法

  • -(void)willChangeValueForKey:(NSString *)key;
  • -(void)didChangeValueForKey:(NSString *)key;

通过KVC设置value能够生效?

  • 通过KVC设置value,KVO的通知会生效
  • KVC设置value会调用setter方法

通过成员变量直接赋值value能否生效

  • 不会触发系统的KVO的
  • 不会调用setter方法
  • 可以在变量赋值的代码块前后添加下面两个API来触发KVO
    ① -(void)willChangeValueForKey:(NSString *)key;
    ② -(void)didChangeValueForKey:(NSString *)key;

观测方法的options

  • NSKeyValueObservingOptionOld 把更改之前的值提供给处理方法
  • NSKeyValueObservingOptionNew 把更改之后的值提供给处理方法
  • NSKeyValueObservingOptionInitial 把初始化的值提供给处理方法,一旦注册,立马就会调用一次。通常它会带有新值,而不会带有旧值。
  • NSKeyValueObservingOptionPrior 分2次调用。在值改变之前和值改变之后。

总结

  • 使用setter方法改变值KVO才会生效
  • 使用setValue:forkey:改变KVO才会生效
  • 成员变量直接修改需手动添加KVO才会生效

KVC

什么是KVC?

  • KVC是Key-value coding的缩写
  • KVC的两个API
    ① -(void)setValue:(nullable id)value forKey:(NSString *)key
    ② -(nullable id)valueForKey:(NSString *)key

使用KVC是否会违背于面向对象的编程思想

KVC的key是没有限制的,如果我们知道这个对象内部的私有变量名称的话,我们可以在外界修改、访问它,从这个角度来考虑的话是会破坏面向对象的思想的

valueForKey的实现流程

KVCGetter方法的流程.png

Accessor Method方法的定义

  • instance var
  • _key
  • _isKey
  • key
  • isKey

setValue:forKey的实现流程

KVCsetter方法的流程.png

Accessor Method方法的定义

  • set:
  • _set:
  • setIs:

instance var

  • _key
  • _isKey
  • key
  • isKey

属性关键字

属性关键字分为哪几类

  • 读写权限
    ① readonly
    ② readwrite iOS默认
  • 原子性
    ① atomic iOS默认
    -修饰的是一个数组,赋值和获取是线程安全的,
    -修改这个数组中的数据不是线程安全的 (例如在atomic修饰的NSMutableArray数组中,进行增删元素操作)
    ② nonatomic
  • 引用技计数
    ① retain mrc
    ② strong arc
    ③ assign 既可以修饰对象,也可以修饰值类型数据
    ④ unsafe_unretained MRC使用的比较多,ARC基本上不怎么用
    ⑤ weak
    ⑥ copy

assign的特点

  • 修饰基本数据类型,如果int、bool等
  • 修饰对象类型时,不改变引用计数
  • 在对象释放以后,assign修饰的对象指针还是会指向这个对象地址,当使用这个对象的时候,会出现野指针。

weak 特点

  • 不改变对象引用计数
  • 所指对象在释放之后会自动置为nil

assign和weak的区别有哪些?

1、weak只可以修饰对象、而assign可以修饰基本数据类型和对象
2、assign修饰的对象的时候,当对象被释放掉以后,assign指针还是会指向对象的地址,而weak修饰的对象,在对象释放后,weak的指针会被置nil

copy

源对象类型 拷贝方式 目标对象类型 拷贝类型
mutable对象 copy 不可变 深拷贝
mutable对象 mutableCopy 可变 深拷贝
immutable对象 copy 不可变 浅拷贝
immutable对象 mutableCopy 可变 深拷贝

① 可变对象的copy和mutablecopy都是深拷贝
② 不可变对象的copy是浅拷贝,mutablecopy是深拷贝
③ copy方法返回的都是不可变对象

浅拷贝

  • 浅拷贝会增加被拷贝对象的引用计数
  • 并没有发生新的内存分配
    浅拷贝就是对内存地址的复制,让目标对象指针和源对象指向同一片内存空间。


    浅拷贝.png

深拷贝

  • 不会增加被拷贝对象的引用计数
  • 产生了内存分配
    深拷贝让目标对象指针和源对象指针指向两片内容相同的内存空间


    深拷贝.png

深拷贝VS浅拷贝?

  • 是否开辟新的内存空间
  • 是否影响引用计数

@property(copy) NSMutableArray *array, 会产生什么样的问题

最终结果是一个不可变的对象,那么你在修改这个数组的时候会崩溃

  • 如果赋值过来的是NSMutableArray,copy之后虽然是深拷贝,但是返回的是NSArray
  • 如果赋值过来的是NSArray,copy之后是NSArray
  • 当修改array这个数组的时候就会出现崩溃问题

MST

1、MRC下如何重写retain修饰变量的setter方法?

@property(nonatomic,retain)id obj;

- (void)setObj:(id)obj{
    //对象不等判断
    //如果传递过来的obj对象是原来的obj对象
    //实际上也是在传递过来的obj对象release的操作,有可能会直接释放掉了传递过来的对象,
    //这时候如果还去访问被释放的对象就会崩溃
    if(_obj != obj){
        [_obj release];
        _obj = [obj retain]
    }
}

2、 分类的实现原理

  • 是由运行时来决议的
  • 不同分类,同名函数名称,谁最后生效,取决于,谁最后参与编译的同名分类方法会生效
  • 如果分类方法的名字和宿主的方法名称一样,那么分类的方法会覆盖宿主类的方法,这里的覆盖是runtime优先处理数组靠前的方法,如果找到就调用。宿主类的同名方法还是存在的

3、KVO实现原理是怎么样的?

  • KVO是系统基于观察模式的一个实现
  • KVO通过isa的混写技术来动态运行时去为某一个类添加子类,并重写setter方法,同时把原有类的isa指针指向这个子类

4、能否为分类添加实例变量?

  • 实例变量(成员变量),即它只能添加属性,setter/getter的声明,但是没有实现,不能使用其父类的一些成员方法
  • 属性是系统自动为我们生成的一个添加下划线的实例变量,系统帮我们实现了setter/getter方法
  • 不使用属性创建的实例变量,需要最后在@implementation中用synthesize生成set方法
    @synthesize name;(创建实例变量的setter、getter方法),@dynamic告诉编译器,不自动生成getter/setter方法,
  • 为分类添加实例变量
    ① 使用关联对象技术为分类添加“实例变量”(只是达到一个成员变量的效果,实际上并不是真正的添加了实例变量)
    ② 代码实例
    .h文件
@interface CustomView (dd)

- (NSString *)name;
- (void)setName:(NSString *)name;

@end

.m

@implementation CustomView (dd)

/*
 * 使用关联对象模拟实例变量
 * 使用objc_getAssociatedObject、objc_setAssociatedObject模拟『属性』的存取方法
 */

- (NSString *)name{
    
    return objc_getAssociatedObject(self, _cmd);
    
}

- (void)setName:(NSString *)name{
    
    objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    
}
@end
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容