Category类
struct category_t {
const char *name;
classref_t cls;
struct method_list_t *instanceMethods;
struct method_list_t *classMethods;
struct protocol_list_t *protocols;
struct property_list_t *instanceProperties;
// Fields below this point are not always present on disk.
struct property_list_t *_classProperties;
method_list_t *methodsForMeta(bool isMeta) {
if (isMeta) return classMethods;
else return instanceMethods;
}
property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
};
对象方法、属性、成员变量、协议信息,存放在class对象中
类方法,存放在meta-class对象中
成员变量的具体值,存放在instance对象
instance对象的isa指针指向class对象
class对象的isa指针指向meta-class对象
meta-class对象的isa指针指向基类的meta-class对象
如何扩大button的点击面积
runtime 消息转发机制(最详细流程)
// 动态解析
+ (BOOL)resolveInstanceMethod:(SEL)sel {
// NSLog(@"动态方法解析");
// NSString *methodName = NSStringFromSelector(sel);
//
// if ([methodName isEqualToString:@"callMyName:"]) {
// return class_addMethod(self, sel, (IMP)callMyName, "v@:@");
// }
//
return NO;
}
- (id)forwardingTargetForSelector:(SEL)aSelector {
// NSString *methodName = NSStringFromSelector(aSelector);
// if ([methodName isEqualToString:@"sendMessage:"]) {
// return [Animal new];
// }
return [super forwardingTargetForSelector:aSelector];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
NSString *methodName = NSStringFromSelector(aSelector);
if ([methodName isEqualToString:@"sendMessage:"]) {
return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
}
return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation{
SEL sel = anInvocation.selector;
Animal *child = [Animal new];
if ([child respondsToSelector:sel]) {
[anInvocation invokeWithTarget:child];
}else {
[super forwardInvocation:anInvocation];
}
}
- (void)doesNotRecognizeSelector:(SEL)aSelector {
NSLog(@"最终并未识别");
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ylVYpA0k-1616467169065)(/Users/ibrotherliyanbing/Library/Application Support/typora-user-images/image-20210323092843103.png)]
Swift的语言优势
https://zhuanlan.zhihu.com/p/21755854
Swift 是一个多范式(Multi-paradigm)的编程语言。它基本涵盖了函数式编程(Functional Programming)语言和面向对象编程语言的所有特性。可以进行函数式编程,或者 Protocol-Oriented Programming 等,而不局限于某种特定编程的范式。
kvo原理图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VqgTkm3e-1616467169068)(/Users/ibrotherliyanbing/Library/Application Support/typora-user-images/image-20210321103816100.png)]
多线程问题
死锁发生条件:当前串行队列添加同步任务
条件:当前、同步、串行 不满足不会死锁
下文中的queue.sync就属于1线程中,然后在开同步,这么会导致sync死锁,修改方式是修改为并行队列
func gcdTest() {
print("=================")
// self.perform(#selector(self.pri), with: nil, afterDelay: 1.0)
let queue = DispatchQueue(label: "serial")
queue.async {
print(Thread.current)
print(1)
// self.perform(#selector(self.pri), with: nil, afterDelay: 1.0)
// self.perform(#selector(self.pri))
// RunLoop.current.run()
queue.sync {
print(2)
}
print(3)
}
print(4)
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8aGLv1ev-1616467169076)(/Users/ibrotherliyanbing/Library/Application Support/typora-user-images/image-20210321184908724.png)]
如果在主线程中运用主队列同步,也就是把任务放到了主线程的队列中。
而同步对于任务是立刻执行的,那么当把第一个任务放进主队列时,它就会立马执行。
可是主线程现在正在处理 syncMain 方法,任务需要等 syncMain 执行完才能执行。
syncMain 执行到第一个任务的时候,又要等第一个任务执行完才能往下执行第二个和第三个任务。
这样 syncMain 方法和第一个任务就开始了互相等待,形成了死锁。
iOS存储
1、Documents 目录:您应该将所有的应用程序数据文件写入到这个目录下。这个目录用于存储用户数据。该路径可通过配置实现iTunes共享文件。可被iTunes备份。
2、AppName.app 目录:这是应用程序的程序包目录,包含应用程序的本身。由于应用程序必须经过签名,所以您在运行时不能对这个目录中的内容进行修改,否则可能会使应用程序无法启动。
3、Library 目录:这个目录下有两个子目录:
Preferences 目录:包含应用程序的偏好设置文件。您不应该直接创建偏好设置文件,而是应该使用NSUserDefaults类来取得和设置应用程序的偏好.
Caches 目录:用于存放应用程序专用的支持文件,保存应用程序再次启动过程中需要的信息。
可创建子文件夹。可以用来放置您希望被备份但不希望被用户看到的数据。该路径下的文件夹,除Caches以外,都会被iTunes备份。
4、tmp 目录:这个目录用于存放临时文件,保存应用程序再次启动过程中不需要的信息。该路径下的文件不会被iTunes备份。
KVO的实现原理
在KVO_MyObject类中,重写了一些方法,举例如
setter
方法:在重写的setter
方法中调用了willChangeValueForKey:
方法和didChangeValueForKey:
方法,在这两个方法中则会调用observeValueForKeyPath:ofObject:change:context
方法,这个方法就是接受通知的回调方法。class
方法:重写的class
方法返回的是MyObject类对象而不是KVO_MyObject类对象,其目的是欺骗使用者。meta-class是类对象的类,每个类都有自己单独的meta-class。所有的类对象并不会属于同一个meta-class。
meta-class要保证类对象具有继承体系中基类的所有实例和类方法,以及继承体系中的所有中间类方法。对于所有NSObject继承体系下的类,NSObject的实例方法和协议方法对他们和他们meta-class的对象都要有效。
所有的meta-class使用基类的meta-class作为自己的基类,对于顶层基类的meta-class也是一样,只是它指向自己而已。
为了可以调用类方法,这个类的isa指针必须指向一个包含这些类方法的类结构体。
这样就引出了meta-class的概念:meta-class是一个类对象的类。
简单解释下:
当你向一个对象发送消息时,runtime会在这个对象所属的那个类的方法列表中查找。
当你向一个类发送消息时,runtime会在这个类的meta-class的方法列表中查找。
meta-class之所以重要,是因为它存储着一个类的所有类方法。每个类都会有一个单独的meta-class,因为每个类的类方法基本不可能完全相同。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OEzHzWVd-1616467169084)(/Users/ibrotherliyanbing/Library/Application Support/typora-user-images/image-20210309140542885.png)]
当一个方法找不到的时候,Runtime 提供了 消息动态解析、消息接受者重定向、消息重定向 等三步处理消息,具体流程如下图所示:
Objective-C 语言
中,对象方法调用都是类似 [receiver selector];
的形式,其本质就是让对象在运行时发送消息的过程。
我们来看看方法调用 [receiver selector];
在『编译阶段』和『运行阶段』分别做了什么?
编译阶段:
[receiver selector];
方法被编译器转换为:
objc_msgSend(receiver,selector)
(不带参数)objc_msgSend(recevier,selector,org1,org2,…)
(带参数)运行时阶段:消息接受者 recevier寻找对应的 selector。
recevier
的 isa 指针
找到 recevier
的 Class(类)
;Class(类)
的 cache(方法缓存)
的散列表中寻找对应的 IMP(方法实现)
;cache(方法缓存)
中没有找到对应的 IMP(方法实现)
的话,就继续在 Class(类)
的 method list(方法列表)
中找对应的 selector
,如果找到,填充到 cache(方法缓存)
中,并返回 selector
;Class(类)
中没有找到这个 selector
,就继续在它的 superClass(父类)
中寻找;selector
,直接执行 recevier
对应 selector
方法实现的 IMP(方法实现)
。selector
,消息被转发或者临时向 recevier
添加这个 selector
对应的实现方法,否则就会发生崩溃。NSOperation介绍
常规解法。
思路:将整数n与1进行与运算,当整数n最低位是1时,则结果非零,否则结果为0。
然后将1左移一位,继续与n进行与运算,当次低位是1时,结果非零,否则结果为0。
循环以上操作,记录非零的次数即可。
代码如下:
`public static int times1(int n ){
int count = 0;
int flag = 1;
while(flag <= n){
if((n&flag) != 0)
count++;
flag = flag<<1;
}
return count;
}
`
优化的解法
思路:
1.对一个整数n,比如10,它的二进制是1010。
2.将10减一变为9,9的二进制是1001.
3.比较10和9的二进制数,对10减一操作就等于将10的二进制的最低位上的1以及后面的位取反,前面的数不变。
总结:把一个整数减去1,再和原整数做与运算,会把该整数最右边1一个1变成0。那么一个整数的二进制表示中有多少个1,就可以进行多少次这样的操作。从而可以减少比较的次数。
public static int times2(int n){ int count = 0; while(n!=0){ count++; n = n&(n-1); } return count; }
在使用Block时,我们可以使用weak关键字来避免外部变量被Block强引用而导致的循环引用,同时为了Block中的代码能够正常执行,许多开发者提出了Weak-Strong搭配使用的方式,类似如下:
{ __weak typeof(self) weakSelf = self; self.block = ^{ __strong typeof(weakSelf) strongSelf = weakSelf; [strongSelf test1]; [strongSelf test2]; }; }
以上代码相对于单独使用weak来说还是有好处的,在单独使用weak时,可以保证在执行 [weakSelf test1]或 [weakSelf test2]单条语句时,weakSelf所指的self不会被释放或self已经释放而直接向nil发送消息。
若使用Weak-Strong搭配的方式的话,可以保证在执行 [strongSelf test1]和 [strongSelf test2]时,是向同一对象发送消息。
为什么这么说呢?当开始执行Block语句时,若self还存在,那么strongSelf可以保证在整个Block代码块中不会被释放,即使Block中调用无数次strongSelf,strongSelf也不会因为多线程而在半途被释放;若开始执行Block时,self已经被释放,那么之后所有的消息都会被发送至nil。所以Weak-Strong搭配可以保证Block中语句被处理为一个事务。
所以说,Weak-Strong并不能保证Block中语句一定会被执行,它只能保证Block中语句作为一个事务被发送到同一对象处。只要理解了weak实现原理,我们就能明白何时单独使用weak也能完成代码功能,而何时必须使用Weak-Strong来保证代码事务能力。
__unsafe_unretained: 不会对对象进行retain,当对象销毁时,会依然指向之前的内存空间(野指针)
weak: 不会对对象进行retain,当对象销毁时,会自动指向nil
assign: 实质与__unsafe_unretained等同
unsafe_unretained也可以修饰代表简单数据类型的property,weak也不能修饰用来代表简单数据类型的property。
__unsafe_unretained 与 weak 比较,使用 weak 是有代价的,因为通过上面的原理可知,__weak需要检查对象是否已经消亡,而为了知道是否已经消亡,自然也需要一些信息去跟踪对象的使用情况。也正因此,__unsafe_unretained 比 __weak快,所以当明确知道对象的生命期时,选择__unsafe_unretained 会有一些性能提升,这种性能提升是很微小的。但当很清楚的情况下,__unsafe_unretained 也是安全的,自然能快一点是一点。而当情况不确定的时候,应该优先选用 __weak 。
unowned使用在Swift中,也会分 weak 和 unowned。unowned 的含义跟 __unsafe_unretained 差不多。假如很明确的知道对象的生命期,也可以选择 unowned。
作者:Colleny_Z
链接:https://www.jianshu.com/p/3c5e335341e0
assign可以修饰OC对象么?
可以,但不会增加该对象的引用计数,当没有强引用持有该对象时,该对象就会被释放,如果此时再向该对象发消息,会导致崩溃问题。
Runtime维护了一个weak表,用于存储指向某个对象的所有weak指针。weak表其实是一个hash(哈希)表,Key是所指对象的地址,Value是weak指针的地址(这个地址的值是所指对象指针的地址)数组。
作者:逍遥晨旭
链接:https://www.jianshu.com/p/13c4fb1cedea
1、初始化时:runtime会调用objc_initWeak函数,初始化一个新的weak指针指向对象的地址。
2、添加引用时:objc_initWeak函数会调用 objc_storeWeak() 函数, objc_storeWeak() 的作用是更新指针指向,创建对应的弱引用表。
3、释放时,调用clearDeallocating函数。clearDeallocating函数首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。
Clang之后的结果如下:
id __attribute__((objc_ownership(weak))) obj1 = obj;
1、初始化时:runtime会调用objc_initWeak函数,初始化一个新的weak指针指向对象的地址。
2、添加引用时:objc_initWeak函数会调用 objc_storeWeak() 函数, objc_storeWeak() 的作用是更新指针指向,创建对应的弱引用表。
3、释放时,调用clearDeallocating函数。clearDeallocating函数首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。
作者:Colleny_Z
链接:https://www.jianshu.com/p/3c5e335341e0
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。