Objective-C基础-OC语法

1、Objective-C的本质

Objective-C代码,底层实现其实都是C\C++代码。
Objective-C的对象、类主要是基于C\C++的结构体实现的。


iOS编译.jpg

将Objective-C代码转换为C\C++代码

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc OC源文件 -o 输出的CPP文件
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc ViewController.m  -o ViewController.cpp

如果需要链接其他框架,使用-framework参数。比如-framework UIKit

2、OC对象的本质

OC对象占用字节数

 NSObject *obj = [[NSObject alloc] init];
 
 // 获得NSObject实例对象的成员变量所占用的大小 >> 8个字节
 NSLog(@"%zd", class_getInstanceSize([NSObject class]));

 // 获得obj指针所指向内存的大小 >> 16个字节
 NSLog(@"%zd", malloc_size((__bridge const void *)obj));

NSObject底层实现

NSObject其实是用结构体实现,有个isa属性。

@interface NSObject {
    Class isa;
}

转化为结构体

struct NSObject_IMPL {
    Class isa; // 8个字节
};
//typedef struct objc_class *Class;//指针

获取分配字节的函数

  • 创建一个实例对象,至少需要多少内存
#import 
class_getInstanceSize([NSObject class]);

  • 创建一个实例对象,实际上分配了多少内存
#import 
malloc_size((__bridge const void *)obj);

继承的类占用字节数

@interface Student : NSObject
{
    Class isa;//8
    int _no;//4
    int _age;//4
    int _age1;//4
    int _age2;//4
    
}
@end

NSLog(@"%zd", class_getInstanceSize([Student class]));//24 成员变量占用24个字节
NSLog(@"%zd", malloc_size((__bridge const void *)stu));//32 系统分配了32个字节

2、OC对象的分类

  • instance对象(实例对象)
  • class对象(对象)
  • meta-class对象(元类对象)

2.1 instance

instance对象就是通过类alloc出来的对象,每次调用alloc都会产生新的instance对象。

NSObject *object1 = [[NSObject alloc] init];
NSObject *object2 = [[NSObject alloc] init];

  • object1、object2是NSObject的instance对象(实例对象)
  • 它们是不同的两个对象,分别占据着两块不同的内存
  • instance对象在内存中存储的信息包括isa指针、其他成员变量。

@interface Person : NSObject {
    @public
    int _age;
}
@end

@implementation Person
@end
Person *p1 = [[Person alloc] init];
person1->_age = 3;

Person *p2 = [[Person alloc] init];
person2->_age = 4;
Objective-C基础-OC语法_第1张图片
instance内存.jpg

2.2 class

NSObject *object1 = [[NSObject alloc] init];
NSObject *object2 = [[NSObject alloc] init];
Class objectClass1 = [object1 class];
Class objectClass2 = [object2 class];
Class objectClass3 = object_getClass(object1);
Class objectClass4 = object_getClass(object2);
Class objectClass5 = [NSObject class];
Class objectClass6 = [[NSObject class] class];
  • objectClass1 ~ objectClass6都是NSObject的class对象(类对象)
  • 它们是同一个对象。每个类在内存中有且只有一个class对象
  • class对象在内存中存储的信息主要包括
    • isa指针
    • superclass指针
    • 类的属性信息(@property)、类的对象方法信息(instance method)
    • 类的协议信息(protocol)、类的成员变量信息(ivar)
Objective-C基础-OC语法_第2张图片
class内存.jpg

2.3 meta-class

Class objectMetaClass = object_getClass([NSObject class]);
  • objectMetaClass是NSObject的meta-class对象(元类对象)
  • 每个类在内存中有且只有一个meta-class对象
  • meta-class对象和class对象的内存结构是一样的,但是用途不一样,在内存中存储的信息主要包括
    • isa指针
    • superclass指针
    • 类的类方法信息
Objective-C基础-OC语法_第3张图片
meta-class内存.jpg
  • 以下代码获取的objectClass是class对象,并不是meta-class对象
Class objectClass6 = [[NSObject class] class];
  • 查看Class是否为meta-class
