SwiftランタむムたたはNSObjectが消えた堎所に぀いお少し

こんにちは友人 私はブラックボックスから突き出おいる匊を匕っ匵るだけで退屈しおいる人の1人です。フヌドの䞋でどのように機胜するかを自分の目で芋たいです。 ランタむムに぀いお説明したす、はいランタむムです。 私たちの経隓では、Objective-Cの叀き良き祖父ず、ただ開発䞭の革新的なSwiftを怜蚎しおください。 あなたず私は、Appleプログラマヌが慎重に考え抜いた抜象化のほが底蟺たで朜る必芁がありたす。 䞀般に新しい蚀語を開発する必芁があった理由を少し芋おみたしょう。 特に、すでに経隓のあるObjective C開発者から、倚くの吊定的なレビュヌを聞いたこずがありたすが、新しいSwift蚀語をよく芋るず、より成熟しおいお真剣です。 最初に、圌はObjective Cの基瀎ずなるCずは異なり、C ++で蚘述したした。ここでは、あなたが同意できる、非垞に個人的な偏芋のみを衚珟したす。



私の意芋では、珟時点でのC ++は、あなたが奜きなこずを行うこずができる最も深刻な開発蚀語ですが、Cよりも゚レガントです。 だからこそ、Swift蚀語、 LLDBなどを曞くための基瀎ずしお遞ばれたのは圌だったず思いたす。 ここではSwift蚀語の機胜を確認せずに、いく぀かのポむントを匷調したす。他のすべおは専門のドキュメントに蚘茉されおいたす。 私の意芋では、初心者プログラマヌにずっおははるかに機胜的で簡単です。実際、これは開発の目暙の1぀であり、新しい開発者の゚ントリヌしきい倀を䞋げるこずです。 特に簡朔なCたたはJavaで曞いた堎合は特に、Objective-Cの括匧を初めお知った埌、どのように髪を立おたかを芚えおおいおください。 もちろん、私の意芋では、目立぀ためにプログラマヌが思い぀いたのは掗緎されおいなかったわけではありたせん。 しかし、これがすべおの歌詞です。ビゞネスに取り掛かりたしょう。 最初に、Objectice Cの祖父の基本クラスを分析し、Swiftず比范したしょう。



これをすべお知っおいる人のために、あなたはただ煙のために出かけるこずができたす。 Objective-Cで知っおいるように、クラスはNSObjectたたはNSProxyから間接的たたは盎接継承する必芁がありたす。 NSObjectクラスは、非公匏プロトコルNSObjectを実装しおいたす。 SwiftObjectを怜蚎するず、それに戻りたす。 将来的には、将来的に2぀の蚀語の友達を䜜るのに圹立぀のはこのプロトコルであるず蚀えたす。ここにそれがポリモヌフィズムの力です NSObjectクラスのすべおのメ゜ッドの䞀郚を芋おいきたす。 そしおその埌、このすばらしいNSObjectの䜕が問題なのかを敢えお結論付けたした。 私は長い間あなたを苊しめたせん、圌らは運転したした



@interface NSObject <NSObject> { Class isa OBJC_ISA_AVAILABILITY; } + (void)load; + (void)initialize; - (instancetype)init #if NS_ENFORCE_NSOBJECT_DESIGNATED_INITIALIZER NS_DESIGNATED_INITIALIZER #endif ; + (instancetype)new OBJC_SWIFT_UNAVAILABLE("use object initializers instead"); + (instancetype)allocWithZone:(struct _NSZone *)zone OBJC_SWIFT_UNAVAILABLE("use object initializers instead"); + (instancetype)alloc OBJC_SWIFT_UNAVAILABLE("use object initializers instead"); - (void)dealloc OBJC_SWIFT_UNAVAILABLE("use 'deinit' to define a de-initializer"); - (void)finalize; ....
      
      





最初の2぀の方法は、通垞のObjectice C開発者にはあたり出䌚わないため、ここでは詳しく説明したせん。 loadメ゜ッドは、クラスたたはカテゎリがランタむムにロヌドされるずきに呌び出され、継承の叀兞的な芏則に埓わず、実行時にアプリケヌションをブロックしたす。 初期化メ゜ッドは、クラスを最初に䜿甚する前に遅延モヌドで呌び出され、アプリケヌションをブロックせず、継承の叀兞的な芏則に埓いたす。



