objective c - API এ বাস্তবায়ন করার সময় আমি কীভাবে ব্লকগুলিতে নিজেকে ধরে রাখতে পারি?




objective-c ios (6)

আমার একটি কর্মক্ষম অ্যাপ্লিকেশন আছে এবং আমি Xcode 4.2 এ এআরসি এ রূপান্তর করতে কাজ করছি। প্রাক চেক সতর্কতা এক একটি বজায় রাখা চক্র নেতৃস্থানীয় ব্লক self দৃঢ়ভাবে জড়িত জড়িত থাকে। আমি ইস্যু illustrated একটি সহজ কোড নমুনা তৈরি করেছি। আমি বিশ্বাস করি এই অর্থটি আমি বুঝি তবে বুঝতে পারছি না যে এই ধরণের পরিস্থিতি বাস্তবায়ন করার জন্য "সঠিক" বা প্রস্তাবিত উপায়টি আমি নিশ্চিত নই।

  • স্ব শ্রেণী মায়াপআই একটি উদাহরণ
  • নীচের কোডটি কেবলমাত্র আমার প্রশ্নের সাথে সম্পর্কিত বস্তুর সাথে সম্পর্কিত মিথস্ক্রিয়াগুলি দেখানোর জন্য সরলীকৃত
  • অনুমান করুন যে MyAPI একটি রিমোট উত্স থেকে তথ্য পায় এবং MyDataProcessor সেই ডেটাতে কাজ করে এবং আউটপুট তৈরি করে
  • প্রসেসর অগ্রগতি ও রাষ্ট্র যোগাযোগ ব্লক সঙ্গে কনফিগার করা হয়

কোড নমুনা:

// code sample
self.delegate = aDelegate;

self.dataProcessor = [[MyDataProcessor alloc] init];

self.dataProcessor.progress = ^(CGFloat percentComplete) {
    [self.delegate myAPI:self isProcessingWithProgress:percentComplete];
};

self.dataProcessor.completion = ^{
    [self.delegate myAPIDidFinish:self];
    self.dataProcessor = nil;
};

// start the processor - processing happens asynchronously and the processor is released in the completion block
[self.dataProcessor startProcessing];

প্রশ্ন: আমি কি "ভুল" করছি এবং / অথবা এটি এআরসি সম্মেলনগুলির সাথে সামঞ্জস্য করার জন্য কীভাবে সংশোধন করা উচিত?


সংক্ষিপ্ত উত্তর

self সরাসরি অ্যাক্সেস করার পরিবর্তে, আপনাকে এটি প্রত্যক্ষভাবে অ্যাক্সেস করা উচিত, এমন একটি রেফারেন্স থেকে যা ধরে রাখা হবে না। আপনি যদি স্বয়ংক্রিয় রেফারেন্স কাউন্টিং (এআরসি) ব্যবহার না করেন তবে আপনি এটি করতে পারেন:

__block MyDataProcessor *dp = self;
self.progressBlock = ^(CGFloat percentComplete) {
    [dp.delegate myAPI:dp isProcessingWithProgress:percentComplete];
}

__block শব্দটি ভেরিয়েবলগুলি চিহ্নিত করে যা ব্লকের ভিতরে সংশোধন করা যেতে পারে (আমরা এটি করছি না) তবে ব্লকটি বজায় রাখার সময় স্বয়ংক্রিয়ভাবে সেগুলি সংরক্ষণ করা হয় না (যদি না আপনি এআরসি ব্যবহার করেন)। যদি আপনি এটি করেন তবে আপনাকে অবশ্যই নিশ্চিত করতে হবে যে MyDataProcessor ইনস্ট্যান্সটি প্রকাশিত হওয়ার পরে ব্লকটি চালানোর জন্য অন্য কিছু করার চেষ্টা করা হচ্ছে না। (আপনার কোডের কাঠামো দেওয়া, এটি একটি সমস্যা হওয়া উচিত নয়।) __block সম্পর্কে আরও পড়ুন

আপনি যদি এআরসি ব্যবহার করেন তবে __block পরিবর্তনের __block এবং রেফারেন্সটি বজায় রাখা হবে, এর পরিবর্তে আপনাকে এটি __weak পরিবর্তে ঘোষণা করা উচিত।

দীর্ঘ উত্তর

ধরুন আপনার এই রকম কোড ছিল:

self.progressBlock = ^(CGFloat percentComplete) {
    [self.delegate processingWithProgress:percentComplete];
}

এখানে সমস্যা যে স্ব ব্লকের একটি রেফারেন্স বজায় রাখা হয়; ইতোমধ্যে ব্লকটি তার প্রতিনিধি সম্পত্তি ফিরিয়ে আনতে এবং প্রতিনিধিকে একটি পদ্ধতি পাঠানোর জন্য নিজের কাছে একটি রেফারেন্স বজায় রাখতে হবে। যদি আপনার অ্যাপের অন্য সমস্ত জিনিস এই বস্তুর তার রেফারেন্সটি প্রকাশ করে তবে তার বজায় রাখা গণনা শূন্য হবে না (কারণ ব্লক এটি নির্দেশ করছে) এবং ব্লকটি কোনও ভুল কাজ করছে না (কারণ বস্তু এটি নির্দেশ করছে) এবং তাই বস্তুর জোড়া হিপ মধ্যে লিক হবে, মেমরি অধিষ্ঠিত কিন্তু একটি ডিবাগার ছাড়া চিরতরে অ্যাক্সেসযোগ্য। দুঃখজনক, সত্যিই।