#import 
BOOL result = class_isMetaClass([NSObject class]);

2.4 isa指针

Objective-C基础-OC语法_第4张图片
isa指针.jpg
  1. instanceisa指向class
    当调用对象方法时,通过instanceisa找到class,最后找到对象方法的实现进行调用
  2. classisa指向meta-class
    当调用类方法时,通过classisa找到meta-class,最后找到类方法的实现进行调用

2.5 class对象的superclass指针

Objective-C基础-OC语法_第5张图片
class对象的superclass指针.jpg
  1. 当Student的instance对象要调用Person的对象方法时,会先通过isa找到Student的class,然后通过superclass找到Person的class,最后找到对象方法的实现进行调用

2.6 meta-class对象的superclass指针

Objective-C基础-OC语法_第6张图片
meta-class对象的superclass指针.jpg
  1. 当Student的class要调用Person的类方法时,会先通过isa找到Student的meta-class,然后通过superclass找到Person的meta-class,最后找到类方法的实现进行调用

2.7 isa、superclass总结

Objective-C基础-OC语法_第7张图片
isa-superclass.png
  1. instance的isa指向class
  2. class的isa指向meta-class
  3. meta-class的isa指向基类的meta-class
  4. class的superclass指向父类的class, 如果没有父类,superclass指针为nil
  5. meta-class的superclass指向父类的meta-class, 基类的meta-class的superclass指向基类的class
  6. instance调用对象方法的轨迹: isa找到class,方法不存在,就通过superclass找父类
  7. class调用类方法的轨迹:isa找meta-class,方法不存在,就通过superclass找父类

注意第5点

@interface NSObject (Test)
+ (void)test;
@end

@implementation NSObject (Test)
- (void)test {
    NSLog(@"-[NSObject test] - %p", self);
}
@end

@interface Person : NSObject
+ (void)test;
@end

@implementation Person
@end
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSLog(@"[Person class] - %p", [Person class]);
        NSLog(@"[NSObject class] - %p", [NSObject class]);
        [Person test];
        // objc_msgSend([Person class], @selector(test))
        // isa -> superclass -> suerpclass -> superclass -> .... superclass == nil
        [NSObject test];
        //objc_msgSend([NSObject class], @selector(test))
    }
    return 0;
}
Objective-C基础-OC语法_第8张图片
meta-class输出.jpg

3、ISA_MASK

Objective-C基础-OC语法_第9张图片
ISA_MASK.jpg
  • 从64bit开始,isa需要进行一次位运算,才能计算出真实地址
# if __arm64__
#   define ISA_MASK        0x0000000ffffffff8ULL
# elif __x86_64__
#   define ISA_MASK        0x00007ffffffffff8ULL
# endif

4、objc_class结构

objc4源码: https://opensource.apple.com/tarballs/objc4/

struct objc_object {
private:
    isa_t isa;
};

struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache;             // 方法缓存
    class_data_bits_t bits;    // 用于获取类的信息
    
    class_rw_t *data() { 
        return bits.data();
    }
};

struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint32_t version;

    const class_ro_t *ro;

    method_array_t methods;         //方法列表
    property_array_t properties;    //属性列表
    protocol_array_t protocols;     //协议列表

    Class firstSubclass;
    Class nextSiblingClass;

    char *demangledName;
};

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;  //instance对象占用的内存空间
#ifdef __LP64__
    uint32_t reserved;
#endif

    const uint8_t * ivarLayout;
    
    const char * name;      //类名
    method_list_t * baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;//成员变量列表

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;
};


5、KVO

KVO的全称是Key-Value Observing,俗称“键值监听”,可以用于监听某个对象属性值的改变

Objective-C基础-OC语法_第10张图片
KVO监听.jpg

例如下类

@interface Person : NSObject
@property (assign, nonatomic) int age;
@end

@implementation Person

@end

5.1 未使用KVO监听的对象

Objective-C基础-OC语法_第11张图片
KVO正常类.jpg

5.2 使用KVO监听的对象

Objective-C基础-OC语法_第12张图片
KVO增加类.jpg
  • _NSSet*ValueAndNotify的内部实现
