Series of articles:OC Basic principle series.OC Basic knowledge series

preface

I was using clang to convert a.m file into a.cpp file, and when I looked inside, it was interesting to see how properties are compiled, so this is going to look at iOS memory management and look at properties, member variables, instance variables

ARC & MRC

Memory management schemes in iOS can be roughly divided into two categories: MRC (manual memory management) and ARC (automatic memory management).

  • MRC
    • inMRCIn the era, the system determines whether an object is destroyed by its reference count, with the following rules
    • The object isWhen creatingThe reference counts are1
    • When objectReferenced by another pointer, you need to manually invoke the[objc retain], make the objectReference count +1
    • When pointer variables are no longer using objects, they need to be called manually[objc release]toRelease object, make the objectReference count -1
    • When an objectThe reference count is 0, the system willThe destructionThis object

[Conclusion] : In MRC mode, the following must be observed:Who created.Who released.Who reference.Who manage

  • ARC
    • ARCPattern is inWWDC2011And iOS5 introducedAutomatic management mechanism, automatic reference counting, is a compiler feature
    • The rules are the same as the MRC, except that,Manual retain, release, and autorelease are not required in ARC mode. The compiler inserts release and autorelease in place.

Memory layout

Before theMemory management (MEMORY partition)Introduced the five regions of memory, in fact, in addition to the five regionsThe kernel areaandThe reservations, take 4G mobile phones as an example, as shown in the figure below3GB is given to the five regions + reserved areasAnd the rest of the1GB for the kernel

  • The kernel area: Area of the system used for kernel processing operations
  • The reservations: Reserved for the system to handle nil, etc

The reason why the last memory address is from0x00400000It started because0 x00000000 said nilCan’t be used directlynilRepresents a segment, so a separate segment of memory is usedDeal with nil, etc.

Memory management scheme

In addition to the MRC and ARC mentioned above, there are three memory management schemes

  • 1.Tagged Pointer: dedicated toWorking with small objects, such as NSNumber, NSDate, and small NSString
  • 2.Nonpointer_isa: non-pointer type ISA, mainly used forOptimize 64-bit addresses. This is inStructure analysis of isa pointer to OC object (below)Isa was introduced
  • 3.SideTables: hash table, the main hash tableTwo tables, respectively,Reference counter table,A weak reference table

This section focuses on Tagged Pointer and SideTables

Tagged Pointer

We introduce it with an interview questionTagged Pointer Is there a problem with the above code? Why?

Running the code above we found thattaggedPointerDemoisnormalOf, but intouchesBeganThe method appearscollapse(taggedPointerDemo executes in viewDidLoad method)

The cause of the crash is that multiple threads release the same object, resulting in excessive release of the object, which causes the crash.

【 Thought 】 : The internal implementation of taggedPointerDemo and touchesBegan is basically the same, the only difference is that nameStr is different, but one didn’t have any problems, and the other crashed. Is it because of nameStr?

validation

So let’s look at what’s different about nameStr, breaking the point at the NSLog, running the code, printing nameStr separately, okay We found that they were different types,taggedPointerDemoIn thenameStrIs of typeNSTaggedPointerString typeAnd thetouchesBeganThe type in is__NSCFStringType.

  • 1.NSTaggedPointerString typetheSmall objectsAnd stored in theThe constant areaBecause nameStr in alloc was originally allocated inThe heap areaBut because ofTaggedPointerDemo has a smaller nameStr, afterIOS optimization, it becomesNSTaggedPointerString typeThere,The constant area
  • 2.touchesBeganIn the methodnameStrType isNSCFStringType stored inThe heap areaon

Memory management for NSString

We can test the memory management of AN NSString in two ways, with NSString initialization

  • 1. BywithString+@""Mode initialization
  • 2. ByWithFormatMode initialization

Running results:We can see from the print that there are three main types of memory management for NSString

  • 1.NSTaggedPointerString: label pointer, is apple inA 64 - bitUnder the environment ofNsstrings, NSNumberAnd so onTo optimize the. For NSString objects
    • whenThe string is a combination of digits and letters and contains 9 characters or less, automatically becomesNSTaggedPointerStringType stored inThe constant area
    • When you haveChinese or other special symbols, will directly become__NSCFStringType stored inThe heap area
  • 2.__NSCFString: it is inThe runtimeTo create theNsstrings subclassAfter, createThe reference count is increased by one.Stored on the heap
  • 3.__NSCFConstantString:String constant, it is a kind ofCompile-time constant.RetainCount value is very big, to operate it,Does not cause a reference count change, stored in the string constants area

