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
实现
#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
方法不一样?
copy
调用objc_setProperty
, 而strong
并没有,答案还是去LLVM
的找答案?
打开LLVM
的开源工程, 在项目里搜索objc_setPropery
,
这里的映射关系很直观的可以看出来:
属性用atomic
&& copy
修饰使用objc_setProperty_atomic_copy
属性用atomic
&& 没有copy
修饰使用objc_setProperty_atomic
属性不是atomic
&& copy
修饰使用objc_setProperty_nonatomic_copy
其它修饰使用 objc_setProperty_atomic_copy
在项目中,系统没有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); }
0x02 - 结论
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
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: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
后边的总字节数
更多类型可以对应官网上到这个表。
0x03 - 属性的attribute
方法有编码类型, 属性也有编码类型
{{"sex","T@\"NSString\",&,N,V_sex"},
{"other","T@\"NSString\",C,N,V_other"}}
T
表示类型@
表示变量类型&
表示引用类型N
表示是nonatomic
V
表示variable
变量,也就是下划线的变量_sex
c
表示copy