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

In the previous article, we introduced the memory management of small objects, copy and strong, and introduced the low-level implementation of Release and Dealloc. In this article, we continue to explore AutoReleasePool in memory management. Exploring AutoReleasePool also takes a look at nsRunloops that are closely associated with AutoReleasePool.

AutoReleasePool Automatic release pool

Automatic release tankisOCA kind ofAutomatic memory reclamation mechanismIn theMRCYou canUse AutoReleasePool to delay the release of memoryIn theARCYou canUse AutoReleasePool to add objects to the nearest automatic release pool.No immediate release, will wait untilRunloop dormancyorOut of autoreleasepool scope {}beforeBe released. It can be represented by the following figure

  • 1. From the programStart until loading is complete.The main threadThe correspondingrunloopWill beA dormant state.Waiting for user interactiontoWake up the runloop
  • 2. The user’sEvery interactionwillStart runloop onceforTo deal withOwnership of the userClick on the,Touch eventsEtc.
  • 3.runloopinListening to thetoInteraction eventsLater, you will getCreate an automatic release poolAnd will allDelay the release oftheObject added to the automatic release pool
  • 4. In aBefore the complete runloop endsAnd try toAutomatic release tankIn theAll objects send a Release messageAnd thenDestroy the automatic release pool

C + + analysis

We write the following code in main.mTo C++ code:

From the above we know that @Autoreleasepool is converted to __AtAutoreleasePool, which is a structure. The __AtAutoreleasePool structure is defined as follows:

As you can see from the above figure, this structure provides two (important) methods: 1.objc_autoreleasePoolPush 2.objc_autoreleasePoolPop.

From the above picture, we can know a few things:

  • 1. This structure hasConstructor + destructorThe structureDefined objectinEnd of scopeAfter,Automatically invoke the destructor
  • Of 2.{} is scopeThe advantage is thatThe structure is clear.readable, you canCreate destroy in time

To see when and how construction and destructor are called, we can write the following code:

The destructor will be called automatically when LjTest is created, and when LjTest is out of the {} scope

Assembly analysis

We add breakpoints to main.mRun the program, to the breakpoint, open assembly debuggingThe results of CLANG analysis are also obtained through debugging.

Underlying source code analysis

In objC source code, AutoreleasePool is described as follows:By description:

  • 1.Automatic release tankIt’s a story aboutThe stack structure of Pointers
  • 2. Among themPointer to theisPoint to the freed objectorPool_boundary sentry(Now often referred to asThe border)
  • 3.Automatic release tankIs aThe structure of the page(virtual memory mentioned), and thisA page is a bidirectional linked list(said to haveThe parent nodeandChild nodes, mentioned in the class, i.e. the inheritance chain of the class.
  • 4.Automatic release tankandThreads are related

From the above description of the automatic release pool, we know several directions of our research:

  • 1.When is the automatic release pool created?
  • 2.How are objects added to the auto-release pool?
  • 3.Which objects will be added to the automatic release pool?

With these questions in mind, we set out to explore the underlying principles of automatic release pools

AutoreleasePoolPage analysis

From the initialclangorAssembly analysisWe get itUnderlying the auto release pool is the calltheobjc_autoreleasePoolPushandobjc_autoreleasePoolPop, their source code is as follows:From the source we can find, areCall the AutoreleasePoolPagethePush and POP implementationsHere is its definition The following conclusions can be made from the above:

  • 1.Automatic release tankIs apageAnd at the same timeAn objecttheThe page size is 4096 bytes
  • 2. From its definition,AutoreleasePoolPageisinheritanceSince theAutoreleasePoolPageData, and the attributes of this class also come from the parent class, as followsAutoreleasePoolPageDataThe definition of

AutoreleasePoolPage -> AutoreleasePoolPageData -> AutoreleasePoolPage -> AutoreleasePoolPage From here, it can be shown that the autofree pool is not only a page, but also a bidirectional linked list structure

  • Of 1.AutoreleasePoolPageData structuretheThe memory size is 56 bytes
    • Attribute magictheThe type is the magic_t structure, the memory size occupied by ism[4]; Memory occupied (i.e4 * 4 = 16 bytes)
    • Attribute next (pointer),Thread (object),Parent object,ChildAre accounted for8 bytes(i.e. 4*8=32 bytes)
    • attributedepth,hiwatA type ofuint32_t, the actual type isUnsigned int, all of4 bytes(i.e. 2*4=8 bytes)

The structure of an empty AutoreleasePoolPage is as follows:

Objc_autoreleasePoolPush source code analysis

Enter the source code implementation of push:There is the following logic:

  • 1. Judge firstWhether a pool exists
  • 2. If no, passautoreleaseNewPageMethod to create
  • 3. If yes, passautoreleaseFastThe pressure of stackThe sentry object

AutoreleaseNewPage create pages

Take a look at how autoreleaseNewPage creates a page With the code implementation above (more on autoreleaseFullPage), we know a conclusion

  • 1. The judgeWhether the current page exists
  • 2. IfThere arethroughautoreleaseFullPagemethodsPressure stack object
  • 3. IfThere is nobyautoreleaseNoPagemethodsCreate a page
    • autoreleaseNoPageAccording to the method,The current threadtheAutomatic release tankIs through theAutoreleasePoolPage createOf (Line 973)
    • AutoreleasePoolPagetheA constructorisThis is done by implementing the initialization method of the parent class AutoreleasePoolPageData
AutoreleasePoolPage

The above saidAutomatic release pool for the current threadIs through theAutoreleasePoolPage createAnd seeAutoreleasePoolPageConstruction method: Among themAutoreleasePoolPageDataThe meaning of the argument passed to the method is:

  • begin()saidThe pressure of stackThat is, nextThe pushaddress of the object to release). Debugging can be done through source codebeginAnd find that its concrete implementation is equal toHeader address +56, in which the56isMemory size of AutoreleasePoolPageData structure

