সমাধান
সংকলক একটি কারণ সম্পর্কে এটি সম্পর্কে সতর্ক করে দিচ্ছে। এটি খুব বিরল যে এই সতর্কতাটি কেবল উপেক্ষা করা উচিত এবং এটির চারপাশে কাজ করা সহজ। এখানে কীভাবে:
if (!_controller) { return; }
SEL selector = NSSelectorFromString(@"someMethod");
IMP imp = [_controller methodForSelector:selector];
void (*func)(id, SEL) = (void *)imp;
func(_controller, selector);
বা আরও পরিশ্রুত (যদিও পড়তে এবং প্রহরী ব্যতীত কঠিন):
SEL selector = NSSelectorFromString(@"someMethod");
((void (*)(id, SEL))[_controller methodForSelector:selector])(_controller, selector);
ব্যাখ্যা
এখানে যা চলছে আপনি নিয়ন্ত্রকের সাথে অনুরোধ পদ্ধতিটির জন্য সি ফাংশন পয়েন্টারটির জন্য জিজ্ঞাসা করছেন। সমস্ত NSObject
সাড়া দেয় methodForSelector:
, তবে আপনি class_getMethodImplementation
অবজেক্টিভ-সি রানটাইম ব্যবহার করতে পারেন (যদি আপনার কাছে কেবল প্রোটোকল রেফারেন্স থাকে তবে দরকারী id<SomeProto>
)। এই ফাংশন পয়েন্টারগুলিকে IMP
s বলা হয় এবং এগুলি সাধারণ typedef
এড ফাংশন পয়েন্টার ( id (*IMP)(id, SEL, ...)
) 1 । এটি পদ্ধতির আসল পদ্ধতিটির স্বাক্ষরের খুব কাছাকাছি হতে পারে তবে সর্বদা ঠিক মিলবে না।
একবার আপনার হয়ে গেলে IMP
, আপনাকে এটি কোনও ফাংশন পয়েন্টারে কাস্ট করতে হবে যাতে এআরসি প্রয়োজনীয় সমস্ত বিবরণ (দুটি অন্তর্নিহিত গোপন তর্ক self
এবং _cmd
প্রতিটি উদ্দেশ্য-সি পদ্ধতি কল সহ) অন্তর্ভুক্ত করে। এটি তৃতীয় লাইনে হ্যান্ডেল করা হয়েছে ( (void *)
ডানদিকে থাকাটি কম্পাইলারকে বলে দেয় যে আপনি কী করছেন এবং পয়েন্টারের ধরণগুলি মেলে না বলে একটি সতর্কতা উত্পন্ন না করার বিষয়টি আপনি জানেন)।
অবশেষে, আপনি ফাংশন পয়েন্টার 2 কল ।
জটিল উদাহরণ
যখন নির্বাচক আর্গুমেন্ট নেয় বা কোনও মান ফেরত দেয়, আপনাকে কিছুটা পরিবর্তন করতে হবে:
SEL selector = NSSelectorFromString(@"processRegion:ofView:");
IMP imp = [_controller methodForSelector:selector];
CGRect (*func)(id, SEL, CGRect, UIView *) = (void *)imp;
CGRect result = _controller ?
func(_controller, selector, someRect, someView) : CGRectZero;
সতর্কতা জন্য যুক্তি
এই সতর্কতার কারণটি হ'ল এআরসি-র সাথে রানটাইমটি জানতে হবে যে আপনি যে পদ্ধতিতে কল করছেন তার ফলাফলটি কী করা উচিত। ফলাফলের কিছু হতে পারে: void
, int
, char
, NSString *
, id
, ইত্যাদি এআরসি স্বাভাবিকভাবে অবজেক্ট প্রকারটি আপনার সাথে কাজ করছি হেডার থেকে এই তথ্য পায়। 3
সত্যিই কেবল 4 টি জিনিস রয়েছে যা প্রত্যাবর্তন মূল্যের জন্য এআরসি বিবেচনা করবে: 4
- Ignore অ বস্তুর প্রকার (
void
, int
ইত্যাদি)
- অবজেক্টের মান ধরে রাখুন, তারপরে এটি আর ব্যবহার না করা হলে ছেড়ে দিন (স্ট্যান্ডার্ড অনুমান)
- আর ব্যবহার না করা হলে নতুন অবজেক্টের মানগুলি প্রকাশ করুন (
init
/ copy
পরিবারে পদ্ধতিগুলি বা এর সাথে যুক্ত ns_returns_retained
)
- কিছুই করবেন না এবং ধরে নিন যে প্রত্যাবর্তিত অবজেক্টের মান স্থানীয় স্কোপে বৈধ হবে (যতক্ষণ না অভ্যন্তরীণ সর্বাধিক রিলিজ পুলটি শুকানো হয়, এর সাথে চিহ্নিত করা হয়
ns_returns_autoreleased
)
methodForSelector:
ধরে নেওয়া কলটি যে পদ্ধতিটি কল করছে তার রিটার্ন মান একটি বস্তু, তবে এটি ধরে রাখে না / ছেড়ে দেয় না। সুতরাং আপনার অবজেক্ট উপরের # 3 হিসাবে মুক্তি পাওয়ার কথা মনে করা হলে আপনি একটি ফুটো তৈরি শেষ করতে পারেন (অর্থাৎ, আপনি যে পদ্ধতিটি কল করছেন সেটি কোনও নতুন অবজেক্ট ফিরিয়ে দেয়)।
আপনি যে রিটার্ন void
বা অন্যান্য অ-অবজেক্টগুলিকে কল করার চেষ্টা করছেন তাদের নির্বাচিতদের জন্য , আপনি সংকলক বৈশিষ্ট্যগুলি সতর্কতাটিকে উপেক্ষা করতে সক্ষম করতে পারেন তবে এটি বিপজ্জনক হতে পারে। আমি ক্ল্যাংকে স্থানীয় পুনরায় ভেরিয়েবলগুলিতে নির্ধারিত নয় এমন রিটার্ন মানগুলি কীভাবে পরিচালনা করে তার কয়েকটি পুনরাবৃত্তির মধ্য দিয়ে যেতে দেখেছি। এআরসি সক্ষম করে এমন কোনও কারণ নেই যে এটি ব্যবহার করতে না চাইলেও এটি থেকে ফিরে আসা অবজেক্টের মানটি ধরে রাখতে এবং ছেড়ে methodForSelector:
দিতে পারে না। সংকলকের দৃষ্টিকোণ থেকে, এটি সর্বোপরি একটি অবজেক্ট। এর অর্থ এই যে আপনি যে পদ্ধতিটি কল করছেন someMethod
, যদি কোনও নন অবজেক্ট (সহ void
) ফিরিয়ে দেয় তবে আপনি কোনও আবর্জনা পয়েন্টার মান ধরে রাখতে / ছেড়ে দিতে এবং ক্র্যাশ করে শেষ করতে পারেন।
অতিরিক্ত যুক্তি
একটি বিবেচনা হ'ল এটি হ'ল একই সতর্কতাটি ঘটবে performSelector:withObject:
এবং আপনি কীভাবে সেই পদ্ধতিটি পরামিতিগুলি ব্যয় করে তা ঘোষণা না করে একই ধরণের সমস্যার মধ্যে পড়তে পারেন। এআরসি গ্রাসকৃত প্যারামিটারগুলি ঘোষণা করার অনুমতি দেয় এবং যদি পদ্ধতিটি প্যারামিটার গ্রহণ করে তবে আপনি সম্ভবত শেষ পর্যন্ত একটি জম্বি এবং ক্র্যাশে একটি বার্তা প্রেরণ করবেন। ব্রিজযুক্ত ingালাই দিয়ে এটিকে ঘিরে কাজ করার উপায় রয়েছে তবে IMP
উপরের ও ফাংশন পয়েন্টার পদ্ধতিটি কেবল উপরে ব্যবহার করা ভাল । যেহেতু গ্রাসকৃত প্যারামিটারগুলি খুব কমই একটি সমস্যা, এটি সম্ভবত আসার সম্ভাবনা নেই।
স্থির নির্বাচক
মজার বিষয় হল, সংকলক স্থিতিযুক্ত ঘোষিত নির্বাচকদের সম্পর্কে অভিযোগ করবে না:
[_controller performSelector:@selector(someMethod)];
এর কারণ হ'ল সংকলক আসলে সংকলনের সময় নির্বাচক এবং অবজেক্ট সম্পর্কে সমস্ত তথ্য রেকর্ড করতে সক্ষম। এটি কোনও কিছুর বিষয়ে কোনও অনুমান করার দরকার নেই। (উত্সটি দেখে আমি বছর খানেক আগে এটি পরীক্ষা করে দেখেছি, তবে এখনই কোনও উল্লেখ নেই))
চাপাচাপি
এই সতর্কতাটি দমন করা প্রয়োজনীয় এবং ভাল কোড ডিজাইনের হয়ে উঠতে পারে এমন পরিস্থিতিটি ভাবতে চেষ্টা করার পরে, আমি খালি আসছি। কেউ দয়া করে ভাগ করুন যদি তাদের যদি এমন কোনও অভিজ্ঞতা থাকে যেখানে এই সতর্কতাটি স্থির করা প্রয়োজন (এবং উপরের জিনিসগুলি সঠিকভাবে পরিচালনা করে না)।
অধিক
এটি NSMethodInvocation
পরিচালনা করার জন্য এটিও তৈরি করা সম্ভব , তবে এটি করার জন্য আরও অনেক বেশি টাইপ করা দরকার এবং এটি ধীর গতির হয়, তাই এটি করার খুব কম কারণ আছে।
ইতিহাস
যখন performSelector:
পদ্ধতির পরিবারটি প্রথম উদ্দেশ্য-সিতে যুক্ত হয়েছিল, তখন আরসিটির অস্তিত্ব ছিল না। এআরসি তৈরির সময়, অ্যাপল সিদ্ধান্ত নিয়েছে যে এই পদ্ধতিগুলির জন্য বিকাশকারীদের অন্য উপায় ব্যবহারের দিকে পরিচালিত করার উপায় হিসাবে একটি মনোনীত নির্বাচকের মাধ্যমে স্বেচ্ছাসেবক বার্তা প্রেরণ করার সময় স্মৃতি কীভাবে পরিচালনা করা উচিত তা স্পষ্টভাবে সংজ্ঞায়িত করার জন্য একটি সতর্কতা উত্পন্ন করা উচিত। উদ্দেশ্য-সি-তে, বিকাশকারীরা কাঁচা ফাংশন পয়েন্টারে সি স্টাইলের ক্যাসেট ব্যবহার করে এটি করতে সক্ষম হন।
সুইফ্ট প্রবর্তনের সাথে সাথে অ্যাপল পরিবারের অভ্যন্তরীণ পদ্ধতিগুলিকে "সহজাতভাবে অনিরাপদ" হিসাবে নথিভুক্ত করেছে performSelector:
এবং সেগুলি সুইফটে উপলব্ধ নয়।
সময়ের সাথে সাথে আমরা এই অগ্রগতি দেখেছি:
- অবজেক্টিভ-সি এর প্রাথমিক সংস্করণগুলি অনুমতি দেয়
performSelector:
(ম্যানুয়াল মেমরি পরিচালনা)
- এআরসি সহ উদ্দেশ্য-সি ব্যবহারের জন্য সতর্ক করে
performSelector:
performSelector:
"অভ্যন্তরীণভাবে অনিরাপদ" হিসাবে সুইফ্টের এই পদ্ধতিগুলিতে অ্যাক্সেস এবং নথির অ্যাক্সেস নেই documents
নামী নির্বাচিতের উপর ভিত্তি করে বার্তা প্রেরণের ধারণাটি অবশ্য "অন্তর্নিহিত অনিরাপদ" বৈশিষ্ট্য নয়। এই ধারণাটি দীর্ঘদিন ধরে অবজেক্টিভ-সি পাশাপাশি আরও অনেক প্রোগ্রামিং ভাষায় ব্যবহৃত হয়েছে।
1 সমস্ত অবজেক্টিভ-সি পদ্ধতিতে দুটি লুকানো আর্গুমেন্ট থাকে self
এবং _cmd
এটি আপনি যখন কোনও পদ্ধতিতে কল করেন তখন তা স্পষ্টভাবে যুক্ত হয়।
2 একটি NULL
ফাংশন কল করা সি তে নিরাপদ নয় কন্ট্রোলারের উপস্থিতি যাচাই করতে ব্যবহৃত প্রহরীটি নিশ্চিত করে যে আমাদের কোনও বস্তু আছে। সুতরাং আমরা জানি যে আমরা একটি কিনবো IMP
থেকে methodForSelector:
(এটা হতে পারে যদিও _objc_msgForward
, বার্তা ফরওয়ার্ডিং সিস্টেমের মধ্যে এন্ট্রি)। মূলত, জায়গায় পাহারাদারের সাথে, আমরা জানি আমাদের কল করার একটি ফাংশন রয়েছে।
3 প্রকৃতপক্ষে, আপনাকে যদি অবজেক্ট হিসাবে ঘোষণা করে id
এবং আপনি সমস্ত শিরোনাম আমদানি না করে থাকেন তবে ভুল তথ্য পাওয়া সম্ভব । আপনি কোডটিতে ক্র্যাশগুলি শেষ করতে পারেন যা সংকলকটি ঠিক মনে করে। এটি খুব বিরল, তবে ঘটতে পারে। সাধারণত আপনি কেবল একটি সতর্কতা পাবেন যে দুটি পদ্ধতির স্বাক্ষরগুলির মধ্যে কোনটি বেছে নেবে তা এটি জানে না।
4 আরও তথ্যের জন্য ধরে রাখা পুনরুদ্ধার মান এবং অপ্রত্যাশিত রিটার্ন মানগুলির বিষয়ে এআরসি রেফারেন্স দেখুন ।