আমি মনে করি এটি সত্যিই দুর্দান্ত প্রশ্ন, এবং এটি লজ্জার বিষয় যে আসল প্রশ্নটি মোকাবেলা করার পরিবর্তে বেশিরভাগ উত্তরই বিষয়টি এড়িয়ে গেছে এবং কেবল সুইজলিং ব্যবহার না করার জন্য বলেছে।
পদ্ধতি সিজলিংয়ের ব্যবহার রান্নাঘরের ধারালো ছুরি ব্যবহার করার মতো। কিছু লোক ধারালো ছুরির কারণে ভীত হয় কারণ তারা মনে করে যে তারা নিজেরাই খারাপভাবে কাটবে, তবে সত্যটি হ'ল ধারালো ছুরিগুলি আরও নিরাপদ ।
পদ্ধতি, সুইজলিং আরও ভাল, আরও দক্ষ, আরও রক্ষণাবেক্ষণযোগ্য কোড লিখতে ব্যবহার করা যেতে পারে। এটি অপব্যবহারও করা যায় এবং ভয়াবহ বাগগুলি হতে পারে।
পটভূমি
সমস্ত নকশার নিদর্শনগুলির মতো, যদি আমরা প্যাটার্নটির পরিণতি সম্পর্কে পুরোপুরি সচেতন হই, তবে এটি ব্যবহার করবেন কি না সে সম্পর্কে আমরা আরও জ্ঞাত সিদ্ধান্ত নিতে সক্ষম হয়েছি। সিঙ্গলেটগুলি এমন কোনও জিনিসের একটি ভাল উদাহরণ যা বেশ বিতর্কিত, এবং সঙ্গত কারণে - সঠিকভাবে প্রয়োগ করা সত্যই তাদের পক্ষে শক্ত। যদিও এখনও অনেকে সিলেটলেট ব্যবহার করতে পছন্দ করে। সুইজলিংয়ের ক্ষেত্রেও একই কথা বলা যেতে পারে। ভাল এবং খারাপ উভয়ই পুরোপুরি বুঝতে পারলে আপনার নিজের মতামত তৈরি করা উচিত।
আলোচনা
পদ্ধতির সুইজলিংয়ের কয়েকটি সমস্যা এখানে রয়েছে:
- পদ্ধতি সুইজলিং পারমাণবিক নয়
- মালিকানাবিহীন কোডের আচরণের পরিবর্তন করে
- সম্ভাব্য নাম দ্বন্দ্ব
- সুইজলিং পদ্ধতিটির যুক্তিগুলিকে পরিবর্তন করে
- সুইজার্সের ক্রমটি গুরুত্বপূর্ণ
- বুঝতে অসুবিধা (পুনরাবৃত্ত লাগছে)
- ডিবাগ করা কঠিন
এই পয়েন্টগুলি সমস্ত বৈধ, এবং এগুলিকে সম্বোধন করার ক্ষেত্রে আমরা পদ্ধতি সাঁতার কাটা সম্পর্কে আমাদের বোঝার পাশাপাশি ফলাফল অর্জনের জন্য ব্যবহৃত পদ্ধতিগুলি উভয়কে উন্নত করতে পারি। আমি প্রত্যেককে একবারে নিয়ে যাব।
পদ্ধতি সুইজলিং পারমাণবিক নয়
আমি এখনও পদ্ধতি সুইজলিংয়ের একটি বাস্তবায়ন দেখতে পাই যা একই সাথে 1 ব্যবহার করা নিরাপদ । আপনি পদ্ধতি সুইজলিং ব্যবহার করতে চান এমন 95% ক্ষেত্রে এটি আসলে কোনও সমস্যা নয়। সাধারণত, আপনি কেবল কোনও পদ্ধতির বাস্তবায়ন প্রতিস্থাপন করতে চান এবং আপনি চান যে প্রয়োগটি আপনার প্রোগ্রামের পুরো জীবনকালের জন্য ব্যবহার করা হোক। এর অর্থ হ'ল আপনার পদ্ধতিটি সাঁতার কাটা উচিত +(void)load
। load
বর্গ পদ্ধতি আপনার আবেদন শুরুতে ধারাবাহিকভাবে মৃত্যুদন্ড কার্যকর করা হয়। আপনি যদি এখানে আপনার সুইজলিং করেন তবে সম্মতিতে কোনও সমস্যা নেই। আপনি যদি +(void)initialize
ঘুরে বেড়াতে থাকেন তবে আপনি নিজের সুইজলিং বাস্তবায়নে একটি রেসের শর্তটি শেষ করতে পারেন এবং রানটাইমটি একটি অদ্ভুত অবস্থায় শেষ করতে পারে।
মালিকানাবিহীন কোডের আচরণের পরিবর্তন করে
এটি সুইজলিংয়ের একটি সমস্যা, তবে এটি পুরোপুরি বিষয় kind লক্ষ্যটি হল যে কোডটি পরিবর্তন করতে সক্ষম হোন। লোকেদের এটি একটি বড় চুক্তি হিসাবে চিহ্নিত করার কারণ হ'ল আপনি NSButton
যে জিনিসগুলির জন্য পরিবর্তন করতে চান তার একটি উদাহরণের জন্য আপনি কেবল জিনিসগুলি পরিবর্তন করছেন না , পরিবর্তে NSButton
আপনার অ্যাপ্লিকেশনটির সমস্ত দৃষ্টান্তের জন্য । এই কারণে, যখন আপনি সাঁতার কাটাচ্ছেন তখন আপনার সতর্ক হওয়া উচিত তবে আপনার এটি পুরোপুরি এড়ানো উচিত নয়।
এটিকে এভাবে ভাবুন ... আপনি যদি কোনও ক্লাসে কোনও পদ্ধতিকে ওভাররাইড করে থাকেন এবং আপনি সুপার ক্লাস পদ্ধতিটি না কল করেন, আপনার সমস্যা দেখা দিতে পারে। বেশিরভাগ ক্ষেত্রে, সুপার ক্লাসটি সেই পদ্ধতিটি কল করার প্রত্যাশা করে (অন্যথায় ডকুমেন্টেড না হলে)। আপনি যদি এই একই চিন্তাকে সুইজলিংয়ে প্রয়োগ করেন তবে আপনি বেশিরভাগ বিষয় coveredেকে রেখেছেন। সবসময় আসল বাস্তবায়ন কল করুন। আপনি যদি তা না করেন তবে আপনি নিরাপদে থাকার জন্য সম্ভবত খুব বেশি পরিবর্তন করছেন।
সম্ভাব্য নাম দ্বন্দ্ব
নামকরণের বিরোধগুলি সমস্ত কোকো জুড়েই একটি সমস্যা। আমরা প্রায়শই বিভাগগুলিতে শ্রেণীর নাম এবং পদ্ধতির নাম উপসর্গ করি। দুর্ভাগ্যক্রমে, নামকরণের বিরোধগুলি আমাদের ভাষায় একটি প্লেগ। Swizzling ক্ষেত্রে, যদিও, তাদের হতে হবে না। আমাদের কেবল পদ্ধতিটি কিছুটা সুইজলিংয়ের সম্পর্কে চিন্তাভাবনার উপায়টি পরিবর্তন করতে হবে। বেশিরভাগ সুইজলিং এইভাবে করা হয়:
@interface NSView : NSObject
- (void)setFrame:(NSRect)frame;
@end
@implementation NSView (MyViewAdditions)
- (void)my_setFrame:(NSRect)frame {
// do custom work
[self my_setFrame:frame];
}
+ (void)load {
[self swizzle:@selector(setFrame:) with:@selector(my_setFrame:)];
}
@end
এটি ঠিক কাজ করে, তবে my_setFrame:
অন্য কোথাও সংজ্ঞায়িত হলে কী হবে ? এই সমস্যাটি সাঁতার কাটার জন্য অনন্য নয়, তবে আমরা যেকোনভাবে এটি ঘিরে কাজ করতে পারি। কর্মক্ষেত্রের অন্যান্য সমস্যাগুলিও সমাধান করার একটি অতিরিক্ত সুবিধা রয়েছে। পরিবর্তে আমরা যা করি তা এখানে:
@implementation NSView (MyViewAdditions)
static void MySetFrame(id self, SEL _cmd, NSRect frame);
static void (*SetFrameIMP)(id self, SEL _cmd, NSRect frame);
static void MySetFrame(id self, SEL _cmd, NSRect frame) {
// do custom work
SetFrameIMP(self, _cmd, frame);
}
+ (void)load {
[self swizzle:@selector(setFrame:) with:(IMP)MySetFrame store:(IMP *)&SetFrameIMP];
}
@end
যদিও এটি কিছুটা কম অবজেক্টিভ-সি-এর মতো দেখায় (যেহেতু এটি ফাংশন পয়েন্টার ব্যবহার করছে), এটি কোনও নামকরণ বিরোধকে এড়িয়ে চলে। নীতিগতভাবে, এটি স্ট্যান্ডার্ড সুইজলিংয়ের মতো ঠিক একই জিনিস করছে। এটি কিছুক্ষণের জন্য সংজ্ঞায়িত হওয়ার সাথে সাথে সুইজলিং ব্যবহার করছে এমন লোকদের জন্য এটি কিছুটা পরিবর্তন হতে পারে তবে শেষ পর্যন্ত, আমি মনে করি এটি আরও ভাল। সুইজলিং পদ্ধতিটি এভাবে সংজ্ঞায়িত করা হয়:
typedef IMP *IMPPointer;
BOOL class_swizzleMethodAndStore(Class class, SEL original, IMP replacement, IMPPointer store) {
IMP imp = NULL;
Method method = class_getInstanceMethod(class, original);
if (method) {
const char *type = method_getTypeEncoding(method);
imp = class_replaceMethod(class, original, replacement, type);
if (!imp) {
imp = method_getImplementation(method);
}
}
if (imp && store) { *store = imp; }
return (imp != NULL);
}
@implementation NSObject (FRRuntimeAdditions)
+ (BOOL)swizzle:(SEL)original with:(IMP)replacement store:(IMPPointer)store {
return class_swizzleMethodAndStore(self, original, replacement, store);
}
@end
নাম পরিবর্তন করে সাঁতার কাটা পদ্ধতিটির আর্গুমেন্টগুলিকে পরিবর্তন করে
এটি আমার মনের মধ্যে বড়। এই কারণে স্ট্যান্ডার্ড পদ্ধতি সুইজলিং করা উচিত নয়। আপনি মূল পদ্ধতির বাস্তবায়নে পাস হওয়া যুক্তিগুলি পরিবর্তন করছেন। এটি যেখানে এটি ঘটে:
[self my_setFrame:frame];
এই লাইনটি যা করে তা হ'ল:
objc_msgSend(self, @selector(my_setFrame:), frame);
যা বাস্তবায়নটি দেখার জন্য রানটাইম ব্যবহার করবে my_setFrame:
। একবার প্রয়োগটি পাওয়া গেলে, এটি একই যুক্তি দিয়ে বাস্তবায়নের জন্য অনুরোধ জানায়। এটি যে বাস্তবায়নটি আবিষ্কার করে তা হ'ল আসল বাস্তবায়ন setFrame:
, সুতরাং এটি এগিয়ে যায় এবং এটি কল করে, তবে _cmd
যুক্তিটি setFrame:
এটির মতো হওয়া উচিত নয় । এখন my_setFrame:
। আসল বাস্তবায়নটি এমন যুক্তি দিয়ে ডাকা হচ্ছে যা এটি প্রত্যাশা করে না যে এটি পাবে। এটা ভাল না।
একটি সহজ সমাধান আছে - উপরে সংজ্ঞায়িত বিকল্প সুইজলিং কৌশল ব্যবহার করুন। যুক্তিগুলি অপরিবর্তিত থাকবে!
সুইজার্সের ক্রমটি গুরুত্বপূর্ণ
যে পদ্ধতিতে পদ্ধতিগুলি সুইজল বিষয়গুলিতে আসে। ধরে setFrame:
নেওয়া কেবলমাত্র এটির উপর সংজ্ঞায়িত করা হয় NSView
, জিনিসগুলির এই ক্রমটি কল্পনা করুন:
[NSButton swizzle:@selector(setFrame:) with:@selector(my_buttonSetFrame:)];
[NSControl swizzle:@selector(setFrame:) with:@selector(my_controlSetFrame:)];
[NSView swizzle:@selector(setFrame:) with:@selector(my_viewSetFrame:)];
পদ্ধতিটি NSButton
সুইজল হয়ে গেলে কী হবে ? ওয়েল সবচেয়ে swizzling নিশ্চিত করবে যে এটা বাস্তবায়ন প্রতিস্থাপন না setFrame:
সকল দর্শনের জন্য, তাই এটি হবে টেনে উদাহরণস্বরূপ পদ্ধতি। -সংজ্ঞায়িত পুনরায় এই বিদ্যমান বাস্তবায়ন ব্যবহার করবে setFrame:
মধ্যে NSButton
বর্গ যাতে বাস্তবায়নের বিনিময় সকল দৃশ্য প্রভাবিত করে না। বিদ্যমান বাস্তবায়ন যার উপর নির্ধারিত হয় NSView
। সুইজল করার সময় একই জিনিস ঘটবে NSControl
(আবার NSView
বাস্তবায়নটি ব্যবহার করে )।
আপনি যখন setFrame:
একটি বোতামে কল করবেন তখন এটি আপনার সুইজল পদ্ধতিটিকে কল করবে এবং তারপরে setFrame:
মূলত সংজ্ঞায়িত পদ্ধতিটিতে সরাসরি লাফিয়ে উঠবে NSView
। NSControl
এবং NSView
swizzled বাস্তবায়নের আহ্বান করা হবে না।
তবে যদি আদেশটি ছিল:
[NSView swizzle:@selector(setFrame:) with:@selector(my_viewSetFrame:)];
[NSControl swizzle:@selector(setFrame:) with:@selector(my_controlSetFrame:)];
[NSButton swizzle:@selector(setFrame:) with:@selector(my_buttonSetFrame:)];
যেহেতু ভিউ সুইজলিং আগে স্থান নেয়, তাই নিয়ন্ত্রণের সুইজলিং সঠিক পদ্ধতিটি টানতে সক্ষম হবে । অনুরূপভাবে, যেহেতু নিয়ন্ত্রণ swizzling বোতাম swizzling সামনে, বোতাম হবে টেনে নিয়ন্ত্রণ এর swizzled বাস্তবায়ন setFrame:
। এটি কিছুটা বিভ্রান্তিকর, তবে এটি সঠিক ক্রম। আমরা কীভাবে এই ক্রমটি নিশ্চিত করতে পারি?
আবার, কেবল load
সুইজল জিনিসগুলি ব্যবহার করুন । আপনি যদি সুইজল করেন load
এবং আপনি কেবল ক্লাসে বোঝা হচ্ছে এমন পরিবর্তনগুলি করেন তবে আপনি নিরাপদে থাকবেন। load
পদ্ধতি গ্যারান্টী যে সুপার বর্গ লোড পদ্ধতি কোনো উপশ্রেণী সামনে ডাকা হবে। আমরা ঠিক সঠিক অর্ডার পাবেন!
বুঝতে অসুবিধা (পুনরাবৃত্ত লাগছে)
একটি traditionতিহ্যগতভাবে সংজ্ঞায়িত সুইজল পদ্ধতিটি দেখে আমি মনে করি কী চলছে তা বলা সত্যিই শক্ত। তবে উপরের সুইজলিংয়ের বিকল্প পদ্ধতিটি দেখে, এটি বোঝা বেশ সহজ। এটি ইতিমধ্যে সমাধান করা হয়েছে!
ডিবাগ করা কঠিন
ডিবাগিংয়ের সময় একটি বিভ্রান্তি একটি অদ্ভুত ব্যাকট্রেস দেখছে যেখানে গলিত নামগুলি মিশ্রিত হয়ে সমস্ত কিছু আপনার মাথার মধ্যে ঝাঁপিয়ে পড়ে। আবার, বিকল্প বাস্তবায়ন এটিকে সম্বোধন করে। আপনি ব্যাকট্রেসগুলিতে পরিষ্কারভাবে নামযুক্ত ফাংশন দেখতে পাবেন। তবুও, সুইজলিং ডিবাগ করা কঠিন হতে পারে কারণ সুইজলিংয়ের ফলে কী প্রভাব পড়ছে তা মনে রাখা শক্ত। আপনার কোডটি ভালভাবে নথিভুক্ত করুন (এমনকি যদি আপনি ভাবেন যে আপনিই কেবল এটি দেখেন তবেই)। ভাল অনুশীলনগুলি অনুসরণ করুন, এবং আপনি ঠিক থাকবেন। মাল্টি-থ্রেড কোডের চেয়ে ডিবাগ করা শক্ত নয়।
উপসংহার
সঠিকভাবে ব্যবহার করা হলে পদ্ধতি সুইজলিং নিরাপদ। একটি সরল নিরাপত্তা পরিমাপ আপনি গ্রহণ করতে পারেন শুধুমাত্র পাঁচমিশালী মদ্যবিশেষ হয় load
। প্রোগ্রামিংয়ের অনেক কিছুর মতো, এটি বিপজ্জনক হতে পারে তবে এর পরিণতিগুলি বোঝা আপনাকে এটিকে সঠিকভাবে ব্যবহার করতে দেয়।
1 উপরের সংজ্ঞায়িত সুইজলিং পদ্ধতিটি ব্যবহার করে আপনি যদি ট্রাম্পলাইনগুলি ব্যবহার করেন তবে আপনি জিনিসগুলিকে থ্রেডটি নিরাপদ করে তুলতে পারেন। আপনার দুটি ট্রামপোলিন লাগবে। পদ্ধতির শুরুতে, আপনাকে ফাংশন পয়েন্টারটি নির্ধারণ করতে হবে store
, কোনও ফাংশন যা স্প্রে হয়ে গেছে যতক্ষণ না ঠিকানাটি store
পরিবর্তিত হয়েছে until এটি কোনও ক্রিয়াকলাপের শর্ত এড়াতে পারে যেখানে store
ফাংশন পয়েন্টার সেট করতে সক্ষম হওয়ার আগে সুইজল পদ্ধতিটি কল করা হয়েছিল । তারপরে আপনার ক্ষেত্রে ট্রামপোলিন ব্যবহার করা দরকার যেখানে প্রয়োগটি ইতিমধ্যে শ্রেণিতে সংজ্ঞায়িত করা হয়নি এবং ট্রামপোলিন লুক রয়েছে এবং সুপার ক্লাস পদ্ধতিটি সঠিকভাবে কল করতে হবে। পদ্ধতির সংজ্ঞা দেওয়া যাতে এটি কার্যকরভাবে সুপার বাস্তবায়িত হয় তা নিশ্চিত করবে যে সুইজলিং কলগুলির ক্রম কোনও ব্যাপার না।