埌続のすべおのメ゜ッドは、オブゞェクトの䜜成ず初期化を担圓したす。 たた、それらに぀いおも少し説明したす。 allocWithZoneメ゜ッドは 、オブゞェクトにメモリを割り圓おる圹割を果たしたす。 内郚では、圌は私たちの最愛のmallocを呌び出したす。 それは、メモリがゟヌンに分割されたずきのbeの時代からです。 すべおのオブゞェクトが1぀のゟヌンで䜜成されるようになったため、内郚でallocWithZoneを呌び出しおデフォルトのゟヌンNSDefaultMallocZoneを枡すallocメ゜ッドが登堎したした 。



オブゞェクトが削陀されるず、 deallocメ゜ッドずfinalizeメ゜ッドが呌び出されたす。 これらのメ゜ッドでは、関連するすべおのリ゜ヌスがクリアされ、その結果、空きメモリが空きメモリプヌルに入りたす。 通垞、 ファむナラむズは䜿甚されず、最埌のリリヌスが発生したスレッドでdeallocが呌び出されたす 。



次の䞀連のメ゜ッドに移動したす



 - (id)copy; - (id)mutableCopy; + (id)copyWithZone:(struct _NSZone *)zone OBJC_ARC_UNAVAILABLE; + (id)mutableCopyWithZone:(struct _NSZone *)zone OBJC_ARC_UNAVAILABLE;
      
      





たあ、おそらくスポットラむトで曞いたこずのない人にずっおも、圌らがコピヌのためだず掚枬するのは簡単です。 ゟヌンでは、すべおが明確であり、これらはもはや関係のない叀代の方法です。 実際、私たちから䜕かをコピヌするには、 NSCopyingプロトコルを実装する必芁がありたす。これらのメ゜ッドを単玔に呌び出すず、すべおが倱敗したす。 ただし、これに぀いおは匕き続き説明したす。 それたでの間、次のパックに進みたす。



 + (BOOL)instancesRespondToSelector:(SEL)aSelector; + (BOOL)conformsToProtocol:(Protocol *)protocol; - (IMP)methodForSelector:(SEL)aSelector; + (IMP)instanceMethodForSelector:(SEL)aSelector; - (void)doesNotRecognizeSelector:(SEL)aSelector; + (NSMethodSignature *)instanceMethodSignatureForSelector:(SEL)aSelector OBJC_SWIFT_UNAVAILABLE(""); - (BOOL)allowsWeakReference UNAVAILABLE_ATTRIBUTE; - (BOOL)retainWeakReference UNAVAILABLE_ATTRIBUTE; + (BOOL)isSubclassOfClass:(Class)aClass; + (BOOL)resolveClassMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0); + (BOOL)resolveInstanceMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0); + (Class)superclass; + (Class)class OBJC_SWIFT_UNAVAILABLE("use 'aClass.self' instead");
      
      





ここでは、内省です。 安党なコヌドの鍵は怜玢゚ンゞンsiにありたす。 これらのメ゜ッドにより、オブゞェクトたたはクラス、実装するプロトコル、応答可胜なセレクタヌなどを評䟡できたす。 進む



 - (id)forwardingTargetForSelector:(SEL)aSelector OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0); - (void)forwardInvocation:(NSInvocation *)anInvocation OBJC_SWIFT_UNAVAILABLE(""); - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector OBJC_SWIFT_UNAVAILABLE("");
      
      





最初の2぀のメ゜ッドは、䜕も実装しないずすべおがクラッシュするたで呌び出されたす。 これは、オブゞェクトがあなたがそれから䜕を望んでいるかを理解せず、それが持たないメ゜ッドを呌び出すずきに起こりたす。 これを有意矩に行う堎合は、これらのメ゜ッドの1぀を決定し、セレクタヌぞの呌び出し、送信先、いわば凊理を決定するのに十分芪切にしおください。 これは、実際にはメッセヌゞリダむレクトメカニズムず呌ばれたす。 これも読むこずができ、たくさんの蚘事があり、3぀の方法がありたす。 これにこだわる぀もりはありたせん。さもないず、この蚘事の本質を倱いたす。



最新の方法を芋おみたしょう



 + (NSUInteger)hash; + (NSString *)description; + (NSString *)debugDescription;
      
      





私は誰もがハッシュメ゜ッドを知っおいるず思う、それはオブゞェクトの比范、コレクション内のネストによるオブゞェクトの配垃などに䜿甚される オブゞェクトのあるアドレスだけでなく、䜕らかの意味のある情報をログで衚瀺する堎合は、 descriptionメ゜ッドずdebugDescriptionメ゜ッドを再定矩する必芁がありたす。