Agged Pointer Basic principles of small objects

If Tagged Pointer is not overreleased, objC will not overrelease Tagged Pointer

Small object reference counting processing analysis

Take a look at the reallySetProperty source code, and we’ll take a closer look at the reallySetProperty method

So if you see that it’s not a copy modifier, you’re going to assign a new value to objc_retain, and you’re going to release the old value, and you’re going to look at objc_retain, the underlying implementation of objc_release

Objc_retain, objC_release; agged Pointer; From this we can conclude that small objects do not retain and release

Address analysis of small objects

So let’s continue with NSString, for NSString

  • The generalNSString object pointer, it isString value + pointer address.The two are separatethe
  • forTagged Pointer cursor, itsPointer + value,Can be represented in small objects. So Tagged PointerContains both Pointers and values

In the previous articleOC basic principle – class loading process – on (_objc_init implementation principle)In class loading, where_read_imagesSource code has a method pairSmall objects are processed, i.e.,InitializeTaggedPointerObfuscator methodLet’s take a lookinitializeTaggedPointerObfuscatorMethod implementationAfter iOS12, Tagged Pointer uses obfuscation. You can set OBJC_DISABLE_TAG_OBFUSCATION to YES to disable Tagged Pointer obfuscationObjc_debug_taggedpointer_obfuscator = taggedPointerUp here we know the code_objc_encodeTaggedPointerIs through theObjc_debug_taggedpointer_obfuscator Xor incoming valueThe decoding_objc_decodeTaggedPointerthroughObjc_debug_taggedpointer_obfuscator Xor incoming valueIs equivalent toTwo layers of xor. For example: pass in the value 1010 0100 and the mask value 0101 0010

1010 0100 ^0101 0010 mask (encoding) 1111 0110 ^0101 0010 mask (decoding) 1010 0100Copy the code

Let’s look at the address of the decoded little object, where61saidA. the ASCII.63saidC the ASCII, let’s take NSNumber as an exampleWe seeAddresses do store values. But the one behind the little object0 xa, xbWhat does that mean? In the end we are judgingWhether it's a small objectFound the answerso0 xa, xbChiefly used ofCheck if it is a TaggedPointerTo determineThe 64thOn isWhether for 1(taggedpointerPointer address represents both pointer address and value.)

  • 0xaConvert to binary1, 010(64 is 1, followed by 63 to 61TagType type2Said),Nsstrings type
  • 0xbConvert to binary1, 011(64 is 1, followed by 63 to 61TagType type3Said),NSNumber typeOne thing to note here is ifNSNumberThe value is- 1, its addressThe values are in complementThe said

This is where you can go through_objc_makeTaggedPointerThe tag type of the method’s argumentobjc_tag_index_tEnter its enumeration, where2 said nsstrings.3 said NSNumber Just to verify: we can define oneNSDate objectTo verify ittagTypeWhether it is6.By printing the result, its address high is0xe, is converted to binary1, 110, excluding 64 bits of 1, the rest3 bits are exactly 6 in decimal.Matches the enumerated values above

Tagged Pointer summary

  • 1.Tagged PointerSmall object type (used for storageNSNumber, NSDate, small NSString), small object Pointers are no longer simple addresses, butAddress + value, i.e.,The real valueSo, actuallyIt's no longer an objectIt’s just aAn ordinary variable dressed as an objectAnd to. So you can read it directly.Advantage is to occupy small space, save memory
  • 2.Tagged PointerSmall objects,Retain and release will not be enteredInstead, it goes straight back, meaningNo ARC management is required, so you canDirectly released and recovered by the system itself
  • 3.Tagged PointertheMemory is not stored in the heap areaIn, but inThe constant areaBut alsoMalloc and free are not required, so you canDirectly read, instead of reading data stored in the heap,The efficiency ofOn the fastThree timesThe left and right sides.createtheThe efficiency ofCompared to the heap areafastnearly100 timesThe left and right sides. TaggedPointer memory management scheme, thanRegular memory management, much faster
  • 4.Tagged Pointer64-bit address of,The first four digits represent the type.The last four bits are mainly used for system processing.The middle 56 bits are used to store values
  • 5. Memory optimization Suggestion: YesNSStringSay, when a stringsmaller, you are advised to directly pass@ ""Initialize as stored inThe constant area, you candirectlyforread. thanThe WithFormat initialization is faster

Explore memory management for strong and copy