Let’s look at AutoreleasePoolPage initialization again

  • 1.objc_thread_self()Is saidThe current thread, and the current thread isObtained through TLS
  • 2.NewParent indicates the parent node
  • 3. The next two parameters areDepth through the parent node,Maximum number of stacksTo calculatedepthAs well ashiwat

View automatic free pool memory structure

Cannot be called manually due to ARC modeautorelease, so switch the Demo to MRC mode (Build Settings -> Objectice-C Automatic Reference CountingSet toNO) Write the following code:Running results:

After running the results, we see that there are six releases, but we have five pushobjects, where the POOL represents the sentinel, or boundary, which is intended to prevent transgressions. If we look at the printed address, we see that the first address of the page is 0x38 different from the sentinel object, which in decimal form is exactly 56, which is also the memory size of AutoreleasePoolPage itself. Let’s change the number of loops to 505 and run it again

From the figure above we can see that the first page is full, storing 504 objects to be released, and the second page only stores one. Let’s change the loop number to 1015 and see if we can only store 504 objects on a page

The first page stores 504, the second page stores 505, and the third page stores 6

From the above, we can draw the following conclusions:

  • 1. The first page can be stored504 objects, and onlyThere are sentries on the first pageWhen one page is full a new page is opened
  • 2. At the beginning of page 2,A maximum of 505 objects can be stored
  • The size of a page is equal to505 times 8 is 4040

That’s what we did beforeAutoreleasePoolPagetheSIZEYes, I said, the size of a page is zero4096 bytes, and in its constructorObject to pushFrom theStart at +56So it can be in one pageThe actual storage can be 4096-56 = 4040 bytesIs converted to the object4040/8 is equal to 505A maximum of one page can be stored505 objects“, the first page of whichThere are sentinel objectsonlyStore 504. Its structure diagram is as follows:It can be known from the above:

  • 1.An autorelease pool has only one sentinel objectAnd,The sentinel is on the first page
  • 2.First page maximumYou can save504Object, start of page 2Up to 505

summary

  • 1.autoreleasepoolIts essence is aStructure object, aAutomatically release pool objectsispage, it isStack storage, in line with theAfter the advancedThe principle of
  • 2.The bottom of the stack of pagesIs a56Byte sizedAn empty placeholder, aThe total page size is 4096 bytes
  • Only 3.The first page has sentinel objects.A maximum of 504 objects can be stored, fromStart of page 2Most storage505 objects
  • 4.autoreleasepoolinAdds the object to be released, the underlying call isobjc_autoreleasePoolPusH method
  • 5.autoreleasepoolinCall the destructor to releaseWhen, the internal implementation is calledobjc_autoreleasePoolPopmethods

