Runtime issues

  • Interview questions from the article “Ali, Byte: A Set of Effective iOS Interview Questions”
  • Github address is updated more frequently
  • Debug objC-Runtime source code, objC4;

Structural model

1. Introduce the runtime memory model (ISA, object, class, metaclass, structure storage information, etc.)

2. Why design Metaclass

3. Class_copyIvarList and class_copyPropertyList difference

Class_copyIvarList gets all the instance variable information in the class object, from class_ro_t:

Ivar *
class_copyIvarList(Class cls, unsigned int *outCount)
{
    const ivar_list_t *ivars;
    Ivar *result = nil;
    unsigned int count = 0;

    if(! cls) {if (outCount) *outCount = 0;
        return nil;
    }

    mutex_locker_t lock(runtimeLock);

    assert(cls->isRealized());

    if ((ivars = cls->data()->ro->ivars)  &&  ivars->count) {
        result = (Ivar *)malloc((ivars->count+1) * sizeof(Ivar));

        for (auto& ivar : *ivars) {
            if(! ivar.offset)continue;  // anonymous bitfield
            result[count++] = &ivar;
        }
        result[count] = nil;
    }

    if (outCount) *outCount = count;
    returnresult; } Duplicate codeCopy the code

Class_rw_t properties, output category/extension/ baseClass properties, and output only the properties of the current class. Instead of looking up the properties defined in superClass.

objc_property_t *
class_copyPropertyList(Class cls, unsigned int *outCount)
{
    if(! cls) {if (outCount) *outCount = 0;
        return nil;
    }

    mutex_locker_t lock(runtimeLock);

    checkIsKnownClass(cls);
    assert(cls->isRealized());

    auto rw = cls->data();

    property_t **result = nil;
    unsigned int count = rw->properties.count();
    if (count > 0) {
        result = (property_t **)malloc((count + 1) * sizeof(property_t *));

        count = 0;
        for (auto& prop : rw->properties) {
            result[count++] = ∝
        }
        result[count] = nil;
    }

    if (outCount) *outCount = count;
    return(objc_property_t *)result; } Duplicate codeCopy the code

Q1: How about baseProperties in class_ro_t?

Q2: Properties in class_rw_t contains all the properties. When was it injected? The answer to see 5.

4. Difference between class_rw_t and class_ro_t




image

The test found that the properties property in class_rw_T contains properties in the classification/extension/base class in order.

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
#ifdef __LP64__
    uint32_t reserved;