Ready to code

We wrote the following properties in viewController.h

@interface ViewController ()
{
    Man *man;
    NSString *workTime;
    NSInteger times;
}
@property (nonatomic, strong)Student *student;
@property (nonatomic, copy)NSString *schoolName;
@property (nonatomic, copy)NSString *name;
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, strong)NSMutableArray *houses;
@end
Copy the code

Noun explanation

Property: A variable decorated with the @property prefix

Member variables: the variables in {} are man, workTime, and times

Instance variable: If the data type of a member variable is a class that can be instantiated, it is an instance variable

Through clang to a.cpp file. We found the properties in its.cpp file and they are shown below in c++So here’s the get and set methods for properties, and there’s a difference between the set method of name and age and houses.Name1 uses copy, assign for age, and Strong for houses.

To explore the copy

We will explore why use objc_setProperty, we go first to take the check objc_setProperty LLVM source, we find the getOptimizedSetPropertyFn put hair, see below:We can see that the set method of an object can be modified differently depending on the modifier used for the attribute

  • ifUse natomic and copy: objc_setProperty_atomic_copy
  • ifUse natomic and non-copy decorations: objc_setProperty_atomic
  • ifUse non-natomic and copy decorations: objc_setProperty_nonatomic_copy
  • ifUse non-natomic and non-copy decorations: objc_setProperty_nonatomic

Our name is non-natomic and a copy modifier, so it should be objc_setProperty_nonatomic_copy. Let’s go back to the source code and see how objc_setProperty_nonatomic_copy is implemented.Above is the source code implementation we found. We noticed the method reallySetProperty. Go to this methodPictured above,We know that if we copy we're calling copyWithZoneBecause it is not atomic, soIt's going to go 90,91 so it's going to fetch the old value, assign the new value to the *slot, and release the old value. Let’s verify this code

@interface Person : NSObject @property (nonatomic, copy) NSString *name; @end int main(int argc, const char * argv[]) { @autoreleasepool { Person *p = [Person alloc]; P.name = @" xiaoming "; P.name = @" xiao Zhang "; NSLog(@"--->"); NSString *nameStr = [p.name copy]; } return 0; }Copy the code

Run the code, break the pointPrint because xiaoming is the first assignment, there is no old value. We continue toThis time,And I'll assign name to the TAB.The old values are there. And then finally in line 100Old values are released. Let’s seeNSString *nameStr = [p.name copy]; We found thatDidn't leaveWe cut it offreallySetPropertyMethod, where does this method go? We break the point and look at the assembly at the breakpoint stop.We notice that we’ll call it laterobjc_storeStrongLet’s look for the method in the source codeInterrupt point, no longer look at assembly, continue to the next stepBut at the moment ofObj is nil, because beforeThere is no nameStr.objc_retain(id obj)Method to determine ifReturn on existence.Create if it does not existSend the retain method via objc_msgSend. We found thatCall the objc_retain methodLet’s explore the strong property (objc_retain will follow)

To explore the strong

Let’s prepare the following code:

@interface Person : NSObject
@property (nonatomic, strong) NSString *name;
@end
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *p  = [Person alloc];
        p.name = @"aaa";
    }
    return 0;
}
Copy the code

The interruption point, when it comes to here is that we look at the assembly and we findThis method is the same as nameStr, which uses objc_storeStrong So now we print, and we find that now· OBj is our assignment to name. And then I do objobjc_retain.

To explore the objc_retain

We know above that for propertiesTo copy, and attribute usageThe strong modificationWill goobjc_retainLet’s seeobjc_retainWhat did he doProperty using copy modifier does not go to objc_retain. We searched objc_retain globally and found the following code

  • If obj does not exist, return directly
  • 1587: Returns if obj is Tagged Pointer
  • 1588: None of the above satisfy the call to retain()

Let’s look at the retain() method

  • 455: Not Tagged Pointer. This method does not want to handle Tagged Pointer
  • 457:The hasCustomRR function checks classes (including their parent classes)Does it containThe default retain method
  • 458: ifRootRetain () is not called.
  • 461: ifForward the message if it existsTo callCustom retain method

