preface
What is the nature of the object? We often hear that objects are structures in nature, so how do we verify that? NONPOINTER_ISA = NONPOINTER_ISA = NONPOINTER_ISA With that in mind, we compiled the OC code with the compiler to see if the compiled code could find any clues.
Compiler Introduction
Clang compiler
Clang is a lightweight compiler for C, C++, and Objective-C. Source code is published under the BSD protocol. Clang will support its normal lambda expressions, simplified handling of return types, and better handling of constEXPr keywords. Clang is an Apple-led, LLVM-based C/C++/ Objective-C compiler.
The Clang use
Clang-rewrite-objc main.m -o main. CPP compiles the object file into a c++ file
If the command line project is no problem, smooth compilation success!
If it is an iOS project, an error will be reported:
The solution is to specify the SDK path (iphonesimulator14.2. SDK should be modified to the corresponding version of your machine)
Clang-rewrite-objc-fobjc-arc-fobjc-runtime = ios-14.2-isysroot / Applications/Xcode. App/Contents/Developer/Platforms/iPhoneSimulator platform/Developer/SDKs/iPhoneSimulator14.2. The SDK main2.m -o main2.cppCopy the code
It can also be compiled using Xcode’s own xcrun command:
# emulator xcrun - SDK iphoneos clang -arch arm64 -rewrite-objc main2.m -o main. CPP arm64 -rewrite-objc main.m -o main.cppCopy the code
Compare the code before and after compilation
Pre-compile code
@interface WJPerson : NSObject
@property (nonatomic.strong) NSString *sName;
@property (nonatomic.copy) NSString *cName;
@end
@implementation WJPerson
@end
Copy the code
Compiled code
// omit the above configuration code
#ifndef _REWRITER_typedef_WJPerson
#define _REWRITER_typedef_WJPerson
typedef struct objc_object WJPerson;
typedef struct {} _objc_exc_WJPerson;
#endif
extern "C" unsigned long OBJC_IVAR_$_WJPerson$_sName;
extern "C" unsigned long OBJC_IVAR_$_WJPerson$_cName;
struct WJPerson_IMPL {
struct NSObject_IMPL NSObject_IVARS;
NSString *_sName;
NSString *_cName;
};
// @property (nonatomic, strong) NSString *sName;
// @property (nonatomic, copy) NSString *cName;
/* @end */
// @implementation WJPerson
static NSString * _I_WJPerson_sName(WJPerson * self, SEL _cmd) { return(* (NSString((* *)char *)self + OBJC_IVAR_$_WJPerson$_sName)); }
static void _I_WJPerson_setSName_(WJPerson * self, SEL _cmd, NSString *sName) { (*(NSString((* *)char *)self + OBJC_IVAR_$_WJPerson$_sName)) = sName; }
static NSString * _I_WJPerson_cName(WJPerson * self, SEL _cmd) { return(* (NSString((* *)char *)self + OBJC_IVAR_$_WJPerson$_cName)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long.id.bool.bool);
static void _I_WJPerson_setCName_(WJPerson * self, SEL _cmd, NSString *cName) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct WJPerson, _cName), (id)cName, 0.1); }
// @end
/ / to omit...
Copy the code
The analysis process
1, the originalWJPerson
inheritanceNSObject
It becomes inheritanceobjc_object
typedef struct objc_object WJPerson;
Copy the code
2,WJPerson
Class intoWJPerson_IMPL
Struct, so classThe essence is the structure
, the declaration ofattribute
Also help us generate automaticallyAn underlined member variable
.
struct WJPerson_IMPL {
struct NSObject_IMPL NSObject_IVARS;
NSString *_sName;
NSString *_cName;
};
// @property (nonatomic, strong) NSString *sName;
// @property (nonatomic, copy) NSString *cName;
Copy the code
3,WJPerson_IMPL
Member variables instruct NSObject_IMPL NSObject_IVARS;
We don’t declare that it’s an inherited member variable. The structure can be found in the compiled codeobjc_object
和 NSObject_IMPL
There’s a member variable in thereClass isa
.
typedef struct objc_class *Class;
struct objc_object {
Class _Nonnull isa __attribute__((deprecated));
};
struct NSObject_IMPL {
Class isa;
};
Copy the code
4,WJPerson.m
I didn’t declare any methods, but the compiled code has 4 more methods, respectivelysName
,cName
thesetter
、getter
Function implementation. Look closely and you’ll seesName
,cName
Different implementation methods,sName
Is read and write by memory translation, andcName
Is through theobjc_setProperty
Is to achieve.sName
,cName
They’re all attributes, just different keywords,sName
是 strong
Modified,cName
iscopy
Decorated, so we can conclude that the decorated keyword changes the underlying implementation.
// @implementation WJPerson
static NSString * _I_WJPerson_sName(WJPerson * self, SEL _cmd) { return(* (NSString((* *)char *)self + OBJC_IVAR_$_WJPerson$_sName)); }
static void _I_WJPerson_setSName_(WJPerson * self, SEL _cmd, NSString *sName) { (*(NSString((* *)char *)self + OBJC_IVAR_$_WJPerson$_sName)) = sName; }
static NSString * _I_WJPerson_cName(WJPerson * self, SEL _cmd) { return(* (NSString((* *)char *)self + OBJC_IVAR_$_WJPerson$_cName)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long.id.bool.bool);
static void _I_WJPerson_setCName_(WJPerson * self, SEL _cmd, NSString *cName) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct WJPerson, _cName), (id)cName, 0.1); }
// @end
Copy the code
NONPOINTER_ISA
Before exploring NONPOINTER_ISA, let’s look at structures, unions, and bitfields
Comparison between structure, bit domain and union
The structure of the body
struct Car1 {
BOOL front;
BOOL back;
BOOL left;
BOOL right;
};
struct Car1 car1;
NSLog(@"%ld".sizeof(car1));
Copy the code
The memory size of car1 is 4 bytes. If you don’t know how to calculate the size of the structure, you can see the internal memory alignment of the structure I wrote earlier.
A domain
/ / the mutex
struct Car2 {
BOOL front: 1; // Use one bit
BOOL back : 1;
BOOL left : 1;
BOOL right: 1;
};
struct Car2 car2;
NSLog(@"%ld".sizeof(car2));
Copy the code
The BOOL type is either 1 or 0, so one bit will do. Car2 uses four bits, which is only half a byte, and the smallest unit is one byte, so the memory size of car2 output above is 1 byte. Three bytes less than the structure.
A consortium
/ / structure
struct Person1 {
char *name;
int age;
double height ;
};
/ / a consortium
union Person2 {
char *name;
int age;
double height ;
};
Copy the code
struct Person1 person1;
person1.name = "person1";
person1.age = 18;
person1.height = 180;
union Person2 person2;
person2.name = "person2";
person2.age = 18;
person2.height = 180;
NSLog(@"%ld-%ld".sizeof(person1),sizeof(person2));
// Print: 24-8
Copy the code
In each step, the attribute of Person1 is successfully assigned and the previously assigned attribute remains unchanged.
(lldb) p person1
(Person1) $0 = (name = 0x0000000000000000, age = 0, height = 0)
(lldb) p person1
(Person1) $1 = (name = "person1", age = 0, height = 0)
(lldb) p person1
(Person1) $2 = (name = "person1", age = 18, height = 0)
(lldb) p person1
(Person1) $3 = (name = "person1", age = 18, height = 180)
(lldb)
Copy the code
In each step, the person2 attribute is assigned successfully, but the other attributes are either dirty or empty, and only the currently assigned attribute is a normal value.
(lldb) p person2
(Person2) $4 = (name = 0x0000000000000000, age = 0, height = 0)
(lldb) p person2
(Person2) $5 = (name = "person2", age = 15952, height = 2.1220036723004548 e-314)
(lldb) p person2
(Person2) $6 = (name = "", age = 18, height = 2.1219957998584539 e-314)
(lldb) p person2
(Person2) $7 = (name = "", age = 0, height = 180)
(lldb)
Copy the code
So we can come to the conclusion that,
Unions are mutually exclusive and share a memory address that affects other member variables
The structure is co-existing, and the member variables are each occupying their own memory, with complementary effects
- A domain (
bit field
), C allows a structure to specify the memory size of its members in bits, using bitfields (bit field
) can store data with fewer bits,Apple uses bitfields and unions to optimize memory for classes
NONPOINTER_ISA
ISA is divided into pure ISA and NONPOINTER_ISA, which contains some information about the class in addition to a pure pointer.
In the previous iOS source exploration alloc, we will eventually bind the structure pointer from the heap to the current class via obj->initIsa(CLS).
inline void
objc_object::initIsa(Class cls)
{
initIsa(cls, false.false);
}
Copy the code
objc_object::initIsa(Class cls, bool nonpointer, UNUSED_WITHOUT_INDEXED_ISA_AND_DTOR_BIT boolhasCxxDtor) { ASSERT(! isTaggedPointer()); isa_t newisa(0);
// Omit some code
}
Copy the code
/ / share
union isa_t {
// constructor
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
/ / variable
uintptr_t bits;
private:
// Accessing the class requires custom ptrauth operations, so
// force clients to go through setClass/getClass by making this
// private.
Class cls;
public:
#if defined(ISA_BITFIELD)
struct {
ISA_BITFIELD; // defined in isa.h
};
bool isDeallocating() {
return extra_rc == 0 && has_sidetable_rc == 0;
}
void setDeallocating() {
extra_rc = 0;
has_sidetable_rc = 0;
}
#endif
void setClass(Class cls, objc_object *obj);
Class getClass(bool authenticated);
Class getDecodedClass(bool authenticated);
};
Copy the code
ISA_BITFIELD
inarm64
andx86_64
Definitions under the architecture
# if __arm64__
// ARM64 simulators have a larger address space, so use the ARM64e
// scheme even when simulators build for ARM64-not-e.
/ / simulator
# if __has_feature(ptrauth_calls) || TARGET_OS_SIMULATOR
# define ISA_MASK 0x007ffffffffffff8ULL
# define ISA_MAGIC_MASK 0x0000000000000001ULL
# define ISA_MAGIC_VALUE 0x0000000000000001ULL
# define ISA_HAS_CXX_DTOR_BIT 0
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t weakly_referenced : 1; \
uintptr_t shiftcls_and_sig : 52; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 8
# define RC_ONE (1ULL<<56)
# define RC_HALF (1ULL<<7)
# else
/ / real machine
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
# define ISA_HAS_CXX_DTOR_BIT 1
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t unused : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 19
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
# endif
# elif __x86_64__
//macOS
# define ISA_MASK 0x00007ffffffffff8ULL
# define ISA_MAGIC_MASK 0x001f800000000001ULL
# define ISA_MAGIC_VALUE 0x001d800000000001ULL
# define ISA_HAS_CXX_DTOR_BIT 1
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t unused : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 8
# define RC_ONE (1ULL<<56)
# define RC_HALF (1ULL<<7)
# else
# error unknown architecture for packed isa
# endif
Copy the code
-
Nonpointer: indicates whether pointer optimization is enabled for isa Pointers. 0: indicates pure ISA Pointers. 1: indicates not only the address of the class object, but also the class information and reference count of the object
-
Has_assoc: flag bit of the associated object. 0 does not exist and 1 exists
-
Has_cxx_dtor: does the object have a destructor for C++ or Objc? If it has a destructor, the destructor logic needs to be done. If not, the object can be freed faster
-
Shiftcls: Stores the value of the class pointer. With pointer optimization turned on, 33 bits are used to store class Pointers in the ARM64 architecture
-
-magic: Used by the debugger to determine whether the current object is a real object or has no space to initialize
-
Weakly_referenced: A weak variable that records whether an object is pointed to or used to point to an ARC. Objects without weak references can be released faster
-
Unused: Indicates whether it has not been used
-
Has_sidetable_rc: hash table that needs to be borrowed to store carry when object reference technique is greater than 10
-
Extra_rc: When representing the reference count of this object, the reference count is actually subtracted by 1. For example, if the object’s reference count is 10, the extra_rc is 9. If the reference count is greater than 10, the following has_sideTABLE_rc is used