引言
作为一个iOS开发,alloc init 在日常开发中应该是出现频率最高的了。只是你真的了解alloc运行原理了么?本文就OC对象原理进行探究。

示例

  1. QLPerson *person1 = [QLPerson alloc] ;
  2. QLPerson *person2 = [person1 init];
  3. QLPerson *person3 = [person1 init];
  4. NSLog(@"%@--%p---%p",person1,person1,&person1);
  5. NSLog(@"%@--%p---%p",person2,person2,&person2);
  6. NSLog(@"%@--%p---%p",person3,person3,&person3);

请问打印的结果是什么?

  1. <QLPerson: 0x600000a38390>--0x600000a38390---0x7ffeee8ffcf8
  2. <QLPerson: 0x600000a38390>--0x600000a38390---0x7ffeee8ffcf0
  3. <QLPerson: 0x600000a38390>--0x600000a38390---0x7ffeee8ffce8

打印结果可以看到:三者打印的结果只有&person1,&person2,&person3是不一样的,为连续的三个地址,其余打印地址均为0x600000a38390,为什么会出现这种情况?下面我们来探究其原理流程。

探究方法
####方法一
添加断点:QLPerson *person1 = [QLPerson alloc] ;xcode断点环境下,按住control键,鼠标点击向下箭头step into instruction进入底层
001-OC对象原理探究 - 图7
1、来到objc_alloc
001-OC对象原理探究 - 图8
2、继续点击箭头,来到libobjc.A.dylib objc_alloc
001-OC对象原理探究 - 图9
3、_objc_rootAllocWithZone

方法二
通过汇编,找到符号
显示汇编:Xcode-->Debug-->Debug Workflow-->Always Show Disassembly
001-OC对象原理探究 - 图10

在汇编中可以看到symbol stub for: objc_alloc,按住control键,点击向下箭头,进入到objc_alloc中,重复方法一的步骤,不再赘述。
####方法三
添加符号断点:objc_alloc,_objc_rootAllocWithZone
001-OC对象原理探究 - 图11
001-OC对象原理探究 - 图12

以上三种方法为探究alloc的思路过程,接下来我们将苹果提供的源码来探究alloc的流程,通过源码调试来达到目的。
首先将objc源码可以在Apple source获取
编译流程较为麻烦,请参考Cooci大神的编译过程。
完成以上步骤后,我们开始探究alloc流程:
1、在objc编译成功的源码工程中,添加测试target,在main.m中代码如下:

  1. int main(int argc, const char * argv[]) {
  2. @autoreleasepool {
  3. QLPerson *person1 = [QLPerson alloc];
  4. // QLPerson *person2 = [person1 init];
  5. // QLPerson *person3 = [person1 init];
  6. // NSLog(@"%@--%p---%p",person1,person1,&person1);
  7. // NSLog(@"%@--%p---%p",person2,person2,&person2);
  8. // NSLog(@"%@--%p---%p",person3,person3,&person3);
  9. NSLog(@"%@",person1);
  10. }
  11. return 0;
  12. }

操作流程如下:command + 左键进入NSObject.mm,调用源码如下:

  1. + (id)alloc {
  2. return _objc_rootAlloc(self);
  3. }
  4. id _objc_rootAlloc(Class cls)
  5. {
  6. return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
  7. }
  8. static ALWAYS_INLINE id callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
  9. {
  10. #if __OBJC2__
  11. if (slowpath(checkNil && !cls)) return nil;
  12. if (fastpath(!cls->ISA()->hasCustomAWZ())) {
  13. return _objc_rootAllocWithZone(cls, nil);
  14. }
  15. #endif
  16. // No shortcuts available.
  17. if (allocWithZone) {
  18. return ((id(*)(id, SEL, struct _NSZone *))objc_msgSend)(cls, @selector(allocWithZone:), nil);
  19. }
  20. return ((id(*)(id, SEL))objc_msgSend)(cls, @selector(alloc));
  21. }

选择进入objc-runtime-new.mm

  1. NEVER_INLINE id _objc_rootAllocWithZone(Class cls, malloc_zone_t *zone __unused)
  2. {
  3. // allocWithZone under __OBJC2__ ignores the zone parameter
  4. return _class_createInstanceFromZone(cls, 0, nil,
  5. OBJECT_CONSTRUCT_CALL_BADALLOC);
  6. }
  7. static ALWAYS_INLINE id _class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
  8. int construct_flags = OBJECT_CONSTRUCT_NONE,
  9. bool cxxConstruct = true,
  10. size_t *outAllocatedSize = nil)
  11. {
  12. ASSERT(cls->isRealized());
  13. // Read class's info bits all at once for performance
  14. bool hasCxxCtor = cxxConstruct && cls->hasCxxCtor();
  15. bool hasCxxDtor = cls->hasCxxDtor();
  16. bool fast = cls->canAllocNonpointer();
  17. size_t size;
  18. size = cls->instanceSize(extraBytes);
  19. if (outAllocatedSize) *outAllocatedSize = size;
  20. id obj;
  21. if (zone) {
  22. obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
  23. } else {
  24. obj = (id)calloc(1, size);
  25. }
  26. if (slowpath(!obj)) {
  27. if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {
  28. return _objc_callBadAllocHandler(cls);
  29. }
  30. return nil;
  31. }
  32. if (!zone && fast) {
  33. obj->initInstanceIsa(cls, hasCxxDtor);
  34. } else {
  35. // Use raw pointer isa on the assumption that they might be
  36. // doing something weird with the zone or RR.
  37. obj->initIsa(cls);
  38. }
  39. if (fastpath(!hasCxxCtor)) {
  40. return obj;
  41. }
  42. construct_flags |= OBJECT_CONSTRUCT_FREE_ONFAILURE;
  43. return object_cxxConstructFromClass(obj, cls, construct_flags);
  44. }

在以上源码中添加断点后,调试过程我总结成了一张图:
001-OC对象原理探究 - 图13

文字流程:
+ alloc_objc_rootAlloccallAlloc_objc_rootAllocWithZone_class_createInstanceFromZone (核心实现,在方法中,完成对象大小计算,对齐,开辟,关联)

总结:
本文为oc对象的初步探究,仅提到oc对象alloc的流程,init过程将在下一章讲述。如有不同意见,请留言。