- (void)setAge:(int)age {
    [self willChangeValueForKey:@"age"];
    [super setAge:age];
    [self didChangeValueForKey:@"age"];
}

调用步骤

  1. 调用willChangeValueForKey:
  2. 调用原来的setter实现
  3. 调用didChangeValueForKey:
  4. didChangeValueForKey:内部会调用observer的observeValueForKeyPath:ofObject:change:context

5.3 Observer存在哪里

可通过GNUstep-Foundation源码查看源码观察实现原理

5.4 KVO代码例子

#import "ViewController.h"
#import 

@interface ViewController ()
@property (strong, nonatomic) Person *person1;
@property (strong, nonatomic) Person *person2;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.person1 = [[Person alloc] init];
    self.person1.age = 1;
    
    self.person2 = [[Person alloc] init];
    self.person2.age = 2;
    
    // 给person1对象添加KVO监听
    NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
    [self.person1 addObserver:self forKeyPath:@"age" options:options context:@"123"];
    
    NSLog(@"person1.class == %@", object_getClass(self.person1)) ;
    NSLog(@"person2.class == %@", object_getClass(self.person2)) ;

    [self printMethodNamesOfClass:object_getClass(self.person1)];
    [self printMethodNamesOfClass:object_getClass(self.person2)];
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    self.person1.age = 20;
    self.person2.age = 20;
}

- (void)dealloc {
    [self.person1 removeObserver:self forKeyPath:@"age"];
    [self.person1 removeObserver:self forKeyPath:@"height"];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    NSLog(@"监听到%@的%@属性值改变了 - %@ - %@", object, keyPath, change, context);
}

- (void)printMethodNamesOfClass:(Class)cls {
    unsigned int count;
    Method *methodList = class_copyMethodList(cls, &count);
    NSMutableString *methodNames = [NSMutableString string];
    for (int i = 0; i < count; i++) {
        Method method = methodList[i];
        NSString *methodName = NSStringFromSelector(method_getName(method));
        [methodNames appendString:methodName];
        [methodNames appendString:@", "];
    }
    free(methodList);
    NSLog(@"%@ %@", cls, methodNames);
}
@end
Objective-C基础-OC语法_第13张图片
KVO输出.jpg

6、KVC

KVC的全称是Key-Value Coding,俗称“键值编码”,可以通过一个key来访问某个属性

  • 常见api
- (void)setValue:(id)value forKeyPath:(NSString *)keyPath;
- (void)setValue:(id)value forKey:(NSString *)key;
- (id)valueForKeyPath:(NSString *)keyPath;
- (id)valueForKey:(NSString *)key; 

  • setValue:forKey:的原理
Objective-C基础-OC语法_第14张图片
setValueforKey原理.jpg

accessInstanceVariablesDirectly方法的默认返回值是YES

  • valueForKey:的原理


    Objective-C基础-OC语法_第15张图片
    ValueforKey原理.jpg

7、Category

7.1 Category底层结构

定义在objc-runtime-new.h中

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);
};

7.1 Category的加载处理过程

  1. 通过Runtime加载某个类的所有Category数据
  2. 把所有Category的方法、属性、协议数据,合并到一个大数组中, 后面参与编译的Category数据,会在数组的前面
  3. 将合并后的分类数据(方法、属性、协议),插入到类原来数据的前面

源码解读顺序

//objc-os.mm
_objc_init
map_images
map_images_nolock

//objc-runtime-new.mm
_read_images
remethodizeClass
attachCategories
attachLists
realloc、memmove、memcpy

7.2 +load方法

  • +load方法会在runtime加载分类时调用
  • 每个分类的+load,在程序运行过程中只调用一次.
  1. 先调用类的+load
    • 按照编译先后顺序调用(先编译,先调用)
    • 调用子类的+load之前会先调用父类的+load
  2. 再调用分类的+load
  • 按照编译先后顺序调用(先编译,先调用)

objc4源码解读过程

//objc-os.mm
_objc_init

load_images

prepare_load_methods
schedule_class_load
add_class_to_loadable_list
add_category_to_loadable_list