#endif

    const uint8_t * ivarLayout;

    const char * name;
    method_list_t * baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;

    method_list_t *baseMethods() const {
        returnbaseMethodList; }}; struct class_rw_t { // Be warned that Symbolication knows the layout of this structure. uint32_t flags; uint32_t version; const class_ro_t *ro; method_array_t methods; property_array_t properties; protocol_array_t protocols; Class firstSubclass; Class nextSiblingClass; char *demangledName;#if SUPPORT_INDEXED_ISA
    uint32_t index;
#endif} Duplicate codeCopy the code

As a developer, it is particularly important to have a learning atmosphere and a communication circle. This is my iOS communication group: 761407670, enter the group password 000, no matter you are small white or big, welcome to enter, share BAT, Ali interview questions, interview experience, discuss technology, we exchange learning and growth together!

5. How are the categories loaded, the loading order of the load methods of the two categories, and the loading order of the methods with the same name of the two categories

. -> realizeClass -> methodizeClass(used to Attach categories)-> attachCategories key is in methodizeClass method implementation

static void methodizeClass(Class cls) { runtimeLock.assertLocked(); bool isMeta = cls->isMetaClass(); auto rw = cls->data(); auto ro = rw->ro; / / = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = / / ellipsis... // ======================================= property_list_t *proplist = ro->baseProperties;if(proplist) { rw->properties.attachLists(&proplist, 1); } / / = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = / / ellipsis... // ======================================= // Attach categories. category_list *cats = unattachedCategoriesForClass(cls,true /*realizing*/);
    attachCategories(cls, cats, false /*don't flush caches*/); / / = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = / / ellipsis... // ======================================= if (cats) free(cats); } Duplicate codeCopy the code

AttachLists rW -> property.attachLists

property_list_t *proplist = ro->baseProperties;
if(proplist) { rw->properties.attachLists(&proplist, 1); Void attachLists(List* const * addedLists, uint32_t addedCount) {if (addedCount == 0) return;

        if (hasArray()) {
            // many lists -> many lists
            uint32_t oldCount = array()->count;
            uint32_t newCount = oldCount + addedCount;
            setArray((array_t *)realloc(array(), array_t::byteSize(newCount))); array()->count = newCount; /* struct array_t {uint32_t count; List* lists[0]; }; */ memmove(array()->lists + addedCount, array()->lists, oldCount * sizeof(array()->lists[0])); memcpy(array()->lists, addedLists, addedCount * sizeof(array()->lists[0])); }else if(! list && addedCount == 1) { // 0 lists -> 1 list list = addedLists[0]; }else{ // 1 list -> many lists List* oldList = list; uint32_t oldCount = oldList ? 1:0; uint32_t newCount = oldCount + addedCount;setArray((array_t *)malloc(array_t::byteSize(newCount)));
            array()->count = newCount;
            if(oldList) array()->lists[addedCount] = oldList; memcpy(array()->lists, addedLists, addedCount * sizeof(array()->lists[0])); }} Copy the codeCopy the code

So the category property is always first, and the baseClass property is shifted back.

Q1: What about the order of categories? The answer to see 6

Add the process of Category resolution when the application image image is loaded into memory. Note the following while (I -) here flashback will deal method in the category of property added to the rw = CLS – > data () of the methods/properties/separate protocols.

static void 
attachCategories(Class cls, category_list *cats, bool flush_caches)
{
    if(! cats)return;
    if (PrintReplacedMethods) printReplacements(cls, cats);

    bool isMeta = cls->isMetaClass();

    // fixme rearrange to remove these intermediate allocations
    method_list_t **mlists = (method_list_t **)
        malloc(cats->count * sizeof(*mlists));
    property_list_t **proplists = (property_list_t **)
        malloc(cats->count * sizeof(*proplists));
    protocol_list_t **protolists = (protocol_list_t **)
        malloc(cats->count * sizeof(*protolists));

    // Count backwards through cats to get newest categories first
    int mcount = 0;
    int propcount = 0;
    int protocount = 0;
    int i = cats->count;
    bool fromBundle = NO;
    while (i--) {
        auto& entry = cats->list[i];

        method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
        if (mlist) {
            mlists[mcount++] = mlist;
            fromBundle |= entry.hi->isBundle();
        }

        property_list_t *proplist = 
            entry.cat->propertiesForMeta(isMeta, entry.hi);
        if (proplist) {
            proplists[propcount++] = proplist;
        }

        protocol_list_t *protolist = entry.cat->protocols;
        if(protolist) { protolists[protocount++] = protolist; } } auto rw = cls->data(); Preparelists (CLS, mLists, McOunt, NO, fromBundle); prepareLists (CLS, mLists, McOunt, NO, fromBundle); rw->methods.attachLists(mlists, mcount); free(mlists);if(flush_caches && mcount > 0) flushCaches(cls); rw->properties.attachLists(proplists, propcount); free(proplists); rw->protocols.attachLists(protolists, protocount); free(protolists); } Duplicate codeCopy the code

6. Can I add extension to NSObject

category:

  • Add classification properties/protocols/methods at run time
  • Class-added methods “override” the original class method because method lookup is done from start to finish and stops once it is found
  • The namesake classification method is effective depending on the order in which the image is compiled. The information read by image is in reverse order, so the later the image is compiled, the earlier the image is read
  • Classes with the same name will cause compilation errors;

extension:

  • Compile-time resolution
  • Exists only as a declaration, in most cases in.m files;
  • Extensions cannot be added to system classes

7. Compare message forwarding mechanism with message forwarding mechanism of other languages

8. What is done before method query -> dynamic parsing -> message forwarding at method call time

9. Differences and application scenarios of IMP, SEL and Method

Definitions of the three:

typedef struct method_t *Method; using MethodListIMP = IMP; struct method_t { SEL name; const char *types; MethodListIMP imp; }; Copy the codeCopy the code

Method is also an object that encapsulates the Method name and implementation, regarding Type Encodings.

Code Meaning
c A char
i An int
s A short
l A long``l is treated as a 32-bit quantity on 64-bit programs.
q A long long
C An unsigned char
I An unsigned int
S An unsigned short
L An unsigned long
Q An unsigned long long
f A float
d A double
B A C++ bool or a C99 _Bool
v A void
* A character string (char *)
@ An object (whether statically typed or typed id)
# A class object (Class)
: A method selector (SEL)
[

array type

]

An array
{

name=type…

}

A structure
(

name

=

type…

)

A union
bnum A bit field of

num

bits

^type A pointer to

type
? An unknown type (among other things, this code is used for function pointers)

-(void)hello:(NSString *)name encode is v@:@.

10. What is the difference between load and initialize methods? What is the difference between them in inheritance

When the load method is called and only the current class itself is called, the superClass’s +load method is not called:

void
load_images(const char *path __unused, const struct mach_header *mh)
{
    // Return without taking locks if there are no +load methods here.
    if(! hasLoadMethods((const headerType *)mh))return;

    recursive_mutex_locker_t lock(loadMethodLock);

    // Discover load methods
    {
        mutex_locker_t lock2(runtimeLock);
        prepare_load_methods((const headerType *)mh);
    }

    // Call +load methods (without runtimeLock - re-entrant)
    call_load_methods();
}

void call_load_methods(void)
{
    static bool loading = NO;
    bool more_categories;

    loadMethodLock.assertLocked();

    // Re-entrant calls do nothing; the outermost call will finish the job.
    if (loading) return;
    loading = YES;

    void *pool = objc_autoreleasePoolPush();

    do {
        // 1\. Repeatedly call class +loads until there aren't any more while (loadable_classes_used > 0) { call_class_loads(); } // 2\. Call category +loads ONCE more_categories = call_category_loads(); // 3\. Run more +loads if there are classes OR more untried categories } while (loadable_classes_used > 0 || more_categories); objc_autoreleasePoolPop(pool); loading = NO; } Duplicate codeCopy the code

+ the initialize implementation

void _class_initialize(Class cls) { assert(! cls->isMetaClass()); Class supercls; bool reallyInitialize = NO; // Make sure super isdone initializing BEFORE beginning to initialize cls.
    // See note about deadlock above.
    supercls = cls->superclass;
    if(supercls && ! supercls->isInitialized()) { _class_initialize(supercls); } // Try to atomicallyset CLS_INITIALIZING.
    {
        monitor_locker_t lock(classInitLock);
        if(! cls->isInitialized() && ! cls->isInitializing()) { cls->setInitializing(); reallyInitialize = YES; }}if (reallyInitialize) {
        // We successfully set the CLS_INITIALIZING bit. Initialize the class.

        // Record that we're initializing this class so we can message it. _setThisThreadIsInitializingClass(cls); if (MultithreadedForkChild) { // LOL JK we don't really call +initialize methods after fork().
            performForkChildInitialize(cls, supercls);
            return;
        }

        // Send the +initialize message.
        // Note that +initialize is sent to the superclass (again) if 
        // this class doesn't implement +initialize. 2157218 if (PrintInitializing) { _objc_inform("INITIALIZE: thread %p: calling +[%s initialize]", pthread_self(), cls->nameForLogging()); } // Exceptions: A +initialize call that throws an exception // is deemed to be a complete and successful +initialize. // // Only __OBJC2__ adds these handlers. ! __OBJC2__ has a // bootstrapping problem of this versus CF's call to
        // objc_exception_set_functions().
#if __OBJC2__
        @try
#endif
        {
            callInitialize(cls);

            if (PrintInitializing) {
                _objc_inform("INITIALIZE: thread %p: finished +[%s initialize]", pthread_self(), cls->nameForLogging()); }}#if __OBJC2__
        @catch (...) {
            if (PrintInitializing) {
                _objc_inform("INITIALIZE: thread %p: +[%s initialize] "
                             "threw an exception",
                             pthread_self(), cls->nameForLogging());
            }
            @throw;
        }
        @finally
#endif
        {
            // Done initializing.
            lockAndFinishInitializing(cls, supercls);
        }
        return;
    }

    else if (cls->isInitializing()) {
        // We couldn't set INITIALIZING because INITIALIZING was already set. // If this thread set it earlier, continue normally. // If some other thread set it, block until initialize is done. // It's ok if INITIALIZING changes to INITIALIZED while we're here, // because we safely check for INITIALIZED inside the lock // before blocking. if (_thisThreadIsInitializingClass(cls)) { return; } else if (! MultithreadedForkChild) { waitForInitializeToComplete(cls); return; } else { // We're on the child side of fork(), facing a class that // was initializing by some other thread when fork() was called. _setThisThreadIsInitializingClass(cls); performForkChildInitialize(cls, supercls); }}else if (cls->isInitialized()) {
        // Set CLS_INITIALIZING failed because someone else already 
        //   initialized the class. Continue normally.
        // NOTE this check must come AFTER the ISINITIALIZING case.
        // Otherwise: Another thread is initializing this class. ISINITIALIZED 
        //   is false. Skip this clause. Then the other thread finishes 
        //   initialization and sets INITIALIZING=no and INITIALIZED=yes. 
        //   Skip the ISINITIALIZING clause. Die horribly.
        return;
    }

    else {
        // We shouldn't be here. _objc_fatal("thread-safe class init in objc runtime is buggy!" ); } } void callInitialize(Class cls) { ((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize); asm(""); } Duplicate codeCopy the code

Pay attention to look at the call callInitialize (CLS) then call lockAndFinishInitializing (CLS, supercls).

Here’s what Dyld did at various stages in the iOS App Cold Start Governance article:

phase work
Loading the Dynamic library Dyld gets the list of dependent dynamic libraries to load from the header of the main executable. It then needs to find each dylib, and the dylib files on which the application depends may in turn depend on other Dylibs, so all it needs to load is the list of dynamic libraries, a collection of recursive dependencies
Rebase and Bind – Rebase Adjusts the pointer pointer inside the Image. In the past, the dynamic library would be loaded at the specified address, and all Pointers and data would be correct for the code, but now the address space layout is randomized, so it needs to be fixed at the original address based on random offsets – Bind will point the pointer to the correct content outside the Image. These external Pointers are bound by symbol names. Dyld needs to search the symbol table to find the corresponding implementation of symbol
Objc setup – Class registration – Add the definition of a category to the class registration – Make sure that every selector is unique
Initializers – Objc’s +load() function – C++ constructor property function – creation of C++ static global variables of non-primitive types (usually classes or structs)

Finally, dyld calls main(), main() calls UIApplicationMain(), and before main() is complete.

11. Talk about the advantages and disadvantages of the message forwarding mechanism

Memory management

1. How does weak work? What is the structure of SideTable

The solution refers to the implementation of weak reference from the melon god.

NSObject *p = [[NSObject alloc] init]; __weak NSObject *p1 = p; // ====> base is runtime objc_initWeak // xcrun-sdk iphoneos clang-arch arm64-rewrite-objc-fobjc-arc -fobjc-runtime=ios-13.2 main.m NSObject objc_initWeak(&p, object pointer); Copy the codeCopy the code

The runtime source code for objc_initWeak is as follows:

id
objc_initWeakOrNil(id *location, id newObj)
{
    if(! newObj) { *location = nil;return nil;
    }

    returnstoreWeak<DontHaveOld, DoHaveNew, DontCrashIfDeallocating> (location, (objc_object*)newObj); } Duplicate codeCopy the code

The SideTable structure is used for reference counting and weakly reference associated tables at runtime. The data structure looks like this:

Struct SideTable {// spinlock_t slock; RefcountMap refcnts; // weak reference weak_table_t weak_table; } struct weak_table_t {// Save weak pointer to weak_entry_t *weak_entries; // Storage space size_t num_entries; // Uintptr_t mask; //hashUintptr_t max_hash_displacement; }; Copy the codeCopy the code

SideTable (); SideTable ();

Static SideTable *tableForPointer(const void *p) copies the codeCopy the code

/ / SideTables()[newObj] / / SideTables()[newObj] / / SideTables(); / / SideTables()[newObj] / / SideTables(); / / SideTables()

static StripedMap<SideTable>& SideTables() {
    return*reinterpret_cast<StripedMap<SideTable>*>(SideTableBuf); } Duplicate codeCopy the code

In the implementation of taking out instance method, C++ standard conversion operator reinterpret_cast is used, which can be expressed as follows:

Reinterpret_cast <new_type> (expression) Copies codeCopy the code

Every weak keyword modified object is represented by weak_entry_t structure, so the weak object defined in the instance will be encapsulated as Weak_entry_t and added to the Weak_table in the SideTable

typedef objc_object ** weak_referrer_t;

struct weak_entry_t {
    DisguisedPtr<objc_object> referent;
    union {
        struct {
            weak_referrer_t *referrers;
            uintptr_t        out_of_line : 1;
            uintptr_t        num_refs : PTR_MINUS_1;
            uintptr_t        mask;
            uintptr_t        max_hash_displacement;
        };
        struct {
            // out_of_line=0 is LSB of one of these (don't care which) weak_referrer_t inline_referrers[WEAK_INLINE_COUNT]; }; } Duplicate codeCopy the code

Old object to remove registration operation Weak_unregister_NO_lock and new object to add registration operation Weak_register_no_lock, the specific implementation can go to the Runtime source view or view melon’s blog.




image

The weak keyword modifiers have two types of objects: on the stack and on the heap. Id referent_id and ID *referrer_id

  • If it’s on the stack,referrerValue is 0 x77889900,referentThe value is 0 x11223344
  • If IT’s on the heap,referrerThe value is 0x1100000+ offset (address of weak A heap),referentThe value is 0 x11223344.

So now the instance object of class A has two weak variables pointing to it, one on the heap and one on the stack.

void
weak_unregister_no_lock(weak_table_t *weak_table, id referent_id, 
                        id *referrer_id)
{
    objc_object *referent = (objc_object *)referent_id;   //  0x11223344
    objc_object **referrer = (objc_object **)referrer_id; //  0x77889900

    weak_entry_t *entry;

    if(! referent)return; // Find the referent from weak_table, which is the instance object of class A aboveif((entry = weak_entry_for_referent(weak_table, Referent))) {// Find the location of the pointer referrer in the entry structure // set the location of the pointer to nil, Remove_referrer (entry, referrer); bool empty =true;
        if(entry->out_of_line() && entry->num_refs ! = 0) { empty =false;
        }
        else {
            for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
                if (entry->inline_referrers[i]) {
                    empty = false; 
                    break; }}}if (empty) {
            weak_entry_remove(weak_table, entry);
        }
    }

    // Do not set* Referrer = nil. Objc_storeWeak () requires that the // value not changeCopy the code

The weak keyword is set to nil after the dealloc instance is released. The dealloc instance is set to nil after the dealloc instance is released. The dealloc instance is set to nil after the dealloc instance is released. Weak_table -> Weak_entries in the table store all entry objects — in fact, all the variables pointing to this instance object, weak_entry_t in the referrers array store is the memory address of variables or attributes, set to nil one by one.

Basic usage methods of associated objects:

#import <objc/runtime.h>

static NSString * const kKeyOfImageProperty;

@implementation UIView (Image)

- (UIImage *)pt_image {
    return objc_getAssociatedObject(self, &kKeyOfImageProperty);
}

- (void)setPTImage:(UIImage *)image { objc_setAssociatedObject(self, &kKeyOfImageProperty, image,OBJC_ASSOCIATION_RETAIN); } @end copies the codeCopy the code

Objc_AssociationPolicy The owning policies of an associated object are as follows:

Behavior @property Equivalent Description
OBJC_ASSOCIATION_ASSIGN @property (assign) or @property (unsafe_unretained) Specifies a weak reference to an associated object.
OBJC_ASSOCIATION_RETAIN_NONATOMIC @property (nonatomic, strong) Specifies a strong reference to an associated object that cannot be used atomically.
OBJC_ASSOCIATION_COPY_NONATOMIC @property (nonatomic, copy) Specifies a copy reference to an associated object that cannot be used atomically.
OBJC_ASSOCIATION_RETAIN @property (atomic, strong) Specifies a strong reference to an associated object that can be used atomically.
OBJC_ASSOCIATION_COPY @property (atomic, copy) Specifies a copy reference to an associated object that can be used atomically.
OBJC_ASSOCIATION_GETTER_AUTORELEASE Automatic release type

The object of OBJC_ASSOCIATION_ASSIGN is different from weak, but more similar to unsafe_unretained. That is, when the target object is destroyed, the attribute value is not automatically deleted. (Translated from Associated Objects)

In the same Associated Objects article, three usages of Associated Objects are summarized:

  • Add private members to the Class: for example, in AFNetworking, the imageRequestOperation object is added to the UIImageView to ensure that images are loaded asynchronously.
  • Add common members to the Class: for example, in FDTemplateLayoutCell, use Associated Objects to cache the height of each cell (snippet 1, snippet 2). By allocating different keys, cells can be retrieved immediately when they are reused, increasing efficiency.
  • Create KVO objects: It is recommended to use categories to create associated objects as observers. See objective-C Associated Objects for an example.

The source code implementation is very simple, I added a full comment, the c++ syntax also do some explanation:

id _object_get_associative_reference(id object, void *key) { id value = nil; uintptr_t policy = OBJC_ASSOCIATION_ASSIGN; { AssociationsManager manager; // Manager.associations () returns an 'AssociationsHashMap' object (*_map) &associations(manager.associations()); // Intptr_t is used for platform compatibility. On 64-bit machines, intptr_t and uintptr_t are aliases for long int and unsigned long int respectively; On 32-bit machines, intptr_t and uintptr_t are aliases for int and unsigned int respectively. disguised_ptr_t disguised_object = DISGUISE(object); /* AssociationsHashMap inherits from unordered_map and stores the key-value combination iterator find (const key_type& key). If the key does not exist, find returns unordered_map::end; Therefore, 'map.find(key) == map.end()' can be used to determine whether the key exists in the current map. */ AssociationsHashMap::iterator i = associations.find(disguised_object);if(i ! = associations. End ()) {/* Unordered_map keys are first and second attributes of iterators, respectively. I ->second fetch another ObjectAssociationMap. Now use the key set by ourselves to find the corresponding associated attribute value. */ ObjectAssociationMap *refs = I ->second; ObjectAssociationMap::iterator j = refs->find(key);if(j ! = refs->end()) { ObjcAssociation &entry = j->second; value = entry.value(); policy = entry.policy(); OBJC_ASSOCIATION_RETAIN = 01401 // OBJC_ASSOCIATION_GETTER_RETAIN = (1 << 8)if (policy & OBJC_ASSOCIATION_GETTER_RETAIN) {
                    // TODO: 有学问
                    objc_retain(value);
                }
            }
        }
    }
    if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {
        objc_autorelease(value);
    }
    returnvalue; } Duplicate codeCopy the code

The corresponding set operation is also easy to implement, patience to read the source comments, even if different c++ are no problem:

void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
    // retain the new value (ifany) outside the lock. ObjcAssociation old_association(0, nil); // Retain or copy if the value object exists id new_value = value? acquireValue(value, policy) : nil; { AssociationsManager manager; // Manager.associations () returns an 'AssociationsHashMap' object (*_map) &associations(manager.associations()); // Intptr_t is used for platform compatibility. On 64-bit machines, intptr_t and uintptr_t are aliases for long int and unsigned long int respectively; On 32-bit machines, intptr_t and uintptr_t are aliases for int and unsigned int respectively. Disguised_ptr_t DISGUised_object = DISGUISE(object);if (new_value) {
            // breakAny existing association. /* AssociationsHashMap inherits from unordered_map, Iterator find (const key_type& key), which returns an iterator of the key object if it exists, or unordered_map::end if it does not; Therefore, 'map.find(key) == map.end()' can be used to determine whether the key exists in the current map. */ AssociationsHashMap::iterator i = associations.find(disguised_object); // This is different from the get operation,set// A new ObjectAssociationMap is created to store all associated attributes of the instance objectif(i ! // secondary table exists /* unordered_map keys are first and second attributes of iterators. I ->second fetch another ObjectAssociationMap. Now use the key set by ourselves to find the corresponding associated attribute value. */ ObjectAssociationMap *refs = I ->second; ObjectAssociationMap::iterator j = refs->find(key); // The association attributes are encapsulated in the Objcassociety structureif(j ! = refs->end()) { old_association = j->second; j->second = ObjcAssociation(policy, new_value); }else{ (*refs)[key] = ObjcAssociation(policy, new_value); }}else{ // create the new association (first time). ObjectAssociationMap *refs = new ObjectAssociationMap; associations[disguised_object] = refs; (*refs)[key] = ObjcAssociation(policy, new_value); // newisa.has_assoc =true;
                object->setHasAssociatedObjects(); }}else {
            // setting the association to nil breaks the association.
            AssociationsHashMap::iterator i = associations.find(disguised_object);
            if(i ! = associations.end()) { ObjectAssociationMap *refs = i->second; ObjectAssociationMap::iterator j = refs->find(key);if(j ! = refs->end()) { old_association = j->second; refs->erase(j); } } } } // release the old value (outside of the lock).if(old_association.hasValue()) ReleaseValue()(old_association); } Duplicate codeCopy the code

3. How is memory management implemented for associated objects? How does an associated object implement the weak property

Policy is used to set the memory management policy. For details, see the preceding section.

How does Autoreleasepool work? What data structures are used

5. How does ARC work? What optimizations have been made for Retain & Release under ARC

6. What can cause memory leaks in ARC

other

  1. Method SwizzleMatters needing attention
  2. Attribute modifieratomicWhat is the internal implementation of? Is it thread safe
  3. What are some ways of introspection in iOS? What is the internal implementation
  4. Objc_getClass object_getClassWhat’s the difference?

NSNotification related

Read on and you can find the answer here.

  1. Implementation principles (structure design, how notifications are stored,name&observer&SELAnd so on)
  2. Are notifications sent synchronously or asynchronously
  3. NSNotificationCenterAre messages received and sent in the same thread? How do I send messages asynchronously
  4. NSNotificationQueueIs it sent asynchronously or synchronously? In which thread responds
  5. NSNotificationQueueandrunloopThe relationship between
  6. How do I ensure that the thread receiving the notification is in the main thread
  7. Does page destruction crash without removing notifications
  8. What happens when you add the same notification more than once? Multiple removal notifications
  9. Can the following methods receive notifications? why

// Send notification [[NSNotificationCenter defaultCenter] addObserver:self Selector :@selector(handleNotification:) name:@"TestNotification"object:@1]; / / receive notifications [NSNotificationCenter defaultCenter postNotificationName: @"TestNotification"object:nil]; Copy the codeCopy the code

Runloop & KVO

runloop

Runloop is not new to any standard iOS development. Familiarity with Runloop is standard. Here are a few typical issues

  1. How does the app receive touch events
  2. Why only the main threadrunloopIs open
  3. Why only refresh the UI on the main thread
  4. PerformSelectorandrunloopThe relationship between
  5. How do I keep threads alive

KVO

As with Runloop, this is standard, and also lists some typical problems

  1. Realize the principle of
  2. How do I manually shut down KVO
  3. Does modifying properties via KVC trigger KVO
  4. Under what circumstances will KVO crash, and how can it be protected
  5. Advantages and disadvantages of KVO

Block

  1. blockWhat does the structure look like
  2. Is a block a class? What are the types
  3. aintVariables are__blockThe difference between embellishment or not? Block variable interception
  4. blockIn the modifiedNSMutableArray, whether to add__block
  5. How does memory management work
  6. blockYou can usestrongModified?
  7. Why is it used to solve circular references__strong, __weakmodified
  8. blockhappencopyThe timing
  9. BlockOf the access object typeAuto variableWhen theThe ARC and MRCWhat’s the difference

multithreading

Mainly by the COMMUNIST Party of China

  1. iOSHow many types of threads are there in development? Respectively compared
  2. GCDWhich queues are available and which queues are provided by default
  3. GCDWhat are the method apis
  4. GCDRelationship between main thread and main queue
  5. How can synchronization be achieved in as many ways as possible
  6. dispatch_onceRealize the principle of
  7. When can a deadlock occur
  8. What types of thread locks are available and their functions and application scenarios are described
  9. NSOperationQueueIn themaxConcurrentOperationCountThe default value
  10. NSTimer, CADisplayLink, dispatch_source_tThe advantages and disadvantages of

View & image correlation

  1. AutoLayoutHow about the principle and performance
  2. UIView & CALayerThe difference between
  3. Event response chain
  4. drawrect & layoutsubviewsCall time
  5. UI refresh principle
  6. Implicit animation vs. display animation
  7. What is off-screen rendering
  8. Difference between imageName and imageWithContentsOfFile
  9. Will multiple images of the same image be reloaded
  10. When are images decoded and how are they optimized
  11. How to optimize image rendering
  12. If the GPU refresh rate exceeds the 60Hz refresh rate of the iOS screen, what is the phenomenon and how to solve it

Performance optimization

  1. How to do startup optimization, how to monitor
  2. How to do caton optimization, how to monitor
  3. How to optimize power consumption and how to monitor it
  4. How to do network optimization, how to monitor

Developer certificate

  1. What is Apple’s purpose in using certificates
  2. The app Store’s authentication process when installing an app
  3. How do developers install apps to devices in Debug mode

Architecture design

Typical source code learning

Just list some core iOS open source libraries, these libraries contain a lot of high quality ideas, source code learning must pay attention to each framework to solve the core problem is what, as well as their advantages and disadvantages, so as to really understand and absorb

  1. AFN
  2. SDWebImage
  3. JSPatch, Aspects(although one is not available and the other is not maintained, both libraries are neat and easy to learn)
  4. Weex/RN, the author thinks that this kind of front-end and client closely related library is must know its principle
  5. CTMediator and other Router libraries are common routing libraries, which are almost always used in development
  6. pleaseCircle of friendsAdd in the comments below

Architecture design

  1. Manual burying point, automatic burying point, visual burying point
  2. MVC, MVP, MVVMDesign patterns
  3. Common design patterns
  4. The disadvantages of singletons
  5. Common routing schemes, and comparison of advantages and disadvantages
  6. If the stability of the project is guaranteed
  7. Designing an Image Caching Framework (LRU)
  8. How to design agit diff
  9. Designing a thread pool? Draw your architecture diagram
  10. What is your app architecture, what are its strengths and weaknesses, why are you doing it, and how can you improve it

Other problems

  1. PerformSelector & NSInvocationAdvantages and disadvantages compared
  2. ocHow to implement multiple inheritance? How to face the section (can refer toDeep Parsing of Aspects -iOS section-oriented programming)
  3. What are thebugCan cause crashes, how to prevent crashes
  4. How to monitor crashes
  5. app(look at LLVM compilation, static linking, dynamic linking, Runtime initialization)
  6. Sandbox the role of each folder partition in the directory
  7. Briefly describes thematch-oFile structure

System basics

  1. The difference between processes and threads
  2. HTTPSHandshake process
  3. What is theMan-in-the-middle attack? How to prevent
  4. TCPThe handshake process? Why three handshakes, four waves
  5. Stack and heapDistrict differences? Who takes up more memory space
  6. Encryption algorithm:Symmetric encryption algorithm and asymmetric encryption algorithmThe difference between
  7. commonSymmetric and asymmetric encryptionWhat are the algorithms
  8. MD5, Sha1, Sha256The difference between
  9. charlesPacket capture process? Do not usecharles.4GHow does the network capture packets

Data structures and algorithms

For mobile developers, there are generally no very difficult algorithms, most of which are based on data structures. The author lists some necessary algorithms, of course, you can go to LeetCode to brush up problems when you have time

  1. Eight sorting algorithms
  2. The stack and queue
  3. String handling
  4. The list
  5. Binary tree related operations
  6. Deep search search
  7. Basic dynamic programming problems, greedy algorithms, binary search

👇 Recommended 👇 :

You can join the iOS Communication group: this is one for meIOS communication group: 761407670 into the group password 000, whether you are small white or big ox welcome to enter, share BAT, Ali interview questions, interview experience, discuss technology, we exchange learning and growth together!