The autoreleaseFast object is pressed

AutoreleaseFast:Mainly divided into the following steps:

  • 1. Obtain the current operation page and determine whether the page exists and is full
  • 2. IfPage there.And underbyaddmethodsThe pressure of stackobject
  • 3. If the pagePresent and fullbyautoreleaseFullPageMethod to schedule a new page
  • 4. IfPage does not existbyautoreleaseNoPageMethod to create a new page

AutoreleaseFullPage method

View source code:

If the current page is full, do the while loop to find the corresponding page of the child node. If not, create a new page and push the object. By pointing the current page’s child to the new page, we can conclude that the pages are connected by a two-way linked list.

The add method

View source code:

This method is mainly to add the release object, its underlying implementation is to release the object through the next pointer store, and next pointer increment, indicating the next location of the release object store. You can see from this that pages are stored in a stack structure

Objc_autoreleasePoolPop source code analysis

inobjc_autoreleasePoolPopThe method has an argument, and when clang is analyzed, the argument passed in isSentinel object returned after push, i.e.,ctxt, its purpose isAvoid stack clutter.Prevents other objects from being pushed off the stackAutoreleasePoolPage is a pop method that calls AutoreleasePoolPagePop source code implementation, mainly by the following steps:

  • 1. Empty page processing, andObtain the page based on the token
  • 2. Fault tolerant processing
  • Through 3.popPagePage out of the stack

View the popPage source code

AllowDebug is false. Release until all objects in the stack up to the current page stop position are released, that is, a release message is sent to objects in the stack until the incoming sentry object is encountered.

ReleaseUntil source

Look at the source we can know:

  • 1.releaseUntilRealization, mainly throughTo iterate over, determines whether the object is equal tostop, its purpose isBefore releasing stopAll objects of
  • 2. First through acquisitionPage next frees the object (that is, the last object of the page), and thenextfordiminishingTo obtainPrevious object
  • 3. The judgeIs it a sentinel objectIf not, it is automatically calledobjc_releaseThe release of

Kill source code implementation

The kill implementation basically destroys the current page, assigns the current page to the parent page, and sets the child object pointer of the parent page to nil

conclusion

Based on the above analysis, the auto-release pool push and POP are summarized as follows

  • In the auto release poolThe pressure of stack(i.e.push) in the operation
    • The page is created when there is no pool, that is, only empty placeholders (stored in TLS),Push the sentinel object
    • In the pagePush ordinary objectsMainly throughnextPointer to theincreasingthe
    • whenPage fullYou need to set the page’schildThe object ofA new page

So, to sum up,objc_autoreleasePushThe overall underlying process is shown in the figure below

  • In the auto release poolOut of the stack(i.e.pop) in the operation
    • In the pagePush ordinary objectsMainly throughNext pointer decrement,
    • whenEmpty pageWhen, need to assign pageparentObject is the current page

In summary, the flow of objc_autoreleasePoolPop off the stack is as follows

Runloop

Runloop source code download address portal

RunLoop and thread relationship

Runloops cannot be created and can only be fetched. There are currently two ways to fetch threads:

CFRunLoopGetMain source

_CFRunLoopGet0 source

And as you can see from this,There are only two types of Runloop, one isThe main threadOne of them isOther threads. namelyRunloop and threadisOne to one correspondencethe

The RunLoop create

Through the top_CFRunLoopGet0You can knowRunloopIs through the__CFRunLoopCreate create(System creation, developers can not create their own try). Let’s see__CFRunLoopCreateSource:

We find that __CFRunLoopCreate is primarily an assignment to the runloop attribute. Let’s look at the CFRunLoopRef in line 1321

  • 1. By definition, actuallyRunLoopIs also aobject. is__CFRunLoop structurethePointer types
  • 2.A RunLoop depends on multiple modes, means aRunLoop needs to handle multiple transactions, i.e.,A Mode corresponds to multiple itemsAnd theAn itemThat contains thetimer,source,observer, can be illustrated by the following figure