call_load_methods
call_class_loads
call_category_loads
(*load_method)(cls, SEL_load)

+load方法是根据方法地址直接调用,并不是经过objc_msgSend函数调用

7.3 +initialize方法

  • +initialize方法会在第一次接收到消息时调用

  • 先调用父类的+initialize,再调用子类的+initialize
    (先初始化父类,再初始化子类,每个类只会初始化1次)

  • +initialize是通过objc_msgSend进行调用的,所以有以下特点

  1. 如果子类没有实现+initialize,会调用父类的+initialize(所以父类的+initialize可能会被调用多次)
  2. 如果分类实现了+initialize,就覆盖类本身的+initialize调用

objc4源码解读过程

//objc-msg-arm64.s
objc_msgSend

//objc-runtime-new.mm
class_getInstanceMethod
lookUpImpOrNil
lookUpImpOrForward
_class_initialize
callInitialize
objc_msgSend(cls, SEL_initialize)

7.4 +load方法、+initialize方法区别

  • 调用方式
  1. load是根据函数地址直接调用
  2. initialize是通过objc_msgSend调用
  • 调用时刻
  1. load是runtime加载类、分类的时候调用(只会调用1次)
  2. initialize是类第一次接收到消息的时候调用,每一个类只会initialize一次(父类的initialize方法可能会被调用多次
  • load调用顺序
  1. 先调用类的load, 先编译的类, 优先调用load,调用子类的load之前, 会先调用父类的load
  2. 再调用分类的load, 先编译的分类,优先调用load
  • initialize调用顺序
  1. 先初始化父类
  2. 再初始化子类(可能最终调用的是父类的initialize方法)

7.5 给分类"添加成员变量"

默认情况下,因为分类底层结构的限制,不能添加成员变量到分类中。但可以通过关联对象来间接实现

7.5.1 关联对象API

//添加关联对象
void objc_setAssociatedObject(id object, const void * key,
                                id value, objc_AssociationPolicy policy)

//获得关联对象
id objc_getAssociatedObject(id object, const void * key)

//移除所有的关联对象
void objc_removeAssociatedObjects(id object)

7.5.2 key的常见用法

static void *MyKey = &MyKey;
objc_setAssociatedObject(obj, MyKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
objc_getAssociatedObject(obj, MyKey)

static char MyKey;
objc_setAssociatedObject(obj, &MyKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
objc_getAssociatedObject(obj, &MyKey)

//使用属性名作为key
objc_setAssociatedObject(obj, @"property", value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_getAssociatedObject(obj, @"property");

//使用get方法的@selecor作为key
objc_setAssociatedObject(obj, @selector(getter), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
objc_getAssociatedObject(obj, @selector(getter))

7.5.3 objc_AssociationPolicy

objc_AssociationPolicy 对应的修饰符
OBJC_ASSOCIATION_ASSIGN assign
OBJC_ASSOCIATION_RETAIN_NONATOMIC strong, nonatomic
OBJC_ASSOCIATION_COPY_NONATOMIC copy, nonatomic
OBJC_ASSOCIATION_RETAIN strong, atomic
OBJC_ASSOCIATION_COPY copy, atomic

7.5.4 关联对象的原理

实现关联对象技术的核心对象有

  • AssociationsManager
  • AssociationsHashMap
  • ObjectAssociationMap
  • ObjcAssociation

objc4源码解读:objc-references.mm

class AssociationsManager {
    static AssociationsHashMap *_map;
};

class AssociationsHashMap : public unordered_map

class ObjectAssociationMap : public std::map

class ObjcAssociation {
    uintptr_t _policy;
    id _value;
};
Objective-C基础-OC语法_第16张图片
关联对象的原理.jpg
  • 关联对象并不是存储在被关联对象本身内存中
  • 关联对象存储在全局的统一的一个AssociationsManager
  • 设置关联对象为nil,就相当于是移除关联对象

8、Block

8.1 block的本质

  • block本质上也是一个OC对象,它内部也有个isa指针
  • block是封装了函数调用以及函数调用环境的OC对象
  • block的底层结构


    Objective-C基础-OC语法_第17张图片
    Block本质.jpg

8.2 block的变量捕获

为了保证block内部能够正常访问外部的变量,block有个变量捕获机制


Objective-C基础-OC语法_第18张图片
block的变量捕获.jpg
int global_var = 10;//全局变量
static int static_global_var = 10;//静态全局变量

void (^block)(void);
void test() {
    auto int local_var = 10;//局部变量
    static int static_local_var = 10;//静态局部变量
    block = ^{
        NSLog(@"%d, %d, %d, %d", global_var, static_global_var, local_var, static_local_var);
    };
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        test();
    }
    return 0;
}

转换为C++代码

struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

static struct __test_block_desc_0 {
  size_t reserved;
  size_t Block_size;
}

int global_var = 10;                    //全局变量
static int static_global_var = 10;  //静态全局变量

void (*block)(void);

struct __test_block_impl_0 {
  struct __block_impl impl;
  struct __test_block_desc_0* Desc;
  int local_var;                    //捕获的局部变量
  int *static_local_var;        //捕获的静态局部变量
  __test_block_impl_0(void *fp, struct __test_block_desc_0 *desc, int _local_var, int *_static_local_var, int flags=0) : local_var(_local_var), static_local_var(_static_local_var) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

8.3 block的类型

block有3种类型,可以通过调用class方法或者isa指针查看具体类型,最终都是继承自NSBlock类型

  • __NSGlobalBlock__ ( _NSConcreteGlobalBlock )
  • __NSStackBlock__ ( _NSConcreteStackBlock )
  • __NSMallocBlock__ ( _NSConcreteMallocBlock )
Objective-C基础-OC语法_第19张图片
block的内存区域.png
Objective-C基础-OC语法_第20张图片
Block类型.jpg
  • 每一种类型的block调用copy后的结果如下所示
Objective-C基础-OC语法_第21张图片
BlockCopy.png

ARC环境下,编译器会根据情况自动将栈上的block复制到堆上,比如以下情况

  • block作为函数返回值时
  • 将block赋值给__strong指针时
  • block作为Cocoa API中方法名含有usingBlock的方法参数时
  • block作为GCD API的方法参数时

8.4 对象类型的auto变量

  • 当block内部访问了对象类型的auto变量时, 如果block是在栈上,将不会对auto变量产生强引用

  • 如果block被拷贝到堆上,会调用block内部的copy函数, copy函数内部会调用_Block_object_assign函数,_Block_object_assign函数会根据auto变量的修饰符(__strong__weak__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用。

  • 如果block从堆上移除, 会调用block内部的dispose函数, dispose函数内部会调用_Block_object_dispose函数
    _Block_object_dispose函数会自动释放引用的auto变量(release)。

Objective-C基础-OC语法_第22张图片
Block-copy-dispose函数.png

例子

//MRC环境下测试
@interface Person : NSObject
@property (assign, nonatomic) int age;
@end

@implementation Person
- (void)dealloc {
    NSLog(@"Person - dealloc");
    [super dealloc];
}
@end

1、如果block是在栈上,将不会对auto变量产生强引用

//MRC环境下测试
typedef void (^Block)(void);
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Block block;
        {
            Person *person = [[Person alloc] init];
            person.age = 10;
            block = ^{
                NSLog(@"---------%d", person.age);
            };//栈block
            [person release];
        }
        NSLog(@"---------%@", block);
        NSLog(@"---end---");
    }
    return 0;
}

转换为C++代码如下

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  Person *person;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, Person *_person, int flags=0) : person(_person) {
    impl.isa = &_NSConcreteStackBlock;//栈Block
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

栈Block输出.jpg

2 如果block是在堆上, 根据auto变量的__strong__weak__unsafe_unretained形成强引用或者弱引用

strong

//ARC环境下测试
typedef void (^Block)(void);
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Block block;
        {
            Person *person = [[Person alloc] init];
            person.age = 10;
            block = ^{
                NSLog(@"---------%d", person.age);
            };
            block();
        }
        NSLog(@"---------%@", block);
        NSLog(@"---end---");
    }
    return 0;
}

转化为C++代码

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  Person *__strong person;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, Person *__strong _person, int flags=0) : person(_person) {
    impl.isa = &_NSConcreteStackBlock;//堆block
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
堆Block输出Strong.jpg

__weak

//ARC环境下测试
typedef void (^Block)(void);
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Block block;
        {
            Person *person = [[Person alloc] init];
            person.age = 10;
            __weak Person *weakPerson = person;
            block = ^{
                NSLog(@"---------%d", weakPerson.age);
            };
            block();
        }
        NSLog(@"---------%@", block);
        NSLog(@"---end---");
    }
    return 0;
}

