@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
代码的实现, 发现copy
和strong
修饰的属性在set
方法不同?objective-c
copy
调用objc_setProperty
, 而strong
并无,答案仍是去LLVM
的找答案?markdown
打开LLVM
的开源工程, 在项目里搜索objc_setPropery
, app
这里的映射关系很直观的能够看出来:ide
属性用atomic
&& copy
修饰使用objc_setProperty_atomic_copy
oop
属性用atomic
&& 没有copy
修饰使用objc_setProperty_atomic
优化
属性不是atomic
&& copy
修饰使用objc_setProperty_nonatomic_copy
ui
其它修饰使用 objc_setProperty_atomic_copy
this
在项目中,系统没有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
状况, 针对strong
和weak
有不一样的处理
/// 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);
}
复制代码
copy
和strong
修饰的属性在底层编译是不同的,由编译器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
}
复制代码
上边俩段代码作的功能是同样的,
strong
和copy
在底层调用objc_storeStrong
, 本质都是对新值retain
,对旧值release
weak
在底层是调用的objc_initWeak
编译器将每一个方法的返回值和参数类型编码为一个字符串, 并将其与方法的
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:8
, v24@0:8@16
等这些表明什么?能够经过Type Encodings - 官方文档这里来了解具体的知识
以v24@0:8@16
为🌰例,
v
表明没有返回值24
表示总共占用的字节数@
表示第一个参数id
的类型是一个对象0
表示从0开始,占8个字节:
表示表明SEL
第二个参数 从8开始,占8个字节@
第三个@表明set方法到参数类型, 从16开始, 占8个,一共是24个字节, 就是v24
后边的总字节数
更多类型能够对应官网上到这个表。
attribute
方法有编码类型, 属性也有编码类型
{{"sex","T@\"NSString\",&,N,V_sex"},
{"other","T@\"NSString\",C,N,V_other"}}
复制代码
T
表示类型@
表示变量类型&
表示引用类型N
表示是nonatomic
V
表示variable
变量,也就是下划线的变量_sex
c
表示copy