Mode type

There are five modes mentioned in Apple documents, while only NSDefaultRunLoopMode and NSRunLoopCommonModes are exposed publicly in iOS. NSRunLoopCommonModes is actually a collection of the Mode, including NSDefaultRunLoopMode and NSEventTrackingRunLoopMode by default.

  • NSDefaultRunLoopMode:The defaultMode, normally run under this model (Including the main thread)
  • NSEventTrackingRunLoopMode (cocoa):Tracking modeUse this mode to goTracking events from user interactions(For example, UITableView slides up and down smoothly, in order not to be affected by other modes.) . UITrackingRunLoopMode(iOS)
  • NSModalPanelRunLoopMode:Handle the Modal Panels event.
  • NSConnectionReplyMode: Handles events related to NSConnection objects,System internal use, users will hardly use it
  • NSRunLoopCommonModes: This is onePseudo mode, which isA set of run loop modes, adding input sources to the mode means thatAll Modes included in Common ModesYou can handle everything. inCocoa applications.The defaultcasesCommon Modes include default Modes,modal Modes, and Event Tracking Modes. Can beUse the CFRunLoopAddCommonMode method to add custom Modes to Common Modes.

Source & Timer & Observer

  • SourceCan be saidWake up some events of RunLoopFor example, when the user clicks on the screen, a RunLoop is created, which is divided into Source0 and Source1
    • Source0saidNon-system event, that is, user-defined events
    • Source1saidSystem events, the mainResponsible for the underlying communications.Have the ability to wake up
  • TimerIs commonly usedNSTimer timerThis kind of
  • ObserverIt is mainly used forListen for state changes in the RunLoopAnd makeA certain response, there are mainly the following states

validation

RunLoop and mode are one-to-many

We talked about that aboveRunLoop and mode are one-to-manyBelow we run the code to prove the actual operation. Let’s go throughLLDB commandTo obtainmainRunloop,currentRunloopthecurrentMode

Runloop has only one mode at run time

Next we get all the models of mainRunLoop

It can be verified from the above print that runloop and CFRunloopMode have a one-to-many relationship

Mode and Item are also one-to-many

We continue at the breakpoint and look at the stack information through bt, where we can see that the item type of the timer is as follows (intercept)To check the Item type in the RunLoop source, there are the following types:

  • Block application:__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__
  • Call timer:__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__
  • Response source0:__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__
  • Source1 response:__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__
  • GCD main queue:__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__
  • The observer source:__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__

Let’s start withThe Timer for exampleGenerally, when initializing the timer, the timer is passedaddTimer:forMode:Method add toRunloop, so in the source code to findaddTimer, namelyCFRunLoopAddTimerMethod, the source code implementation is as follows

  • 1. The main judgment of its realizationWhether kCFRunLoopCommonModesAnd thenFind the mode of runloop for a matchTo deal with
  • Of 2.KCFRunLoopCommonModes is not a mode, it is a kind ofAbstract pseudo-patterns.More flexible than defaultMode
  • Through 3.CFSetAddValue(rl->_commonModeItems, rlt); As you can see,Runloop and mode are one-to-manyAt the same timeMode and item are also one-to-manythe

RunLoop perform

As we know, RunLoop execution depends on the run method, and as you can see from the stack below, the __CFRunLoopRun method is executed underneathGo to __CFRunLoopRun source code:The __CFRunLoopRun source code shows that different objects are handled differently

  • If you haveobserverThe call__CFRunLoopDoObservers
  • If you haveblockThe call__CFRunLoopDoBlocks
  • If you havetimerThe call__CFRunLoopDoTimers
  • If it issource0The call__CFRunLoopDoSources0
  • If it issource1The call__CFRunLoopDoSource1

_ _CFRunLoopDoTimers

Check out the __CFRunLoopDoTimers source code

A single timer is processed through the for loop, and the __CFRunLoopDoTimer source code is entered below

After the timer is executed, the __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ function will be called, which is exactly the same as that in the timer stack call