转化为C++代码

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  Person *__weak weakPerson;    //weak指针
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, Person *__weak _weakPerson, int flags=0) : weakPerson(_weakPerson) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
堆Block输出weak.jpg

8.5 __block修饰符

8.5.1 __block原理

  • __block可以用于解决block内部无法修改auto变量值的问题
  • __block不能修饰全局变量、静态变量(static)
  • 编译器会将__block变量包装成一个对象
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_age_0 *age; // by ref
};

struct __Block_byref_age_0 {
  void *__isa;
__Block_byref_age_0 *__forwarding;
 int __flags;
 int __size;
 int age;
};

Objective-C基础-OC语法_第23张图片
Block__forwarding指针.png

8.5.2 __block的内存管理

1、当block在栈上时,并不会对__block变量产生强引用。
2、当block被copy到堆时

  • 会调用block内部的copy函数
  • copy函数内部会调用_Block_object_assign函数
  • _Block_object_assign函数会对__block变量形成强引用(retain
Objective-C基础-OC语法_第24张图片
Block__copy__block变量.jpg

3、当block从堆中移除时

  • 会调用block内部的dispose函数
  • dispose函数内部会调用_Block_object_dispose函数
  • _Block_object_dispose函数会自动释放引用的__block变量(release
Objective-C基础-OC语法_第25张图片
Block__dispose_block变量.jpg

4、__block__forwarding指针

Objective-C基础-OC语法_第26张图片
Blockcopy后的__forwarding指针.jpg

5、对象类型的auto变量、__block变量

  • 当block在栈上时,对它们都不会产生强引用

  • 当block拷贝到堆上时,都会通过copy函数来处理它们

  • __block变量(假设变量名叫做a)
    _Block_object_assign((void*)&dst->a, (void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);

  • 对象类型的auto变量(假设变量名叫做p)
    _Block_object_assign((void*)&dst->p, (void*)src->p, 3/*BLOCK_FIELD_IS_OBJECT*/);

  • 当block从堆上移除时,都会通过dispose函数来释放它们

  • __block变量(假设变量名叫做a)
    _Block_object_dispose((void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);

  • 对象类型的auto变量(假设变量名叫做p)
    _Block_object_dispose((void*)src->p, 3/*BLOCK_FIELD_IS_OBJECT*/);

8.6 Block的循环引用问题

Objective-C基础-OC语法_第27张图片
Block循环引用1.jpg

Objective-C基础-OC语法_第28张图片
Block循环引用2.jpg

ARC下

使用__weak__unsafe_unretained解决循环引用问题
__weak__unsafe_unretained区别在于__weak在对象释放时会自动置nil,而__unsafe_unretained不会置nil。

__weak typeof(self) weakSelf = self;
self.block = ^{    
    NSLog(@"%p", weakSelf);
};

使用__block解决循环引用问题,必须调用Block

__block id weakSelf = self;
self.block = ^{
    NSLog(@"%p", weakSelf);
    weakSelf = nil;
};
self.block();

MRC下

__unsafe_unretained解决循环引用问题

__unsafe_unretained id weakSelf = self;
self.block = ^{
    NSLog(@"%p", weakSelf);
};

__block解决循环引用问题,可以不用调用Block,

__block id weakSelf = self;
self.block = ^{
    NSLog(@"%p", weakSelf);
};

你可能感兴趣的:(Objective-C基础-OC语法)