NSObject没有父类,他的super class 指向nil.isa指针指向自己.
OC中每个类中都包含一个isa变量,显然这里的isa是指向另一个类的指针,说白了就是表明这个类是哪个类的实例,以便找到代码中调用的本类或父类的类方法。对于NSObject及其子类,指向的就是它的元类,正如实例中也有个isa指针指向其所属的类一样。而对于元类,每个元类的isa都指向根元类。那么根元类的isa指向哪里?——它自己。这样就构成一个封闭的循环,实现了无懈可击的OC类系统。这种关系在下面的图中有清晰的体现。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wbA7A0oc-1649150121073)(http://www.kaotop.com/file/tupian/20220519/1361289384_8487.PNG)]
13. 有几种 Block。ARC 下有几种,MRC 下有几种。简单分为三种来用:
像函数一样定义和使用,,不同于函数的是可以定义在方法内也可以定义在方法外定义成property的属性来使用用作修饰词接下来一样样的来展示; 1. 像函数一样定义和使用,不同于函数的是可以定义在方法内也可以定义在方法外 1)在方法中使用
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSString *(^thisBlock) (NSString *thisName) = ^(NSString *name){
return [NSString stringWithFormat:@"%@:%@",@"name",name];
};
NSLog(@"%@",thisBlock(@"xiaoming"));
}
2)在方法外使用,就像定义一个方法一样
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
BlockVoid(24);
}
void (^BlockVoid) (int age) = ^(int xiaomingAge){
NSLog(@"xiaomingAge:%d",xiaomingAge);
};
2. 定义成property的属性来使用总结: 上面俩block也是block的一种写法不过这么用的貌似不多见,大多时候这么写反而显得麻烦了(从这里可以看出,block可以定义在方法内部也可以定义在方法外部,在外部的时候看着是不是更像函数了)
/**
常规无返回值有参数block,比较常用
*/
@property(nonatomic,copy)void (^myBlock) (NSString *name,int age);
/**
常规有返回值有参数block,使用起来其实和上面那个差别不大
*/
@property(nonatomic,copy)NSString *(^haveReturnBlock) (NSString *name);
//用法
- (void)test2
{
_myBlock(@"CodeLiu",24);
}
- (NSString *)test3
{
return _haveReturnBlock(@"小花");
}
除了这个用法,还有一种很好玩的用法,看着比较生动,其实是一样的:
#import
/**
定义一个block
@param returnContent 返回值
*/
typedef void(^RetureContentBlock)(id returnContent);
@interface LHBlockForUse : NSObject
/**
用定义的block来声明一个变量
*/
@property(nonatomic,copy)RetureContentBlock returnBlock;
@end
//用法
- (void)test1
{
_returnBlock(@"You can put every type in here");
}
3.用作修饰词
在Block内是不能修改Block外的变量的,如果要修改就需要用__block进行修饰
__block int lastAge = 24;
void (^lastAgeBlock) (int age) = ^(int addAge){
lastAge = lastAge + addAge;
NSLog(@"xiaomingLastAge:%d",lastAge);
};
lastAgeBlock(1);
14. __block 如何实现。
我们都知道:Block不允许修改外部变量的值,这里所说的外部变量的值,指的是栈中指针的内存地址。__block 所起到的作用就是只要观察到该变量被 block 所持有,就将“外部变量”在栈中的内存地址放到了堆中。进而在block内部也可以修改外部变量的值。
__block修饰符是如何做到修改变量值的
如果把val变量加上__block修饰符,编译器会怎么做呢?
//int val = 0; 原代码
__block int val = 0;//修改后的代码
编译后的代码:
struct __Block_byref_val_0 {
void *__isa;
__Block_byref_val_0 *forwarding;
int __flags;
int __size;
int val;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0 *Desc;
__Block_byref_val_0 *val;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc,__Block_byref_val_0 *_val, int flags=0) : val(_val->__forwrding){
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
struct void __main_block_func_0(struct __main_block_impl_0 *__cself){
__Block_byref_val_0 *val = __cself->val;
printf("val = %d",val->__forwarding->val);
}
但是关键在于__main_block_impl_0结构体中的这一行:
__Block_byref_val_0 *val;
由于__main_block_impl_0结构体中现在保存了一个指针变量,所以任何对这个指针的 *** 作,是可以影响到原来的变量的。
进一步,我们考虑截获的自动变量是Objective-C的对象的情况。在开启ARC的情况下,将会强引用这个对象一次。这也保证了原对象不被销毁,但与此同时,也会导致循环引用问题。
需要注意的是,在未开启ARC的情况下,如果变量附有__block修饰符,将不会被retain,因此反而可以避免循环引用的问题。
总结
在本文的开头,提出两个简单的问题,如果你不能从根本上弄懂这两个问题,那么希望你阅读完本文后能有所收获。
为什么block中不能修改普通变量的值?__block的作用就是让变量的值在block中可以修改么?如果有的读者认为,问题太简单了,
而且你的答案是:
(因为编译器会有警告,各种教程也都说了不能修改。
应该是的吧。)
回到开篇的两个问题,答案应该很明显了。
由于无法直接获得原变量,技术上无法实现修改,所以编译器直接禁止了。
都可以用来让变量在block中可以修改,但是在非ARC模式下,__block修饰符会避免循环引用。注意:block的循环引用并非__block修饰符引起,而是由其本身的特性引起的。
block是什么?很多教程、资料上都称Block是“带有自动变量值的匿名函数”。这样的解释显然是正确的,但也是不利于初学者理解的。
block参考链接
很少有人知道weak表其实是一个hash(哈希)表,Key是所指对象的地址,Value是weak指针的地址数组。更多人的人只是知道weak是弱引用,所引用对象的计数器不会加一,并在引用对象被释放的时候自动被设置为nil。通常用于解决循环引用问题。但现在单知道这些已经不足以应对面试了,好多公司会问weak的原理。weak的原理是什么呢?下面就分析一下weak的工作原理(只是自己对这个问题好奇,学习过程中的笔记,希望对读者也有所帮助)。
weak 实现原理的概括在 runtime 阶段,对于 weak 变量而言,系统使用 hash 表进行管理,将weak 变量所指向的地址作为 hash 表的 key,当某个对象的引用计数为零的时候,系统根据对象的内存地址找到所有指向该对象的 weak 变量,并将这些 weak 变量置为 nil
Runtime维护了一个weak表,用于存储指向某个对象的所有weak指针。weak表其实是一个hash(哈希)表,Key是所指对象的地址,Value是weak指针的地址(这个地址的值是所指对象的地址)数组。
weak 的实现原理可以概括一下三步:
初始化时:runtime会调用objc_initWeak函数,初始化一个新的weak指针指向对象的地址。
添加引用时:objc_initWeak函数会调用 objc_storeWeak() 函数, objc_storeWeak() 的作用是更新指针指向,创建对应的弱引用表。
释放时,调用clearDeallocating函数。clearDeallocating函数首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。
当weak引用指向的对象被释放时,又是如何去处理weak指针的呢?当释放对象时,其基本流程如下:调用objc_release
因为对象的引用计数为0,所以执行dealloc
在dealloc中,调用了_objc_rootDealloc函数
在_objc_rootDealloc中,调用了object_dispose函数
调用objc_destructInstance
最后调用objc_clear_deallocating
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)