The Timer to summarize

  • 1.Custom timer.Set the ModeAnd should beJoin the RunLoopIn the
  • 2. The RunLooprunMethod is called when it executes__CFRunLoopDoTimersExecute all timers
  • In 3.__CFRunLoopDoTimersMethod, will passThe for loop executes a single timerThe operation of the
  • In 4.__CFRunLoopDoTimerMethod, the corresponding timer callback function is executed after the timer is completed

Above, is aimed attimerThe execution analysis forObserver, block, source0, source1, its execution principle is similar to that of timer, which will not be repeated hereApple Official DocumentationHandles diagrams of different sources for RunLoop

RunLoop underlying principles

As can be seen from the stack information above, the underlying implementation path of RUN isCFRunLoopRun -> CFRunLoopRun -> __CFRunLoopRunEnter the CFRunLoopRun source code, where the parameter 1.0e10 (scientific count) is equal to 1* e^10, used to representtimeout CFRunLoopRunSpecific CFRunLoopRunSpecific

  • First of all, according to theModeName finds the corresponding mode, and then divided into three main cases:
    • If it isentry,Notice the observer, that is,Enter the runloop
    • If it isexit,By the observer, that is,Exit the runloop
    • If it isOther intermediate states, mainlyProcess various sources through runloop

__CFRunLoopRun is called. This is handled differently depending on the source of the event. When the RunLoop is asleep, the RunLoop can be woken up by the corresponding event.

conclusion

In summary, the execution flow of RunLoop

AutoreleasePool related interview questions

Relevant surface attempt

When are temporary variables released?

  • 1. If thenormalDown, generallyIt is released immediately when it is out of scope
  • 2. If temporary variables are addedAutomatic release tank, will delay the release, i.eRunloop sleep or autoreleasepool release after scope

AutoreleasePool principle

  • 1.Automatic release tankthenatureIs aAutoreleasePoolPage structure object, it is aThe stack structure stores pages, each oneAutoreleasePoolPageAre based onBidirectional linked list form join
  • 2.Automatic release tanktheThe pressure of stackandOut of the stackMainly throughThe constructor of a structureandDestructor callsWith the underlyingobjc_autoreleasePoolPudhandobjc_autoreleasePoolPopIn fact,Call the push and POP of AutoreleasePoolPageTwo methods
  • Every time 3.Call pushIn fact, iscreateaNew AutoreleasePoolPageAnd theAutoreleasePoolPageThe specificoperationisInsert a POOL_BOUNDARYAnd,Returns the memory address where POOL_BOUNDARY was inserted. whilepushinternalCall autoreleaseFastMethods: There are three main cases
    • whenThe page isAnd,discontentWhen,Call the add methodwillObject to the next pointer of the page.And next increment
    • whenThe page isAnd,Is fullWhen,Call autoreleaseFullPage to initializeaA new pageAnd thenCall the add method to add the object to the page stackIn the
    • whenPage does not existWhen,Call autoreleaseNoPage to create a hotPageAnd thenCall the add methodwillObject added to the page stack
  • 4. WhenPerform popwhenPassing in a valuetheThe value is the return value of the push operation, i.e.,POOL_BOUNDARY Specifies the memory address token. sopopThe internal implementation isFind the page where the sentinel object is located based on the tokenAnd thenUsing objc_releaseThe release oftokenBefore the object, and putThe next pointer is in the correct position

Can AutoreleasePool be nested?

  • 1. Can be nested, its purpose is to be able toControl the application's peak memoryMake it not too high
  • 2. Nesting is possible becauseThe auto-release pool is a stack nodeThrough theBidirectional linked list form joinAnd isOne to one for threadsthe
  • 3. Automatically release the poolMultilayer embeddedCondoms are just non-stopPushs sentinel objectIn pop, the inside is released first and the outside is released

Which objects can be added to AutoreleasePool? Alloc creation ok?

  • 1. InUnder the MRCuseNew, alloc, copyKeyword generated object andretainThe object ofManual release is requiredWill not be added to the automatic release pool
  • In 2.Under the MRCSet toautoreleaseThe object ofManual release is not requiredWill,Go directly to the automatic release pool
  • All 3.autoreleaseObject, inOnce it's out of scope, will beAutomatically adds to the recently created auto release poolIn the
  • In 4.Under the ARCOnly need toFocus on reference CountingBecause the creation is done inMain thread runningThe,The system automatically creates an AutoreleasePool for the main thread, soThe creation is automatically placed in the automatic release pool