When we go down, we see that we can only go 461 rows, we can't go 458 rows. This is because when hasCustomRR rechecks, it looks up through the ISA pointer until it finds NSObject, where it overrides the retain method _objc_rootRetainWill be calledrootRetain()Methods. Let’s take a look at the rootRetain() methodFound that calledRootRetain (false, false) Note the value passed: false, false, let’s look at the rootRetain method.RootRetain has a variety of methods, and we’ll pick out the most important ones to explain

  • 489: ifYes Tagged Pointer returns directly
  • 492:transcribeToSideTableUsed forIndicates whether extra_rc is overflowingBy default,false(Not copied to SideTable)
  • 499:Atomic get ISA
  • 501:Isa is not optimized over NonpointerisNonpointer in isa structure, specific seeStructure analysis of isa pointer to OC object (below)
  • 502: If not, justEmpty isa.bits of an object
  • 503: If soMetaclass, indicating a class object,Direct return
  • 504: iftryRetainforFalse and sideTable is locked,Play the lock(Retain must be trueIf theThe current thread locks the sideTable object, you need tounlock).
  • 505:Judge tryRetainWhether it istrueTo determine theIs the retain successful?If successful, judgesidetable_retain()Does it exist? IfReturn this if it existsIf theReturn nil if it does not exist. iffailureCall it further downsidetable_retain().
  • 515:Bits of newisa,Addc operation(comment saidAdd +1 to the reference count.RC_ONEisOne wants to shift to the left 56, whileExtra_rc of isa Pointers: reference count,Between 63-56. Note: inx86Below)
  • 519:A carry indicates an overflowAt this timeIndicates that extra_rc is no longer stored in an ISA pointer.
  • 520-521:If overflow is not handledIn line 520Empty bits, called again on line 521rootRetainFor the time of,HandleOverflow is set to true by rootRetain_overflow. So you go straight down
  • 525-529:525 linesLock sideTableThat line is 528Halve the reference count(RC_HALF is 1 shifted 7 to the left, extra_rc is full 8 bits, less than half),Continues to exist in ISA529 line,Has_sidetable_rc is set to true, indicating thatBorrowed sideTable storage
  • Because it says that if it spills, it willTranscribeToSideTable set to true. So ifOverflow will come in.
  • Overflow the reference count above,Half is in isa Pointers, and the other half is in sideTable.
  • 538: ifTryRetain to false.Lock the SideTable, it isunlock.
  • 539: return

From the above explanation we can determine a few problems: 1. Calling rootRetain causes the reference count to + 1.2. When the reference count overflows, half the reference count is stored in EXTRA_RC of ISA and the other half in sideTableWe talked about propertiesDirect copy and strong modifierProperty assignmentCall rootRetain,Assigning an object copy and a strong modifier result in a reference count of +1.becauseStrong is a strong reference, so +1And theCopy modifies property, justCall copyWithZoneAnd theCopy immutable objectsIs theCopy memory addresses, that is,This is also pointing to memory, so +1 is required.To see belowWe see that the memory address is the same.

SideTables hash

Above we said that when reference counts are stored to a certain value, they are not stored in extra_RC in the ISA pointer. Instead, half of them are stored in the SideTables hash table.

The reason:If they are stored in the hash table, each operation on the hash table needs to be unlocked. This operation takes time and consumes high performance. Therefore, the 50/50 operation aims to improve performance.sidetable_addExtraRC_nolockFound forSideTablefromSideTablesTake, descriptionSidetables are multiple tables

Question 1. Why are there many files in memory? What is the maximum number? 六四屠杀

  • ifA hash table has only one tableThat means thatGlobal all objectswillStored in a table, the operationAny object, will be carried outunlockA lock locks the read and write of the entire table. whenThe lock, other objects may also operate on the table, meansData insecurity
  • ifEach object opens a tableWill,Cost performanceSo alsoYou can't have an infinite number of tables

Let’s look at the SideTable structure, the underlying implementation of SideTables

We find that sideTable contains mutex SLOCK, reference counting table Refcnts, and a weak reference table Weak_table. SideTables is obtained by get method of SideTablesMap. The SideTablesMap is defined by StripedMap

. Take a look at the StripedMap source code

As you can see, there can only be a maximum of eight hash tables in the real machine at any one time

Question 2. Why are hash tables used instead of arrays and linked lists?

  • An array of: The characteristics lie inEasy to query (that is, access by subscript).It is troublesome to add and delete(Similar to what we talked about beforemethodList.Add and delete memcopy and memmove, very troublesome), so the properties of arrays areFast to read, but not easy to store
  • The list: The characteristics lie inAdd or delete convenient.Slow query(fromThe head node starts traversing the query), so the properties of linked lists areFast storage, slow reading
  • Hash table: Its essence is oneHash tableThe hash tableBrings together the best of arrays and linked lists.Add, delete, change and check are more convenient, e.g.Zip hash tableIn the previous lock article, we talked about thistlsThe storage structure ofZip formIs the most commonly used linked list

