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
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 |
b num |
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.
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,
referrer
Value is 0 x77889900,referent
The value is 0 x11223344 - If IT’s on the heap,
referrer
The value is 0x1100000+ offset (address of weak A heap),referent
The 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
Method Swizzle
Matters needing attention- Attribute modifier
atomic
What is the internal implementation of? Is it thread safe - What are some ways of introspection in iOS? What is the internal implementation
Objc_getClass object_getClass
What’s the difference?
NSNotification related
Read on and you can find the answer here.
- Implementation principles (structure design, how notifications are stored,
name&observer&SEL
And so on) - Are notifications sent synchronously or asynchronously
NSNotificationCenter
Are messages received and sent in the same thread? How do I send messages asynchronouslyNSNotificationQueue
Is it sent asynchronously or synchronously? In which thread respondsNSNotificationQueue
andrunloop
The relationship between- How do I ensure that the thread receiving the notification is in the main thread
- Does page destruction crash without removing notifications
- What happens when you add the same notification more than once? Multiple removal notifications
- 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
- How does the app receive touch events
- Why only the main thread
runloop
Is open - Why only refresh the UI on the main thread
PerformSelector
andrunloop
The relationship between- How do I keep threads alive
KVO
As with Runloop, this is standard, and also lists some typical problems
- Realize the principle of
- How do I manually shut down KVO
- Does modifying properties via KVC trigger KVO
- Under what circumstances will KVO crash, and how can it be protected
- Advantages and disadvantages of KVO
Block
block
What does the structure look like- Is a block a class? What are the types
- a
int
Variables are__block
The difference between embellishment or not? Block variable interception block
In the modifiedNSMutableArray
, whether to add__block
- How does memory management work
block
You can usestrong
Modified?- Why is it used to solve circular references
__strong, __weak
modified block
happencopy
The timingBlock
Of the access object typeAuto variable
When theThe ARC and MRC
What’s the difference
multithreading
Mainly by the COMMUNIST Party of China
iOS
How many types of threads are there in development? Respectively comparedGCD
Which queues are available and which queues are provided by defaultGCD
What are the method apisGCD
Relationship between main thread and main queue- How can synchronization be achieved in as many ways as possible
dispatch_once
Realize the principle of- When can a deadlock occur
- What types of thread locks are available and their functions and application scenarios are described
NSOperationQueue
In themaxConcurrentOperationCount
The default valueNSTimer, CADisplayLink, dispatch_source_t
The advantages and disadvantages of
View & image correlation
AutoLayout
How about the principle and performanceUIView & CALayer
The difference between- Event response chain
drawrect & layoutsubviews
Call time- UI refresh principle
- Implicit animation vs. display animation
- What is off-screen rendering
- Difference between imageName and imageWithContentsOfFile
- Will multiple images of the same image be reloaded
- When are images decoded and how are they optimized
- How to optimize image rendering
- 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
- How to do startup optimization, how to monitor
- How to do caton optimization, how to monitor
- How to optimize power consumption and how to monitor it
- How to do network optimization, how to monitor
Developer certificate
- What is Apple’s purpose in using certificates
- The app Store’s authentication process when installing an app
- 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
- AFN
- SDWebImage
- JSPatch, Aspects(although one is not available and the other is not maintained, both libraries are neat and easy to learn)
- Weex/RN, the author thinks that this kind of front-end and client closely related library is must know its principle
- CTMediator and other Router libraries are common routing libraries, which are almost always used in development
- please
Circle of friends
Add in the comments below
Architecture design
- Manual burying point, automatic burying point, visual burying point
MVC, MVP, MVVM
Design patterns- Common design patterns
- The disadvantages of singletons
- Common routing schemes, and comparison of advantages and disadvantages
- If the stability of the project is guaranteed
- Designing an Image Caching Framework (LRU)
- How to design a
git diff
- Designing a thread pool? Draw your architecture diagram
- What is your app architecture, what are its strengths and weaknesses, why are you doing it, and how can you improve it
Other problems
PerformSelector & NSInvocation
Advantages and disadvantages comparedoc
How to implement multiple inheritance? How to face the section (can refer toDeep Parsing of Aspects -iOS section-oriented programming)- What are the
bug
Can cause crashes, how to prevent crashes - How to monitor crashes
app
(look at LLVM compilation, static linking, dynamic linking, Runtime initialization)- Sandbox the role of each folder partition in the directory
- Briefly describes the
match-o
File structure
System basics
- The difference between processes and threads
HTTPS
Handshake process- What is the
Man-in-the-middle attack
? How to prevent TCP
The handshake process? Why three handshakes, four wavesStack and heap
District differences? Who takes up more memory space- Encryption algorithm:
Symmetric encryption algorithm and asymmetric encryption algorithm
The difference between - common
Symmetric and asymmetric encryption
What are the algorithms MD5, Sha1, Sha256
The difference betweencharles
Packet capture process? Do not usecharles
.4G
How 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
- Eight sorting algorithms
- The stack and queue
- String handling
- The list
- Binary tree related operations
- Deep search search
- 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!