ふう、私はすでにこれらの方法を説明するのにうんざりしおいたした。 このクラスを詳しく芋おみたしょう。ここでは、基本的に怜玢゚ンゞンのすべおのクラスの先頭にありたす。 圌の䜕が問題なのですか なぜ圌がそんなに奜きじゃないの はい、圌のすべおが間違っおいたす



私たちのOOP原則がそこに蚀うように継承、カプセル化、ポリモヌフィズム しかし、䜕らかの理由で、1぀のクラスからすべおを継承するずは蚀われおいたせん。JavaずCの開発者を蚱しおください。 アヌキテクチャを構築した経隓のあるプログラマヌは、圓初は非垞に考え抜かれおいたアヌキテクチャヌ決定のさたざたな萜ずし穎に遭遇したず思いたす。 継承は時限爆匟のようなもので、最終的には思慮深いアヌキテクチャをすべおれロにするか、開発のために手を瞛りたす。 建築に関する倚くの本は、継承ではなく構成を遞択するず述べおいたす。 これはより柔軟なアプロヌチですが、継承も凊理するのに非垞に匷力です。 ぀たり、抜象化を分離し、さらに拡匵するこずは正しいこずです。 今埌、Swiftはプログラマが利甚できるこの基本クラスを倱ったず蚀いたす。



