MRC related

Object Setter method

- (void)setName:(NSString *)name {

    if(_name ! = name) {/ / 1

        [_name release]; / / 2

        _name = [name retain]; / / 3

    }

}

Copy the code
  • In MRC mode, setter methods are handled as follows:
  1. Prevent the duplicate _name from being released prematurely, causing the zombie object to crash.
  2. The old object _name is released in advance to prevent memory leaks.
  3. Add one to the retain reference count for object name to prevent use errors due to object release.

Object deep copy and shallow copy


  • The copy method of immutable objects performs a shallow copy operation, while the copy method of mutable objects performs a deep copy operation.
  • The mutableCopy method for immutable objects performs a deep copy operation, while the mutableCopy method for mutable objects performs a deep copy operation.

Objects use the copy or strong modifier

@property (nonatomic.copyNSMutableArray *array;

@property (nonatomic.strongNSMutableArray *array;

Copy the code
  • The essential difference between these two lines of code is what is done inside the setter method:
    • The copy modifier property, which executes the [array copy] method in setter methods, causes a mutable array to become an immutable array;
    • Property of the strong modifier, which executes the [array retain] method in setter methods and simply increments the reference count to protect the current object from being released;
    • The usual string manipulation is copy, and mutable objects are decorated with strong!!

autoreleasepool

Automatic release tank

int main(int argc, const char * argv[]) {

    @autoreleasepool {



    }

    return 0;

}

Copy the code

Autoreleasepool into the underlying C++ source code, you can know that the underlying structure is __AtAutoreleasePool constituted!

struct __AtAutoreleasePool {

__AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush(); }

~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj); }

  void * atautoreleasepoolobj;

};

Copy the code

AutoreleasePoolPage structure

class AutoreleasePoolPage

{

    static size_t const SIZE = PAGE_MAX_SIZE;  / / 4096

    static size_t const COUNT = SIZE / sizeof(id);

    magic_t const magic; // Verify AutoreleasePoolPage integrity

    id *next; // point to the next address where the AutoRelease object will be stored

    pthread_t const thread; // The current page thread

    AutoreleasePoolPage * const parent; // The parent pointer to the current page

    AutoreleasePoolPage *child; // Pointer to the child of the current page

    uint32_t const depth;

    uint32_t hiwat;

}

Copy the code
  • The maximum memory available for AutoreleasePoolPage is 4096 bytes, and 56 bytes for variables!
  • AutoreleasePoolPage is a two-way linked list of data structure storage method, each page through the parent and child link to each other, you can use the list lookup method to find the object you want torelease!

The principle of

  • Assuming that the address starts from 0x1000 to 0x2000, the total memory space occupied is 4096 bytes, among which the byte size from variable to 0x1038 is 56 bytes.
  • If the variable size in AutoreleasePoolPage is greater than 4096 bytes, a new page will be generated to continue storing the object.

The runtime source

static inline void *push()

{

     id *dest;

     if (DebugPoolAllocation) {

         // Create a new page

         dest = autoreleaseNewPage(POOL_BOUNDARY);

      } else {

        // Get the page and create a new one if there is no page or if the page is full

         dest = autoreleaseFast(POOL_BOUNDARY);

      }

      return dest;

}

static inline id *autoreleaseFast(id obj)

{

     AutoreleasePoolPage *page = hotPage();

     if(page && ! page->full()) {// add obj to hot page

         return page->add(obj);

     } else if (page) { // Create a new page to push the obj object onto the stack

         return autoreleaseFullPage(obj, page);

     } else { // Create a new page if there is no page

         return autoreleaseNoPage(obj);

     }

}



// Page is already full, get unfilled page or create a new page

id *autoreleaseFullPage(id obj, AutoreleasePoolPage *page)

{

      do {

          if (page->child) page = page->child; // Get the last unfilled page

          // Create a new page

          else page = new AutoreleasePoolPage(page);

      } while (page->full());

      setHotPage(page);

      return page->add(obj);

}



// Create a new page and push obj to the stack

id *autoreleaseNoPage(id obj)

{

      // initialize page and pass POOL_BOUNDARY as the first object at the bottom of the stack

      AutoreleasePoolPage *page = new AutoreleasePoolPage(nil);

      setHotPage(page);

      if (pushExtraBoundary) {

          page->add(POOL_BOUNDARY);

      }

      // push the obj object onto the stack

      return page->add(obj);

}



// Get a new page

id *autoreleaseNewPage(id obj)

{

     AutoreleasePoolPage *page = hotPage(); // Get the page being used

     if (page) return autoreleaseFullPage(obj, page); // Get a new page

     else return autoreleaseNoPage(obj); // Initialize the page

}

Copy the code
  • If there is no current page, create a new page and push the POOL_BOUNDARY as the first object on the stack. Add the obj object to the page.
  • If a page exists and the page is not full, the current obJ pair is put on the stack. If the current page is full, create a new page and push the POOL_BOUNDARY as the first object on the stack. Add the obj object to the page.
static inline void pop(void *token)

{

      AutoreleasePoolPage *page;

      id *stop;

      page = pageForPointer(token); In which page is the token found based on the object token

      stop = (id *)token;

      page->releaseUntil(stop); // Release objects from hotpage until stop

      // Set page to nil depending on the case

      if (DebugPoolAllocation  &&  page->empty()) {

          AutoreleasePoolPage *parent = page->parent;

          page->kill();

          setHotPage(parent);

      } else if(DebugMissingPools && page->empty() && ! page->parent) {

          page->kill();

          setHotPage(nil);

      }

      else if (page->child) {

          if (page->lessThanHalfFull()) {

              page->child->kill();

          }

          else if (page->child->child) {

              page->child->child->kill();

          }

      }

}

Copy the code
  • Call POP and pass in the token object. The variable page will find which page is in, and the object will know the token location, and the excess page will be released.

Object dealloc

The runtime source

- (void)dealloc {

    _objc_rootDealloc(self);

}

_objc_rootDealloc(id obj)

{

    obj->rootDealloc();

}

objc_object::rootDealloc()

{

    if (isTaggedPointer()) return;  // Return TaggedPointer directly



    if (fastpath(isa.nonpointer // No bitfields are used to store information &&

! isa.weakly_referenced// Not weakly referenced &&

! isa.has_assoc// No associated object &&

! isa.has_cxx_dtor// There is no destructor &&

! isa.has_sidetable_rc// Reference count is not stored in sideTable))

    {

assert(! sidetable_present());

        free(this); // Release the object directly

    }

    else {

        object_dispose((id)this);

    }

}

object_dispose(id obj)

{

    objc_destructInstance(obj);

    free(obj);

}

void *objc_destructInstance(id obj)

{

    if (obj) {

        bool cxx = obj->hasCxxDtor();

        bool assoc = obj->hasAssociatedObjects();

        if (cxx) object_cxxDestruct(obj);

        if (assoc) _object_remove_assocations(obj);

        obj->clearDeallocating();

    }



    return obj;

}

Copy the code
  • object_cxxDestruct:

    • The object associated with the obj object being released was released.
    • A superclass related object is released.
  • _object_remove_assocations:

    • Remove the assocation object associated with the current object from the AssociationsHashMap hash table.
  • ClearDeallocating:

    • If the object is weakly referenced by other objects, the weak reference object is set to nil, and the current object is deleted from the Weak_table weak reference table.
    • Remove the object from sideTable if the current object has reference counts stored in the sideTable.

Resources: Explore dealloc