手撕iOS底层10 -- strong©&weak底层了解

0X01 - strong & copy & weak

@interface LGPerson : NSObject
@property (nonatomic, copy) NSString *name;
@end

@implementation LGPerson
@end

@interface Teacher : LGPerson
{
    NSString *age;
}
@property (nonatomic, strong) NSString *sex;
@property (nonatomic, copy) NSString *other;

@end

@implementation Teacher

@end
复制代码

如上示例代码, 使用clang -rewrite-objc main.m命令, 查看Objective-C源代码的c实现html

#ifndef _REWRITER_typedef_LGPerson
#define _REWRITER_typedef_LGPerson
typedef struct objc_object LGPerson;
typedef struct {} _objc_exc_LGPerson;
#endif

extern "C" unsigned long OBJC_IVAR_$_LGPerson$_name;
struct LGPerson_IMPL {
	struct NSObject_IMPL NSObject_IVARS; // 继承自NSObject
	NSString *_name;
};

// @property (nonatomic, copy) NSString *name;
/* @end */


// @implementation LGPerson
// 属性name的get方法
static NSString * _I_LGPerson_name(LGPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_name)); }

extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);
// 属性name的set方法 copy修饰
static void _I_LGPerson_setName_(LGPerson * self, SEL _cmd, NSString *name) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct LGPerson, _name), (id)name, 0, 1); }
// @end


#ifndef _REWRITER_typedef_Teacher
#define _REWRITER_typedef_Teacher
typedef struct objc_object Teacher;
typedef struct {} _objc_exc_Teacher;
#endif

extern "C" unsigned long OBJC_IVAR_$_Teacher$_sex;
extern "C" unsigned long OBJC_IVAR_$_Teacher$_other;
struct Teacher_IMPL {
	struct LGPerson_IMPL LGPerson_IVARS;// 继承自LGPerson
  // 成员变量
	NSString *age; 
  // 属性的带下划线的成员变量
	NSString *_sex; 
	NSString *_other;
};

// @property (nonatomic, strong) NSString *sex;
// @property (nonatomic, copy) NSString *other;

/* @end */

// @implementation Teacher


static NSString * _I_Teacher_sex(Teacher * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_Teacher$_sex)); }
// 属性sex的set方法 strong修饰
static void _I_Teacher_setSex_(Teacher * self, SEL _cmd, NSString *sex) { (*(NSString **)((char *)self + OBJC_IVAR_$_Teacher$_sex)) = sex; }

static NSString * _I_Teacher_other(Teacher * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_Teacher$_other)); }
// 属性other的set方法 copy修饰
static void _I_Teacher_setOther_(Teacher * self, SEL _cmd, NSString *other) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct Teacher, _other), (id)other, 0, 1); }
// @end
复制代码

经过查看c代码的实现, 发现copystrong修饰的属性在set方法不同?objective-c

copy调用objc_setProperty, 而strong并无,答案仍是去LLVM的找答案?markdown

打开LLVM的开源工程, 在项目里搜索objc_setProperyapp

这里的映射关系很直观的能够看出来:ide

属性用atomic && copy修饰使用objc_setProperty_atomic_copyoop

属性用atomic && 没有copy修饰使用objc_setProperty_atomic优化

属性不是atomic && copy修饰使用objc_setProperty_nonatomic_copyui

其它修饰使用 objc_setProperty_atomic_copythis

在项目中,系统没有setName这个方法,调用setName()会直接sel被hook掉, 走到底层objc_setPro这里去编码

objc-781的源码里,对应的方法如上。


copy修饰的 经过断点底层是调用的objc_storeStrong

strong修饰的底层调用的也是objc_storeStrong,源码中看objc_storeStrong

void objc_storeStrong(id *location, id obj) {
    id prev = *location;
    if (obj == prev) {
        return;
    }
    objc_retain(obj); // 对新值retain
    *location = obj;
    objc_release(prev);// 对旧值的release
}

复制代码

而后在LLVM项目中搜索objc_storeStrong

EmitARCStoreStrongCall方法里找到,再去找那里调用了这个方法, 在GenerateCopyHelperFunction方法里