When is AutoreleasePool released?

  • 1. After the App starts, Apple is in the main threadRunLoopTwo of them are registered inObserver, its callback is_wrapRunLoopWithAutoreleasePoolHandler().
  • 2. The first event monitored by the Observer isEntry(about to enter Loop), which is called within its callback_objc_autoreleasePoolPush(a)Create an automatic release pool. Its order is -2147483647,Highest priorityTo ensure that the release pool is created before any other callbacks.
  • 3. The second Observer monitors two events:BeforeWaiting(Ready to enter hibernation)Calls to _objc_autoreleasePoolPop() and _objc_autoreleasePoolPush() free the old pool and create a new one;Exit(that is, will exit the Loop)_objc_autoreleasePoolPop() torelease the automatic release pool. The order of this Observer is 2147483647,Lowest priorityTo ensure that its release pool occurs after all other callbacks.

Relationship between Thread and AutoreleasePool

inThe official documentation, find the following descriptionThe understanding is as follows:

  • 1.Each thread, including the main thread is maintainedOwn autofree pool stack structure
  • 2. A new auto release pool is added to theTo the top of the stack; It is removed from the stack when the autofree pool is destroyed
  • (3) forThe current threadSay, willAutomatic release of the pairLike in theAutomatically release the stack top of the pool; inThread to stopwhenAutomatically releases all automatic release pools associated with this thread

Summary: Each thread has an autofree pool stack structure associated with it. New pools are pushed to the top of the stack when they are created, pools are removed when they are destroyed, free objects are pushed to the top of the stack for the current thread, and the associated autofree pool is automatically released when the thread stops

Relationship between RunLoop and AutoreleasePool

inThe official documentation, find the following descriptionThe understanding is as follows:

  • 1. The main programRunLoopIn every timeEvent loopBefore,Automatically create an autoreleasePool
  • 2. And will be inEnd of event loopWhen,Perform drainOperation,Release the objects in it

RunLoop interview questions

The interview questions a

There’s a child thread, and there’s a timer in the child thread. Can the timer execute and print continuously?【 answer 】 : No, becauseRunloops for child threads do not start by default, you need torunloop runTo start, change the above code to the following:

RunLoop and thread relationship

  • 1.Each threadThere areA corresponding RunLoop, soRunloops correspond to threads one by one, its binding relationship through aGlobal DIctionary storage, the thread is key, and runloop is value.
  • 2.RunLoop in threadmainlyTo manage threadsWhen theThe thread RunLoop is enabledAfter, will be inAfter the missionforA dormant statewhenAn event triggered the wake upAgain,Start to work.Work while there is workRest if you don’t live
  • 3.The main threadtheRunLoopisThe default openAfter the program is started, it will always run, and will not exit
  • 4.RunLoop for other threadsThe default isDon't openIf necessary, enable it manually

NSRunLoop is different from CFRunLoopRef

  • 1.NSRunLoopisObject-oriented API based on CFRunLoopRef, it isunsafethe
  • 2.CFRunLoopRefisBased on C language, it isThread safetythe

What is the mode of Runloop?

  • Mode is mainly used to specify the priority of events in RunLoopthe

The interview questions five

To the + scheduledTimerWithTimeInterval: way to trigger the timer, when sliding on the page list, the timer will suspend the callback, why? How to solve it?

  • 1. The timer stops becauseWhen the sliding scrollView, the main thread RunLoop willSwitch from NSDefaultRunLoopMode to UITrackingRunLoopModeAnd timer is added inNSDefaultRunLoopMode. So the timer doesn’t execute
  • 2.timerIn theNSRunLoopCommonModesPerformed in the

Wrote last

Write more content, due to my limited ability, some places may explain the problem, please be able to point out, at the same time to the memory management questions, welcome everyone to leave a message, also hope that you like a lot of support. I hope that we can communicate with each other, explore and make progress together!