যে ক্ষেত্রে সহজে এই কাজ করে ঠিক করা যেতে পারে:

id progressDelegate = self.delegate;
self.progressBlock = ^(CGFloat percentComplete) {
    [progressDelegate processingWithProgress:percentComplete];
}

এই কোডে, স্ব ব্লকটি ধরে রেখেছে, ব্লক প্রতিনিধিকে ধরে রেখেছে, এবং কোন চক্র নেই (এখানে থেকে দৃশ্যমান; প্রতিনিধি আমাদের অবজেক্টটি ধরে রাখতে পারে তবে এটি এখন আমাদের হাত থেকে বেরিয়ে এসেছে)। এই কোডটি একইভাবে একটি লিক ঝুঁকির সম্মুখীন হবে না, কারণ ব্লক তৈরি হওয়ার সময় প্রতিনিধি সম্পত্তিটির মানটি ধরা হয়, এটি সঞ্চালিত হওয়ার সময় দেখার পরিবর্তে। একটি পার্শ্ব প্রতিক্রিয়া হল যে, যদি আপনি এই ব্লকটি তৈরি করার পরে প্রতিনিধি পরিবর্তন করেন তবে ব্লকটি পুরানো প্রতিনিধিকে আপডেট বার্তা পাঠাবে। ঘটতে পারে কিনা বা না আপনার অ্যাপ্লিকেশন উপর নির্ভর করে।

এমনকি আপনি যদি সেই আচরণের সাথে শান্ত ছিলেন তবে আপনি এখনও আপনার ক্ষেত্রে সেই কৌশলটি ব্যবহার করতে পারবেন না:

self.dataProcessor.progress = ^(CGFloat percentComplete) {
    [self.delegate myAPI:self isProcessingWithProgress:percentComplete];
};

এখানে আপনি পদ্ধতি কলটিতে সরাসরি প্রতিনিধিকে self প্রেরণ করছেন, তাই আপনাকে কোথাও এটিকে পেতে হবে। ব্লক টাইপের সংজ্ঞা সম্পর্কে আপনার যদি নিয়ন্ত্রণ থাকে, তবে সেরা জিনিসটিকে ব্লকটিতে একটি পরামিতি হিসাবে প্রেরণ করা হবে:

self.dataProcessor.progress = ^(MyDataProcessor *dp, CGFloat percentComplete) {
    [dp.delegate myAPI:dp isProcessingWithProgress:percentComplete];
};

এই সমাধানটি ধরে রাখার চক্র এড়ানো এবং সর্বদা বর্তমান প্রতিনিধিকে কল করে।

আপনি ব্লক পরিবর্তন করতে না পারেন, আপনি এটি মোকাবেলা করতে পারে। একটি বজায় রাখা চক্র একটি সতর্কতা, একটি ত্রুটি না কারণ, তারা আপনার অ্যাপ্লিকেশন জন্য অগত্যা নাশব্দ বানান না। অপারেশন সম্পন্ন হলে MyDataProcessor ব্লকগুলি মুক্ত করতে পারবেন, তার অভিভাবক আগেই এটি মুক্ত করার চেষ্টা করবে, চক্র ভেঙে যাবে এবং সবকিছু ঠিকঠাক পরিষ্কার হবে। যদি আপনি এই বিষয়ে নিশ্চিত হতে পারেন, তবে সঠিক #pragma কোডটি ব্লক করার জন্য #pragma ব্যবহার করতে হবে। (অথবা একটি প্রতি-ফাইল কম্পাইলার পতাকা ব্যবহার করুন। তবে সমগ্র প্রকল্পের জন্য সতর্কতাটি অক্ষম করবেন না।)

এছাড়াও আপনি উপরে একটি অনুরূপ কৌশল ব্যবহার করতে পারেন, একটি রেফারেন্স দুর্বল বা unretained ঘোষণা এবং ব্লক যে ব্যবহার করে। উদাহরণ স্বরূপ:

__weak MyDataProcessor *dp = self; // OK for iOS 5 only
__unsafe_unretained MyDataProcessor *dp = self; // OK for iOS 4.x and up
__block MyDataProcessor *dp = self; // OK if you aren't using ARC
self.progressBlock = ^(CGFloat percentComplete) {
    [dp.delegate myAPI:dp isProcessingWithProgress:percentComplete];
}