実際、それはSwiftObjectず呌ばれ、Objective-Cず察話する必芁があるすべおのクラスSwiftで蚘述されたが継承されたす。 圌に぀いおもっず話したしょう。 倚くの人がおそらく蚀うでしょうこの男は䜕ですか 継承はクヌルなこずで、コヌドを再利甚したすが、それは悪いこずです。 このトピックを別の蚘事に入れお、他のこずに぀いお話したす。 たずえば、䜕もコピヌしたくないのに、なぜcopyメ゜ッドが必芁なのですか それにもかかわらず、私はそれを呌び出すこずができ、NSCopyingパンクを実装しない堎合、それ自䜓ですべおが萜ちたす。 継承に぀いお話したしょう。 オブゞェクトを初期化する堎合に呌び出す必芁があるinitメ゜ッドがありたすが、それ自䜓で呌び出されるdeallocメ゜ッドがありたす オブゞェクトのラむフサむクルのメ゜ッドであり、決しお手動で呌び出す必芁はありたせん。 しかし、誰も私にこれをやめさせたせん、本圓に玠晎らしいですか はい、それはたったく玠晎らしいこずではありたせん。 NSObjectクラス自䜓により、必芁のないこずを実行したり、必芁のないこずを知るこずができたす。 このトピックをさらに発展させるこずはできたすが、NSObjectがすべきではないこずはすでに明らかであるため、プログラマヌにずっおはSwift蚀語に姿を消したした。 正匏にはもちろん、基本クラスはIOSプラットフォヌムのみに留たりたしたが、お互いに仲良くするために、これら2぀の蚀語老人Objective-Cず野心的なSwiftです。 圌を片目で芋おみたしょう。



 @implementation SwiftObject + (void)initialize {} + (instancetype)allocWithZone:(struct _NSZone *)zone { assert(zone == nullptr); return _allocHelper(self); } + (instancetype)alloc { // we do not support "placement new" or zones, // so there is no need to call allocWithZone return _allocHelper(self); } + (Class)class { return self; } - (Class)class { return (Class) _swift_getClassOfAllocated(self); } + (Class)superclass { return (Class) _swift_getSuperclass((const ClassMetadata*) self); } - (Class)superclass { return (Class) _swift_getSuperclass(_swift_getClassOfAllocated(self)); } + (BOOL)isMemberOfClass:(Class)cls { return cls == (Class) _swift_getClassOfAllocated(self); } - (BOOL)isMemberOfClass:(Class)cls { return cls == (Class) _swift_getClassOfAllocated(self); } - (instancetype)self { return self; } - (BOOL)isProxy { return NO; } - (struct _NSZone *)zone { auto zone = malloc_zone_from_ptr(self); return (struct _NSZone *)(zone ? zone : malloc_default_zone()); } - (void)doesNotRecognizeSelector: (SEL) sel { Class cls = (Class) _swift_getClassOfAllocated(self); fatalError(/* flags = */ 0, "Unrecognized selector %c[%s %s]\n", class_isMetaClass(cls) ? '+' : '-', class_getName(cls), sel_getName(sel)); } - (id)retain { auto SELF = reinterpret_cast<HeapObject *>(self); swift_retain(SELF); return self; } - (void)release { auto SELF = reinterpret_cast<HeapObject *>(self); swift_release(SELF); } - (id)autorelease { return _objc_rootAutorelease(self); } - (NSUInteger)retainCount { return swift::swift_retainCount(reinterpret_cast<HeapObject *>(self)); } - (BOOL)_isDeallocating { return swift_isDeallocating(reinterpret_cast<HeapObject *>(self)); } - (BOOL)_tryRetain { return swift_tryRetain(reinterpret_cast<HeapObject*>(self)) != nullptr; } - (BOOL)allowsWeakReference { return !swift_isDeallocating(reinterpret_cast<HeapObject *>(self)); } - (BOOL)retainWeakReference { return swift_tryRetain(reinterpret_cast<HeapObject*>(self)) != nullptr; } // Retaining the class object itself is a no-op. + (id)retain { return self; } + (void)release { /* empty */ } + (id)autorelease { return self; } + (NSUInteger)retainCount { return ULONG_MAX; } + (BOOL)_isDeallocating { return NO; } + (BOOL)_tryRetain { return YES; } + (BOOL)allowsWeakReference { return YES; } + (BOOL)retainWeakReference { return YES; } - (void)dealloc { swift_rootObjCDealloc(reinterpret_cast<HeapObject *>(self)); } - (BOOL)isKindOfClass:(Class)someClass { for (auto isa = _swift_getClassOfAllocated(self); isa != nullptr; isa = _swift_getSuperclass(isa)) if (isa == (const ClassMetadata*) someClass) return YES; return NO; } + (BOOL)isSubclassOfClass:(Class)someClass { for (auto isa = (const ClassMetadata*) self; isa != nullptr; isa = _swift_getSuperclass(isa)) if (isa == (const ClassMetadata*) someClass) return YES; return NO; } + (BOOL)respondsToSelector:(SEL)sel { if (!sel) return NO; return class_respondsToSelector((Class) _swift_getClassOfAllocated(self), sel); } - (BOOL)respondsToSelector:(SEL)sel { if (!sel) return NO; return class_respondsToSelector((Class) _swift_getClassOfAllocated(self), sel); } + (BOOL)instancesRespondToSelector:(SEL)sel { if (!sel) return NO; return class_respondsToSelector(self, sel); } - (BOOL)conformsToProtocol:(Protocol*)proto { if (!proto) return NO; auto selfClass = (Class) _swift_getClassOfAllocated(self); // Walk the superclass chain. while (selfClass) { if (class_conformsToProtocol(selfClass, proto)) return YES; selfClass = class_getSuperclass(selfClass); } return NO; } + (BOOL)conformsToProtocol:(Protocol*)proto { if (!proto) return NO; // Walk the superclass chain. Class selfClass = self; while (selfClass) { if (class_conformsToProtocol(selfClass, proto)) return YES; selfClass = class_getSuperclass(selfClass); } return NO; } - (NSUInteger)hash { return (NSUInteger)self; } - (BOOL)isEqual:(id)object { return self == object; } - (id)performSelector:(SEL)aSelector { return ((id(*)(id, SEL))objc_msgSend)(self, aSelector); } - (id)performSelector:(SEL)aSelector withObject:(id)object { return ((id(*)(id, SEL, id))objc_msgSend)(self, aSelector, object); } - (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2 { return ((id(*)(id, SEL, id, id))objc_msgSend)(self, aSelector, object1, object2); } - (NSString *)description { return _getDescription(self); } - (NSString *)debugDescription { return _getDescription(self); } + (NSString *)description { return _getClassDescription(self); } + (NSString *)debugDescription { return _getClassDescription(self); } - (NSString *)_copyDescription { // The NSObject version of this pushes an autoreleasepool in case -description // autoreleases, but we're OK with leaking things if we're at the top level // of the main thread with no autorelease pool. return [[self description] retain]; } - (CFTypeID)_cfTypeID { // Adopt the same CFTypeID as NSObject. static CFTypeID result; static dispatch_once_t predicate; dispatch_once_f(&predicate, &result, [](void *resultAddr) { id obj = [[NSObject alloc] init]; *(CFTypeID*)resultAddr = [obj _cfTypeID]; [obj release]; }); return result; } // Foundation collections expect these to be implemented. - (BOOL)isNSArray__ { return NO; } - (BOOL)isNSDictionary__ { return NO; } - (BOOL)isNSSet__ { return NO; } - (BOOL)isNSOrderedSet__ { return NO; } - (BOOL)isNSNumber__ { return NO; } - (BOOL)isNSData__ { return NO; } - (BOOL)isNSDate__ { return NO; } - (BOOL)isNSString__ { return NO; } - (BOOL)isNSValue__ { return NO; } @end
      
      





あれ ご芧の内容ですが、SwiftObjectクラスは非公匏プロトコルNSObjectを実装しおいたすが、メ゜ッドの実装はすでに完党に異なっおいたす。 NSObjectから明瀺的に継承されおいないすべおのSwiftクラスは、SwiftObjectクラスから暗黙的に継承されるようになりたした。 これは、Objectice Cずの察話を必芁ずするプラットフォヌムでのみ発生するこずをすぐに修正したす。ObjectiveC以倖のプラットフォヌムLinuxなどでは、これは必芁ありたせん。



私たちが孊んだように、これは別の話です。 少し教えおもいいず思いたす。 ご存知のように、SwiftはAppleリポゞトリのパブリックドメむンにありたす。 誰もそれをダりンロヌドしお゜ヌス自䜓からコンパむルするこずを気にしたせん。これはたさに私たちがやったこずです。 しかし、私たちはもう少し進みたした。 バヌゞョン8以降の有名なXcodeを䜿甚するず、ツヌルチェヌンを手に入れるこずができたす。 はい、それはどういう意味ですか これは、デバッグ情報を䜿甚しおSwiftをアセンブルし、Xcodeに配眮できるこずを意味したす。



画像



私の同僚はそれをやったので、Swift゜ヌスをXcodeから盎接デバッグできたした。



画像



私たちは少し気を散らされおいたす、私たちは掚論を続けたす。 Objective-Cで生成されたメタデヌタずSwiftで生成されたメタデヌタの性質が異なるこずはすでに明らかです。 Objectice Cで長い間曞いおおり、少なくずも少しランタむムを遞択したプログラマヌなら誰でもこの構造を知っおいる



 struct objc_class { Class isa OBJC_ISA_AVAILABILITY; #if !__OBJC2__ Class super_class OBJC2_UNAVAILABLE; const char *name OBJC2_UNAVAILABLE; long version OBJC2_UNAVAILABLE; long info OBJC2_UNAVAILABLE; long instance_size OBJC2_UNAVAILABLE; struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; struct objc_method_list **methodLists OBJC2_UNAVAILABLE; struct objc_cache *cache OBJC2_UNAVAILABLE; struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; #endif } OBJC2_UNAVAILABLE;
      
      





私たちは皆、すべおのオブゞェクトが最終的に、ある皋床この構造の圢で芋えるこずを知っおいたす。 私たちのお気に入りのNSObjectは、この構造ず盎接察話するこずからプログラマを保護する抜象化です。 あなたはそれに぀いおもっず読むこずができたす、ロシア語でさえ、蚀語の存圚の間に曞かれたたくさんの蚘事がありたす。 Swiftに戻りたしょう。 珟圚、メタデヌタを保存するための特別なクラスのメタデヌタが登堎したした。これは非垞に膚倧で、Swiftのすべおのメタデヌタの基瀎を構成したす。 別の蚘事でその構造の詳现を説明したす。 もう1぀のポむントは、すべおのSwiftオブゞェクトが独自のメタデヌタ構造を持っおいるにもかかわらず、互換性のためにさらに倚くのObjective-Cメタデヌタを生成したす。 ぀たり、各Swiftオブゞェクトには2セットのメタデヌタがありたす。



少し芁玄したしょう。 NSObjectは芋苊しいため、新しい蚀語で䜿甚する堎所がないこずがわかりたした。 したがっお、Swiftでは、䜕からも継承せずにクラスを䜜成できたすが、実際には、互換性のために、SwiftObjectから継承したす。 SwiftObjectクラスはフレンドになり、NSObjectクラスは非公匏プロトコルNSObjectによっお蚱可されたした。 これにより、Swiftオブゞェクトをidにキャストし、Objective Cに枡すこずができたすが、そこで動䜜するのがいいので、各Swiftオブゞェクトは、そのメタデヌタに加えお、Objective C.メタデヌタを生成したす。 みんなありがずう 健康ず良い気分



All Articles