在上边这个代码里, 有一个switch, 分好多种枚举BlockCaptureEntityKind状况, 针对strongweak有不一样的处理

/// Represents a type of copy/destroy operation that should be performed for an
/// entity that's captured by a block.
enum class BlockCaptureEntityKind {
  CXXRecord, // Copy or destroy
  AddressDiscriminatedPointerAuth,
  ARCWeak,
  ARCStrong,
  NonTrivialCStruct,
  BlockObject, // Assign or release
  None
};
复制代码
  • 若是是strong修饰,就会执行EmitARCStoreStrongCall方法

  • 若是是weak修饰,就会执行EmitARCCopyWeak,在底层调用objc_initWeak

    /// void \@objc_copyWeak(i8** %dest, i8** %src)
    /// Disregards the current value in %dest. Essentially
    /// objc_release(objc_initWeak(dest, objc_readWeakRetained(src)))
    void CodeGenFunction::EmitARCCopyWeak(Address dst, Address src) {
      emitARCCopyOperation(*this, dst, src,
                           CGM.getObjCEntrypoints().objc_copyWeak,
                           llvm::Intrinsic::objc_copyWeak);
    }
    
    复制代码

0x02 - 结论

  1. copystrong修饰的属性在底层编译是不同的,由编译器LLVM对全部属性做了优化处理, 其中copy是经过objc_setProperty赋值, strong是经过基地址(self)+偏移来赋值,(也就是指针偏移到成员变量所在的位置)而后还原成strong类型
// 源码c实现
static void _I_Teacher_setSex_(Teacher * self, SEL _cmd, NSString *sex) { (*(NSString **)((char *)self + OBJC_IVAR_$_Teacher$_sex)) = sex; } 
//-----------------------------------
// objc-781源码 
void objc_storeStrong(id *location, id obj) {
    id prev = *location;
    if (obj == prev) {
        return;
    }
    objc_retain(obj); // 对新值retain
    *location = obj;
    objc_release(prev);// 对旧值的release
}
复制代码

上边俩段代码作的功能是同样的,

  1. strongcopy在底层调用objc_storeStrong, 本质都是对新值retain,对旧值release
  2. weak在底层是调用的objc_initWeak

0x03 - 方法签名

编译器将每一个方法的返回值和参数类型编码为一个字符串, 并将其与方法的selector关联在一块儿, 可使用@encode编译器指令来获取它,给定一个类型,@encode返回这个类型的字符串编码,任何可使用sizeof()操做参数,均可以使用@encode

Objective-C的源代码 c实现里,有这样的一些代码和符号, 打开经过clang -rewrite-objc main.m生成的main.cpp文件,

{{(struct objc_selector *)"name", "@16@0:8", (void *)_I_LGPerson_name},
	{(struct objc_selector *)"setName:", "v24@0:8@16", (void *)_I_LGPerson_setName_},
	{(struct objc_selector *)"name", "@16@0:8", (void *)_I_LGPerson_name},
	{(struct objc_selector *)"setName:", "v24@0:8@16", (void *)_I_LGPerson_setName_}}
复制代码

@16@0:8v24@0:8@16等这些表明什么?能够经过Type Encodings - 官方文档这里来了解具体的知识

v24@0:8@16为🌰例,

  • v表明没有返回值
  • 24表示总共占用的字节数
  • @表示第一个参数id的类型是一个对象
  • 0表示从0开始,占8个字节
  • : 表示表明SEL第二个参数 从8开始,占8个字节
  • @第三个@表明set方法到参数类型, 从16开始, 占8个,

一共是24个字节, 就是v24后边的总字节数

更多类型能够对应官网上到这个表。

0x03 - 属性的attribute

方法有编码类型, 属性也有编码类型

{{"sex","T@\"NSString\",&,N,V_sex"},
	{"other","T@\"NSString\",C,N,V_other"}}
复制代码
  • T表示类型
  • @表示变量类型
  • &表示引用类型
  • N表示是nonatomic
  • V表示variable变量,也就是下划线的变量_sex
  • c表示copy

参考地址Property Type and Functions

欢迎大佬留言指正😄,码字不易,以为好给个赞👍 有任何表达或者理解失误请留言交流;共同进步;

相关文章
相关标签/搜索