উপরের তিনটিই আপনাকে ফলাফল বজায় রেখে একটি রেফারেন্স দেবে, যদিও তারা সবাই একটু ভিন্নভাবে আচরণ করে: __weak অবজেক্টটি প্রকাশ করার সময় রেফারেন্স শূন্য করার চেষ্টা করবে; __unsafe_unretained আপনাকে একটি অবৈধ পয়েন্টার দিয়ে ছেড়ে চলে যাবে; __block প্রকৃতপক্ষে আরেকটি স্তর __block যোগ করবে এবং আপনাকে ব্লকের মধ্যে থেকে রেফারেন্সের মান পরিবর্তন করতে সহায়তা করবে (এই ক্ষেত্রে অপ্রাসঙ্গিক, যেহেতু dp অন্য কোথাও ব্যবহার করা হয় না)।

আপনি কী কোড পরিবর্তন করতে পারবেন এবং আপনি কী করতে পারবেন তা নির্ভর করে সেরাটি নির্ভর করবে। কিন্তু আশা করি এটি কিভাবে এগিয়ে যেতে হবে তার কিছু ধারনা দিয়েছেন।


আপনি যদি নিশ্চিত হন যে আপনার কোড কোনও স্থির চক্র তৈরি করবে না বা চক্রটি পরে ভাঙ্গবে, তবে সতর্কতাটি নীরব করার সবচেয়ে সহজ উপায় হল:

// code sample
self.delegate = aDelegate;

self.dataProcessor = [[MyDataProcessor alloc] init];

[self dataProcessor].progress = ^(CGFloat percentComplete) {
    [self.delegate myAPI:self isProcessingWithProgress:percentComplete];
};

[self dataProcessor].completion = ^{
    [self.delegate myAPIDidFinish:self];
    self.dataProcessor = nil;
};

// start the processor - processing happens asynchronously and the processor is released in the completion block
[self.dataProcessor startProcessing];

এই কাজটি কারন হল যে যখন ডক-অ্যাক্সেসের বৈশিষ্ট্যগুলি Xcode এর বিশ্লেষণ দ্বারা বিবেচনা করা হয়, এবং সেইজন্য

x.y.z = ^{ block that retains x}

x এর y দ্বারা বজায় রাখা (বরাদ্দ বাম পাশে) এবং x x (ডান পাশে) দ্বারা ধরে রাখা, পদ্ধতি কলগুলি একই বিশ্লেষণের বিষয় নয়, এমনকি যখন তারা সম্পত্তি-অ্যাক্সেস পদ্ধতির কল হয় যেগুলি ডট-অ্যাক্সেসের সমতুল্য, এমনকি যখন সেই সম্পত্তি অ্যাক্সেস পদ্ধতিগুলি কম্পাইলার-জেনারেট হয় তখনও

[x y].z = ^{ block that retains x}

শুধুমাত্র ডান দিকটিকে ধরে রাখার (x x দ্বারা) তৈরি হিসাবে দেখা হয় এবং কোনও চক্রের সতর্কতা তৈরি করা হয় না।


একটি সাধারণ সমাধান জন্য, আমি এই precompile শিরোনাম সংজ্ঞায়িত আছে। Capturing এড়াতে এবং এখনও id ব্যবহার এড়ানো দ্বারা কম্পাইলার সাহায্য সক্ষম

#define BlockWeakObject(o) __typeof(o) __weak
#define BlockWeakSelf BlockWeakObject(self)

তারপর কোডে আপনি করতে পারেন:

BlockWeakSelf weakSelf = self;
self.dataProcessor.completion = ^{
    [weakSelf.delegate myAPIDidFinish:weakSelf];
    weakSelf.dataProcessor = nil;
};


ভবিষ্যতে চক্র ভাঙ্গা হবে যখন আপনি ইতিবাচক যখন সতর্কবার্তা দমন বিকল্প আছে:

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-retain-cycles"

self.progressBlock = ^(CGFloat percentComplete) {
    [self.delegate processingWithProgress:percentComplete];
}

#pragma clang diagnostic pop

এই ভাবে আপনি __weak , self আলাইজিং এবং স্পষ্ট ivar prefixing সঙ্গে কাছাকাছি বানর আছে না।


সতর্কতা => "ব্লক ভিতরে আত্ম capturing সম্ভবত একটি চক্র চক্র হতে পারে"

যখন আপনি স্বর বা তার সম্পত্তিটিকে ব্লকের অভ্যন্তরে উল্লেখ করেন যা দৃঢ়ভাবে স্বর দ্বারা ধরে রাখা হয় তার চেয়ে উপরে সতর্কতা দেখায়।

তাই এটি এড়ানো জন্য আমরা এটি একটি সপ্তাহ রেফারেন্স করতে হবে

__weak typeof(self) weakSelf = self;

তাই ব্যবহার করার পরিবর্তে

blockname=^{
    self.PROPERTY =something;
}

আমরা ব্যবহার করা উচিত

blockname=^{
    weakSelf.PROPERTY =something;
}

নোট: চক্র বজায় রাখা সাধারণত হয় যখন কিছু দুটি বস্তু একে অপরকে উল্লেখ করে যার দ্বারা উভয় রেফারেন্স গণনা = 1 এবং তাদের ডেলোক পদ্ধতি কখনও বলা হয় না।





automatic-ref-counting