The copy of the object above, strong (retain is the same, both call the retain() method, combined with the little object above. What do we summarize on retain()

What does Retain do in summary

  • 1.retainIt’s going to be at the bottom firstCheck whether it is Nonpointer ISAIf theIf no, the hash table is directly manipulated to perform the +1 operation
  • 2. IfIs Nonpointer isa, you also need toDetermine if the release is in progressIf theThe dealloc process is executed, free the weak reference table and reference count table, and finally free the object memory
  • 3. IfNonpointer ISA does the normal reference count +1 if it is not being released. One thing to notice here is,Extra_rc has only 8 bits to store reference count values on the real machineWhen the storageFull of, need helpHash tables are used for storage. You need toFull extra_rc is split in half.Half (2^7) is stored in the hash table.The other half is still stored in extra_RCUsed of conventionalThe +1 or -1 operation of the reference count, and then back again

Release source code analysis

The +1 operation is analyzed above. The -1 operation is analyzed below: release. Let’s look at the underlying implementation of release abovereallySetPropertyFinally, the old values are evaluatedobjc_release, we went fromobjc_releaseTo start,objc_release->release()->rootRelease()->rootRelease()

It is roughly similar to the rootRetain method above, but in reverse. Here is a brief analysis

  • 1. Determine if it isNonpointer isa, if not, thenPerform -1 directly on the hash table
  • 2. If it isNonpointer isa, theextra_rcIn theThe -1 operation refers to the count valueAnd,Stores the EXTRA_RC state to carry at this timeIn the
  • 3. If the current stateCarray 0, then goUnderflow process
  • 4.underflowThe process has the following steps:
    • judgeHash tableIn theWhether half of the reference count is stored
    • If yes, run theHash tableIn theTake out theStore half of the reference count to proceed1 the operationAnd thenStore it in extra_RC
    • If at this timeExtra_rc no value.The hash table is also emptyIs directTo carry on the destructor, i.e.,Dealloc operation, belong toAutomatic trigger

Dealloc source code analysis

Delloc ->_objc_rootDealloc->rootDealloc -> Retain -> Release -> delloc->_objc_rootDealloc

  • 1. According to the conditionsCheck whether there is ISA, CXX, associated object, weak reference table, reference count tableIf theThere is noThe directFree Free memory
  • 2. IfThere are,Enter the object_dispose method

As you can see from the above, the object_Dispose method has several purposes

  • 1. To destroy an instance, perform the following operations
    • 1. Call c++ destructor
    • 2. Delete the associated reference
    • 3. Release the hash table
    • 4. Clear the weak reference table
  • 2. Free Releases memory

At this point, retain -> Release -> dealloc is all concatenated

RetainCount source code analysis

RetainCount is mentioned above. Let’s look at how retainCount works at the bottom. Let’s look at an interview question first Q: What is the print result? Answer: 1. Why? If you say reference count +1 because NSObject was alloc, then you’re saying the right result, but you don’t know why

In the articleUnderstanding allocIn theNever said that Allock would add +1 to the reference count. So why is the answer 1? Let’s analyze it Above is the retainCount source code, we at rootRetainCount interrupt point, for debugging

The object created by alloc actually has a reference count of 0. Its reference count prints as 1 because the underlying rootRetainCount method has a reference count of +1 by default, but only reads the reference count, not writes. To prevent objects created by alloc from being freed (a reference count of 0 would be freed), the underlying program defaults to +1 at compile time. In fact, the reference count in EXTRA_RC is still 0

conclusion

  • 1.allocObject createdThere is no retain and release
  • 2.allocObject creatingThe reference count is 0, can be inCompile time, the programThe default add 1, so the reference count is read as 1

extension

Let’s look at the above. CPP file finds the following:We seeEach property has a get method and a set method

Let’s explain what the red box means. The red box is the signature. Take @16@0:8 for example.

  • The first @ is the return value of type ID.
  • 16 indicates that the return value is 16 bytes.
  • The second @ is the first argument
  • Zero means we start at zero and go to eight.
  • : indicates the SEL method number
  • 8 refers to 8 to 16

“v24@0:8@16”, where v indicates that no value is returned

For easy understanding, I have attached a picture of the official explanation, as well as the official link, you can go to the official for more detailed explanation.The attached:Type Encoding- Official document ,Property Type String- Official documentation