আপনি আইভার ব্যবহার করবেন কেন?


153

আমি সাধারণত এই প্রশ্নটি অন্যভাবে জিজ্ঞাসা করতে দেখি, যেমন প্রতিটি আইভারের সম্পত্তি হতে হবে? (এবং আমি এই প্রশ্নটিতে ববমের উত্তর পছন্দ করি)।

আমি আমার কোডে প্রায় একচেটিয়াভাবে সম্পত্তি ব্যবহার করি। তবে প্রতিবারই, আমি এমন একজন ঠিকাদারের সাথে কাজ করি যিনি দীর্ঘদিন ধরে আইওএস-এ বিকাশ করছেন এবং একটি traditionalতিহ্যবাহী গেম প্রোগ্রামার। তিনি এমন কোড লিখেছেন যা প্রায় কোনও সম্পত্তিই ঘোষণা করে না এবং আইভারগুলিতে ঝুঁকে পড়ে। আমি ধরে নিলাম তিনি এটি করেছেন কারণ ১।) গেটর / সেটারের মধ্য দিয়ে না যাওয়ার ন্যূনতম পারফরম্যান্স লাভের জন্য অবজেক্টিভ সি 2.0 (অক্টোবর '07) এবং ২) অবধি সম্পত্তি সর্বদা উপস্থিত না থাকায় তিনি এর সাথে অভ্যস্ত ছিলেন।

তিনি এমন কোড লিখেছেন যা ফাঁস হয় না, আমি এখনও তাকে আইভরের চেয়ে বেশি সম্পত্তি ব্যবহার করতে পছন্দ করব। আমরা এটি সম্পর্কে কথা বলেছি এবং তিনি কম বেশি সম্পত্তি ব্যবহার করার কারণ দেখেন না কারণ আমরা কেভিও ব্যবহার করছিলাম না এবং তিনি স্মৃতি সমস্যার সমাধান করার অভিজ্ঞতা অর্জন করেছেন।

আমার প্রশ্নটি আরও ... আপনি কেন কখনও আইভার পিরিয়ড ব্যবহার করতে চান - অভিজ্ঞ বা না। একটি পারফরম্যান্স পার্থক্য আছে কি সত্যিই একটি আইভার ব্যবহার ন্যায়সঙ্গত হবে?

এছাড়াও স্পষ্টকরণের পয়েন্ট হিসাবে, আমি প্রয়োজন অনুসারে সেটার এবং গেটারগুলিকে ওভাররাইড করি এবং গিটার / সেটারের অভ্যন্তরের সাথে সম্পর্কিত আইভারটি ব্যবহার করি। যাইহোক, কোনও গেটর / সেটার বা আরআইডি এর বাইরে আমি সর্বদা self.myPropertyবাক্য গঠন ব্যবহার করি ।


সম্পাদনা 1

আমি ভাল প্রতিক্রিয়া সব প্রশংসা করি। আমি যেটিকে ভুল বলে মনে করতে চাই তা হ'ল আইভারের সাহায্যে আপনি এনক্যাপসুলেশন পাবেন যেখানে আপনার সম্পত্তি নেই। শ্রেণীর ধারাবাহিকতায় কেবল সম্পত্তিটি সংজ্ঞায়িত করুন। এটি বহিরাগতদের থেকে সম্পত্তি গোপন করবে। আপনি ইন্টারফেসে সম্পত্তি কেবল পঠন হিসাবে ঘোষণা করতে পারেন এবং বাস্তবায়নে পাঠ্য রাইট হিসাবে এটি পুনরায় সংজ্ঞা দিতে পারেন:

// readonly for outsiders
@property (nonatomic, copy, readonly) NSString * name;

এবং ক্লাস ধারাবাহিকতা আছে:

// readwrite within this file
@property (nonatomic, copy) NSString * name;

এটি সম্পূর্ণরূপে "ব্যক্তিগত" থাকার জন্য এটি কেবল শ্রেণির ধারাবাহিকতায় ঘোষণা করে।


2
আকর্ষণীয় প্রশ্নের জন্য উত্সাহিত করুন - ভালভাবে লিখুন এবং এটিও যে আমি আইভারদের জন্য এই মামলাটি শুনতে চাই কারণ মনে হয় এটি আমাকে স্যামের মতো করতে শেখানো হয়েছে।
দামো

2
নোট করুন যে অটোমেটিক রেফারেন্স কাউন্টিং (এআরসি) ivars তে একই বৈশিষ্ট্য হিসাবে একই মেমরি পরিচালনার সুবিধাগুলি প্রয়োগ করে, তাই আরসি কোডে পার্থক্যটি সত্যই এনক্যাপসুলেশন সম্পর্কে about
বেনজাদো

1
আপনার প্রশ্ন এবং বিশেষত সম্পাদনা 1 অংশটি আরও অনেক তথ্যপূর্ণ তার পরে নির্বাচিত উত্তর।
ব্যবহারকারী523234

1
সম্পাদনা -১-তে: আমি মনে করি কী-ভ্যালু-কোডিংয়ের সাথে .h মধ্যে কেবলমাত্র একমাত্র ঘোষণাপত্রের মধ্যে, প্রতিটি সম্পত্তি পড়তে এবং লিখতে সম্ভব হয়, যেমন: [অবজেক্ট সেটভ্যালু: [এনএসএন নাম্বার উইথ আইট: 20] ফরকি: @ "সম্পত্তি নাম "];
বাইনারি

1
@ আপনার সম্পাদনায় সাম 1: আপনি যদি কোনও ব্যক্তিগত সম্পত্তি ব্যবহার করেন এবং .m ফাইলে শ্রেণি সম্প্রসারণ / ধারাবাহিকতা ব্যবহার করেন এটি সাবক্লাসের জন্য দৃশ্যমান নয়। আপনার আবার কোডটি লিখতে হবে বা ক্লাস এক্সটেনশনের সাথে অন্য .h ব্যবহার করতে হবে। @ সুরক্ষিত / ডিফল্ট সহ আরও সহজ।
বাইনারি

উত্তর:


100

encapsulation

আইভারটি যদি ব্যক্তিগত হয় তবে প্রোগ্রামের অন্যান্য অংশগুলি এটি সহজেই পেতে পারে না। একটি ঘোষিত সম্পত্তি সহ, চালাক লোকেরা অ্যাক্সেসকারীদের মাধ্যমে সহজেই অ্যাক্সেস করতে এবং পরিবর্তন করতে পারে।

কর্মক্ষমতা

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

অনানুষ্ঠানিক প্রকার

উদাহরণ: আপনার যদি সি ++ টাইপ থাকে তবে কখনও কখনও কখনও সরাসরি অ্যাক্সেসের জন্য আরও ভাল পন্থা। প্রকারটি অনুলিপিযোগ্য নয়, বা এটি অনুলিপি হতে পারে না v

Multithreading

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

প্রোগ্রামের সঠিকতা

যেহেতু সাবক্লাসগুলি যে কোনও পদ্ধতিকে ওভাররাইড করতে পারে তাই আপনি অবশেষে দেখতে পাচ্ছেন যে আপনার রাষ্ট্রকে যথাযথভাবে পরিচালনা করা এবং ইন্টারফেসে লেখার মধ্যে একটি অর্থপূর্ণ পার্থক্য রয়েছে। প্রোগ্রামের নির্ভুলতার জন্য সরাসরি অ্যাক্সেস বিশেষত আংশিকভাবে নির্মিত রাষ্ট্রগুলিতে সাধারণ - আপনার প্রারম্ভিকদের মধ্যে এবং ইন dealloc, সরাসরি অ্যাক্সেস ব্যবহার করা ভাল। আপনি এটি কোনও অ্যাক্সেসর, সুবিধাযুক্ত নির্মাণকারীর বাস্তবায়নেও সাধারণ দেখতে পাচ্ছেন copy,mutableCopy এবং সংরক্ষণ / ধারাবাহিকতাতে বাস্তবায়নের।

এটি আরও ঘন ঘন হিসাবে ঘন ঘন সব কিছু থেকে সরানো একটি সার্বজনীন পাঠ্য লেখার অ্যাক্সেসরের মানসিকতা রয়েছে যা তার প্রয়োগের বিশদ / ডেটা ভালভাবে আড়াল করে। কখনও কখনও আপনি সঠিকভাবে পার্শ্ব প্রতিক্রিয়া কাছাকাছি পদক্ষেপ প্রয়োজন একটি সাবক্লাস 'ওভাররাইড সঠিক কাজ করার জন্য প্রবর্তন করতে পারে।

বাইনারি আকার

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

জটিলতা হ্রাস করে

কিছু ক্ষেত্রে, + টাইপ + যুক্ত করা একেবারেই অপ্রয়োজনীয়, সরল ভেরিয়েবল যেমন একটি পদ্ধতিতে লিখিত এবং অন্যটিতে পড়ার জন্য সেই সমস্ত অতিরিক্ত স্ক্যাফোোল্ডিং বজায় রাখে।


সম্পত্তি বা অ্যাক্সেসরগুলি ব্যবহার করা খারাপ তা বলা মোটেই ঠিক নয় - প্রত্যেকটির গুরুত্বপূর্ণ সুবিধা এবং সীমাবদ্ধতা রয়েছে। অনেকগুলি ওও ভাষা এবং ডিজাইনের পদ্ধতির মত, আপনারও ওজেজে যথাযথ দৃশ্যমানতা সহ অ্যাক্সেসরদের পক্ষে নেওয়া উচিত। আপনার বিচ্যুত হওয়ার সময়গুলি হবে। সেই কারণে, আমি মনে করি ivar ঘোষণা করে (যেমন এটি ঘোষণা @private) বাস্তবায়নের মধ্যে সরাসরি অ্যাক্সেসগুলিকে সীমাবদ্ধ করা সবচেয়ে ভাল ।


পুনরায় সম্পাদনা 1:

আমাদের মধ্যে বেশিরভাগ কীভাবে একটি লুকানো অ্যাক্সেসরকে ডায়নামিকভাবে কল করতে পারেন তা (যতক্ষণ না আমরা নামটি জানি ...) or এদিকে, আমাদের অধিকাংশ আছে না মুখস্ত কিভাবে সঠিকভাবে এক্সেস ivars যা (KVC পরলোক) দৃশ্যমান নয় করতে। শ্রেণির ধারাবাহিকতা সাহায্য করে , তবে এটি দুর্বলতার পরিচয় দেয়।

এই কর্মক্ষেত্রের সুস্পষ্ট:

if ([obj respondsToSelector:(@selector(setName:)])
  [(id)obj setName:@"Al Paca"];

এখন এটি কেবল আইভার দিয়ে চেষ্টা করুন এবং কেভিসি ছাড়াই।


@ সাম ধন্যবাদ, এবং ভাল প্রশ্ন! পুনরায় জটিলতা: এটি অবশ্যই উভয় পথে যায়। পুনরায় এনক্যাপসুলেশন - আপডেট হয়েছে
জাস্টিন

@ বুবাম আরইআর : নির্দিষ্ট উদাহরণ যদিও আমি আপনার সাথে একমত হয়েছি যে এটিই ভুল সমাধান, তবে আমি অনুভব করতে পারি না যে অনেক অভিজ্ঞ অবজেক ডেভস বিশ্বাস করেন যে এটি ঘটবে না; আমি এটি অন্যের প্রোগ্রামগুলিতে দেখেছি এবং অ্যাপ স্টোরগুলি ব্যক্তিগত অ্যাপল এপিআইগুলির ব্যবহার নিষিদ্ধকরণের দিকে চলে গেছে।
জাস্টিন

1
আপনি কী আইটেম-> ফু দিয়ে একটি ব্যক্তিগত আইভার অ্যাক্সেস করতে পারবেন না? মনে রাখা খুব কঠিন নয়।
নিক লকউড

1
আমার অর্থ আপনি সি -> সিনট্যাক্স ব্যবহার করে অবজেক্ট থেকে পয়েন্টার ডিফারেন্স ব্যবহার করে এটি অ্যাক্সেস করতে পারবেন। অবজেক্টিভ-সি ক্লাসগুলি মূলত হুডের নীচে স্ট্রাক্ট হয় এবং স্ট্রাক্টকে একটি পয়েন্টার দেওয়া হয়, সদস্যদের অ্যাক্সেসের জন্য সি সিনট্যাক্সটি হয় -> যা উদ্দেশ্য সি-ক্লাসে ivars এর জন্যও কাজ করে।
নিক লকউড

1
@ নিকলোকউড যদি আইভার হয় তবে সংকলকটির @privateসদস্য এবং শ্রেণীর বাইরের সদস্যের প্রবেশাধিকার নিষিদ্ধ করা উচিত এবং উদাহরণের পদ্ধতিগুলি - এটি কি আপনি দেখছেন না?
জাস্টিন

76

আমার জন্য এটি সাধারণত পারফরম্যান্স হয়। একটি অবজেক্টের আইভার অ্যাক্সেস করা যেমন স্ট্রাকযুক্ত মেমোরিতে পয়েন্টার ব্যবহার করে সিতে স্ট্রাক্ট সদস্যের অ্যাক্সেসের তত দ্রুত। আসলে, অবজেক্টিভ-সি অবজেক্টগুলি মূলত সি স্ট্রাক্টগুলি গতিশীল বরাদ্দ মেমরিতে অবস্থিত। এটি সাধারণত আপনার কোডটি পেতে পারে তত দ্রুত, হ্যান্ড অপ্টিমাইজড অ্যাসেমব্লিং কোডও এর চেয়ে দ্রুততর হতে পারে না।

গিটার / সেটিংয়ের মাধ্যমে আইভার অ্যাক্সেস করার সাথে একটি অবজেক্টিভ-সি পদ্ধতি কল কল জড়িত, যা একটি "সাধারণ" সি ফাংশন কলের চেয়ে অনেক ধীর (কমপক্ষে 3-4 বার) এবং এমনকি একটি সাধারণ সি ফাংশন কল ইতিমধ্যে একাধিকবার ধীর হতে পারে একটি কাঠামো সদস্য অ্যাক্সেস করা। আপনার সম্পত্তি গুণাবলীর উপর নির্ভর করে, সেটার / সংগ্রহকারী কম্পাইলার দ্বারা উত্পন্ন বাস্তবায়ন ফাংশন অন্য সি ফাংশন কল থাকতে পারে objc_getProperty/ objc_setPropertyএই থাকবে হিসাবে retain/ copy/ autoreleaseযেমন বস্তু প্রয়োজন এবং আরও পারমাণবিক বৈশিষ্ট্যের জন্য spinlocking সঞ্চালন যেখানে প্রয়োজন। এটি সহজেই খুব ব্যয়বহুল হতে পারে এবং আমি 50% ধীর হওয়ার কথা বলছি না।

আসুন এটি চেষ্টা করুন:

CFAbsoluteTime cft;
unsigned const kRuns = 1000 * 1000 * 1000;

cft = CFAbsoluteTimeGetCurrent();
for (unsigned i = 0; i < kRuns; i++) {
    testIVar = i;
}
cft = CFAbsoluteTimeGetCurrent() - cft;
NSLog(@"1: %.1f picoseconds/run", (cft * 10000000000.0) / kRuns);

cft = CFAbsoluteTimeGetCurrent();
for (unsigned i = 0; i < kRuns; i++) {
    [self setTestIVar:i];
}
cft = CFAbsoluteTimeGetCurrent() - cft;
NSLog(@"2: %.1f picoseconds/run", (cft * 10000000000.0) / kRuns);

আউটপুট:

1: 23.0 picoseconds/run
2: 98.4 picoseconds/run

এটি ৪.২৮ গুণ ধীর এবং এটি একটি অ-পারমাণবিক আদিম প্রান্ত ছিল, এটি সর্বোত্তমতম ঘটনা ; বেশিরভাগ অন্যান্য ক্ষেত্রে আরও খারাপ হয় (একটি পারমাণবিক NSString *সম্পত্তি চেষ্টা করুন !)। সুতরাং আপনি যদি প্রতিটি আইভার অ্যাক্সেসের তুলনায় 4-5 গুণ কম ধীরে ধীরে বেঁচে থাকতে পারেন তবে বৈশিষ্ট্যগুলি ব্যবহার করা ভাল (কমপক্ষে যখন এটি কার্য সম্পাদনের ক্ষেত্রে আসে), তবে, এমন অনেকগুলি পরিস্থিতি রয়েছে যেখানে এই ধরনের পারফরম্যান্স ড্রপ হয় সম্পূর্ণ অগ্রহণযোগ্য।

আপডেট 2015-10-20

কিছু লোক যুক্তি দেখান যে এটি কোনও আসল বিশ্বের সমস্যা নয়, উপরের কোডটি নিখুঁতভাবে সিন্থেটিক এবং আপনি কখনই এটি বাস্তব প্রয়োগে লক্ষ্য করবেন না। ঠিক আছে, তাহলে আসুন একটি বাস্তব বিশ্বের নমুনা চেষ্টা করি।

নীচের কোডগুলি Accountঅবজেক্টগুলি সংজ্ঞায়িত করে। কোনও অ্যাকাউন্টে এমন বৈশিষ্ট্য রয়েছে যা তার মালিকের নাম ( NSString *), লিঙ্গ ( enum), এবং বয়স ( unsigned) পাশাপাশি একটি ভারসাম্য ( int64_t) বর্ণনা করে। একটি অ্যাকাউন্ট অবজেক্টের একটি initপদ্ধতি এবং একটি compare:পদ্ধতি রয়েছে। compare:পদ্ধতিটি এই হিসাবে সংজ্ঞায়িত করা হয়: পুরুষের আগে মহিলা আদেশ, নাম বর্ণানুক্রমিকভাবে অর্ডার করে, পুরানো আগে যুবক অর্ডারগুলি, ভারসাম্যগুলি কম থেকে কম।

আসলে এখানে দুটি অ্যাকাউন্ট ক্লাস রয়েছে, AccountAএবং AccountB। যদি আপনি তাদের বাস্তবায়নের দিকে লক্ষ্য করেন তবে লক্ষ্য করবেন যে এগুলি একটি ব্যাতিক্রম সহ প্রায় সম্পূর্ণ অভিন্ন: compare:পদ্ধতি। AccountAবস্তু অ্যাক্সেস তাদের নিজস্ব বৈশিষ্ট্য , পদ্ধতি (সংগ্রহকারী) দ্বারা যখন AccountBবস্তু অ্যাক্সেস তাদের নিজস্ব বৈশিষ্ট্য ivar দ্বারা। এটাই আসলে একমাত্র তফাত! তারা উভয়ই গেটর দ্বারা তুলনা করার জন্য অন্য বস্তুর বৈশিষ্ট্যগুলিতে অ্যাক্সেস করে (আইভার দ্বারা এটি অ্যাক্সেস করা নিরাপদ হবে না! যদি অন্য অবজেক্টটি সাবক্লাস হয় এবং গিটারটিকে ওভাররাইড করে ফেলে থাকে তবে কী হবে?) এছাড়াও খেয়াল করুন যে আইভার হিসাবে আপনার নিজস্ব সম্পত্তি অ্যাক্সেস করলে এনক্যাপসুলেশনটি ভেঙে যায় না (আইভারগুলি এখনও সর্বজনীন নয়)।

পরীক্ষার সেটআপটি সত্যই সহজ: 1 টি মিয়ো এলোমেলো অ্যাকাউন্ট তৈরি করুন, তাদের একটি অ্যারেতে যুক্ত করুন এবং সেই অ্যারেটি বাছাই করুন। এটাই. অবশ্যই, দুটি অ্যারে রয়েছে, একটি AccountAঅবজেক্টের জন্য এবং একটি AccountBঅবজেক্টের জন্য এবং উভয় অ্যারে অভিন্ন অ্যাকাউন্টগুলি (একই ডেটা উত্স) দিয়ে পূর্ণ থাকে। অ্যারেগুলি বাছাই করতে আমাদের কতক্ষণ সময় লাগে।

গতকাল আমি যে কয়েকটি রান করেছি তার ফলাফল এখানে:

runTime 1: 4.827070, 5.002070, 5.014527, 5.019014, 5.123039
runTime 2: 3.835088, 3.804666, 3.792654, 3.796857, 3.871076

যেমন আপনি দেখতে পাচ্ছেন, AccountBঅবজেক্টের অ্যারে বাছাই করার চেয়ে অবজেক্টের অ্যারে বাছাই করা সর্বদা তাড়াতাড়ি দ্রুত হয়AccountA

যে কেউ দাবি করে যে ১.৩৩ সেকেন্ড পর্যন্ত রানটাইম পার্থক্য কোনও পার্থক্য রাখে না তার চেয়ে ভাল ইউআই প্রোগ্রামিং কখনই করা উচিত নয়। যদি আমি একটি বড় টেবিলের বাছাইয়ের ক্রমটি পরিবর্তন করতে চাই, উদাহরণস্বরূপ, এই সময়ের মতো পার্থক্যগুলি ব্যবহারকারীর জন্য একটি বিশাল পার্থক্য করে (গ্রহণযোগ্য এবং একটি স্লথ UI মধ্যে পার্থক্য)।

এছাড়াও এক্ষেত্রে স্যাম্পল কোডটিই এখানে সম্পাদিত একমাত্র আসল কাজ, তবে আপনার কোডটি কত ঘন ঘন জটিল ক্লকওয়ার্কের কেবলমাত্র একটি ছোট গিয়ার হয়? এবং প্রতিটি গিয়ার যদি পুরো প্রক্রিয়াটিকে এভাবে ধীর করে দেয় তবে শেষেরটি পুরো ঘড়ির কাঁটার গতির জন্য কী বোঝায়? বিশেষত যদি একটি কাজের পদক্ষেপ অন্য একের আউটপুটের উপর নির্ভর করে, যার অর্থ সমস্ত অদক্ষতা যোগ হবে। বেশিরভাগ অদক্ষতা তাদের নিজেরাই সমস্যা নয়, এটি তাদের নিখুঁত যোগফল যা পুরো প্রক্রিয়াতে সমস্যা হয়ে দাঁড়ায়। এবং এই জাতীয় সমস্যা হ'ল কোনও প্রোফাইলার সহজেই দেখাতে পারবেন না কারণ কোনও প্রোফাইলার সমালোচনামূলক গরম দাগগুলি সন্ধান করতে চলেছেন, তবে এই অদক্ষতার কোনওটিই তাদের নিজেরাই গরম দাগ নয়। সিপিইউ সময়টি তাদের মধ্যে গড়ে গড়ে ছড়িয়ে যায়, তবুও তাদের প্রত্যেকেরই এর মধ্যে এর মতো একটি ক্ষুদ্র ভগ্নাংশ থাকে, এটি অনুকূলিতকরণের জন্য মোট সময় অপচয় বলে মনে হয়। এবং এটা সত্য,

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

এখন আপনার main.mফাইলে কোডটি এখানে রয়েছে (কোডটি এআরসি সক্ষম হওয়ার উপর নির্ভর করে এবং পুরো প্রভাবটি দেখার জন্য সংকলন করার সময় অপ্টিমাইজেশন ব্যবহার করতে ভুলবেন না):

#import <Foundation/Foundation.h>

typedef NS_ENUM(int, Gender) {
    GenderMale,
    GenderFemale
};


@interface AccountA : NSObject
    @property (nonatomic) unsigned age;
    @property (nonatomic) Gender gender;
    @property (nonatomic) int64_t balance;
    @property (nonatomic,nonnull,copy) NSString * name;

    - (NSComparisonResult)compare:(nonnull AccountA *const)account;

    - (nonnull instancetype)initWithName:(nonnull NSString *const)name
        age:(const unsigned)age gender:(const Gender)gender
        balance:(const int64_t)balance;
@end


@interface AccountB : NSObject
    @property (nonatomic) unsigned age;
    @property (nonatomic) Gender gender;
    @property (nonatomic) int64_t balance;
    @property (nonatomic,nonnull,copy) NSString * name;

    - (NSComparisonResult)compare:(nonnull AccountB *const)account;

    - (nonnull instancetype)initWithName:(nonnull NSString *const)name
        age:(const unsigned)age gender:(const Gender)gender
        balance:(const int64_t)balance;
@end


static
NSMutableArray * allAcocuntsA;

static
NSMutableArray * allAccountsB;

static
int64_t getRandom ( const uint64_t min, const uint64_t max ) {
    assert(min <= max);
    uint64_t rnd = arc4random(); // arc4random() returns a 32 bit value only
    rnd = (rnd << 32) | arc4random();
    rnd = rnd % ((max + 1) - min); // Trim it to range
    return (rnd + min); // Lift it up to min value
}

static
void createAccounts ( const NSUInteger ammount ) {
    NSArray *const maleNames = @[
        @"Noah", @"Liam", @"Mason", @"Jacob", @"William",
        @"Ethan", @"Michael", @"Alexander", @"James", @"Daniel"
    ];
    NSArray *const femaleNames = @[
        @"Emma", @"Olivia", @"Sophia", @"Isabella", @"Ava",
        @"Mia", @"Emily", @"Abigail", @"Madison", @"Charlotte"
    ];
    const NSUInteger nameCount = maleNames.count;
    assert(maleNames.count == femaleNames.count); // Better be safe than sorry

    allAcocuntsA = [NSMutableArray arrayWithCapacity:ammount];
    allAccountsB = [NSMutableArray arrayWithCapacity:ammount];

    for (uint64_t i = 0; i < ammount; i++) {
        const Gender g = (getRandom(0, 1) == 0 ? GenderMale : GenderFemale);
        const unsigned age = (unsigned)getRandom(18, 120);
        const int64_t balance = (int64_t)getRandom(0, 200000000) - 100000000;

        NSArray *const nameArray = (g == GenderMale ? maleNames : femaleNames);
        const NSUInteger nameIndex = (NSUInteger)getRandom(0, nameCount - 1);
        NSString *const name = nameArray[nameIndex];

        AccountA *const accountA = [[AccountA alloc]
            initWithName:name age:age gender:g balance:balance
        ];
        AccountB *const accountB = [[AccountB alloc]
            initWithName:name age:age gender:g balance:balance
        ];

        [allAcocuntsA addObject:accountA];
        [allAccountsB addObject:accountB];
    }
}


int main(int argc, const char * argv[]) {
    @autoreleasepool {
        @autoreleasepool {
            NSUInteger ammount = 1000000; // 1 Million;
            if (argc > 1) {
                unsigned long long temp = 0;
                if (1 == sscanf(argv[1], "%llu", &temp)) {
                    // NSUIntegerMax may just be UINT32_MAX!
                    ammount = (NSUInteger)MIN(temp, NSUIntegerMax);
                }
            }
            createAccounts(ammount);
        }

        // Sort A and take time
        const CFAbsoluteTime startTime1 = CFAbsoluteTimeGetCurrent();
        @autoreleasepool {
            [allAcocuntsA sortedArrayUsingSelector:@selector(compare:)];
        }
        const CFAbsoluteTime runTime1 = CFAbsoluteTimeGetCurrent() - startTime1;

        // Sort B and take time
        const CFAbsoluteTime startTime2 = CFAbsoluteTimeGetCurrent();
        @autoreleasepool {
            [allAccountsB sortedArrayUsingSelector:@selector(compare:)];
        }
        const CFAbsoluteTime runTime2 = CFAbsoluteTimeGetCurrent() - startTime2;

        NSLog(@"runTime 1: %f", runTime1);
        NSLog(@"runTime 2: %f", runTime2);
    }
    return 0;
}



@implementation AccountA
    - (NSComparisonResult)compare:(nonnull AccountA *const)account {
        // Sort by gender first! Females prior to males.
        if (self.gender != account.gender) {
            if (self.gender == GenderFemale) return NSOrderedAscending;
            return NSOrderedDescending;
        }

        // Otherwise sort by name
        if (![self.name isEqualToString:account.name]) {
            return [self.name compare:account.name];
        }

        // Otherwise sort by age, young to old
        if (self.age != account.age) {
            if (self.age < account.age) return NSOrderedAscending;
            return NSOrderedDescending;
        }

        // Last ressort, sort by balance, low to high
        if (self.balance != account.balance) {
            if (self.balance < account.balance) return NSOrderedAscending;
            return NSOrderedDescending;
        }

        // If we get here, the are really equal!
        return NSOrderedSame;
    }

    - (nonnull instancetype)initWithName:(nonnull NSString *const)name
        age:(const unsigned)age gender:(const Gender)gender
        balance:(const int64_t)balance
    {
        self = [super init];
        assert(self); // We promissed to never return nil!

        _age = age;
        _gender = gender;
        _balance = balance;
        _name = [name copy];

        return self;
    }
@end


@implementation AccountB
    - (NSComparisonResult)compare:(nonnull AccountA *const)account {
        // Sort by gender first! Females prior to males.
        if (_gender != account.gender) {
            if (_gender == GenderFemale) return NSOrderedAscending;
            return NSOrderedDescending;
        }

        // Otherwise sort by name
        if (![_name isEqualToString:account.name]) {
            return [_name compare:account.name];
        }

        // Otherwise sort by age, young to old
        if (_age != account.age) {
            if (_age < account.age) return NSOrderedAscending;
            return NSOrderedDescending;
        }

        // Last ressort, sort by balance, low to high
        if (_balance != account.balance) {
            if (_balance < account.balance) return NSOrderedAscending;
            return NSOrderedDescending;
        }

        // If we get here, the are really equal!
        return NSOrderedSame;
    }

    - (nonnull instancetype)initWithName:(nonnull NSString *const)name
        age:(const unsigned)age gender:(const Gender)gender
        balance:(const int64_t)balance
    {
        self = [super init];
        assert(self); // We promissed to never return nil!

        _age = age;
        _gender = gender;
        _balance = balance;
        _name = [name copy];

        return self;
    }
@end

3
অত্যন্ত তথ্যমূলক এবং নিচে থেকে পৃথিবীর ব্যাখ্যা। কোডের নমুনার জন্য ভোট দিন
ফিলিপ 007

1
আপনার পোস্টে আমি যে কি কিয়ালিফায়ার দেখতে পাচ্ছি তার মধ্যে একটি হ'ল "... সমালোচনামূলক কোড পথগুলি থেকে।" মুল বক্তব্যটি হ'ল কোডটি পড়তে / লিখতে সহজ করে তোলে এবং তারপরে আপনি কী সমালোচনামূলক পথ বলে মনে করেন optim এটি তার প্রয়োজন যেখানে জটিলতা যুক্ত করবে।
স্যান্ডি চ্যাপম্যান

1
@ ভিক্টরলেক্সটিংটন আমার কোডে আমি একটি সেট স্থাপন করছিলাম unsigned intযা কখনই ধরে রাখা / প্রকাশিত হয় না, আপনি আরসি ব্যবহার করেন না কেন। রক্ষণাবেক্ষণ / প্রকাশ নিজেই ব্যয়বহুল, সুতরাং তাত্পর্য কম হবে কারণ রক্ষণাবেক্ষণ ব্যবস্থাপনার একটি স্ট্যাটিক ওভারহেড যুক্ত থাকে যা সর্বদা উপস্থিত থাকে, সেটার / গেটর বা আইভার সরাসরি ব্যবহার করে; তবুও আপনি সরাসরি আইভরে অ্যাক্সেস করলে আপনি একটি অতিরিক্ত পদ্ধতি কলের ওভারহেড সংরক্ষণ করতে পারবেন। বেশিরভাগ ক্ষেত্রে বড় কথা নয়, যদি না আপনি সেকেন্ডে কয়েক হাজার বার এটি করছেন। অ্যাপল বলছে গিটার্স / সেটটারগুলি ডিফল্টরূপে ব্যবহার করুন, যদি না আপনি কোনও ডিআই / ডিএলক পদ্ধতিতে থাকেন বা কোনও বাধাজনিত জায়গা না পেয়ে থাকেন।
মক্কি

1
@ ফগমিস্টার একটি কোড নমুনা যুক্ত করেছেন যা দেখায় যে এটি খুব সহজেই খুব সাধারণ বাস্তব বিশ্বের উদাহরণে বিশাল পার্থক্য আনতে পারে। এবং এই উদাহরণটির একটি সুপার কম্পিউটারের সাথে ট্রিলিয়ন কোটি গণনা করার কোনও সম্পর্ক নেই, এটি সত্যই একটি সাধারণ ডেটা টেবিল বাছাই করা সম্পর্কে আরও বেশি (মিলিয়ন অ্যাপসের মধ্যে একটি সাধারণ সাধারণ ঘটনা))
মেকি

2
@মালহাল হিসাবে চিহ্নিত একটি সম্পত্তি আপনি যখনই এটি অ্যাক্সেস copyকরবেন ততবার এর মানের একটি অনুলিপি তৈরি করবে নাcopyসম্পত্তি প্রাপ্তি একটি সম্পত্তি strong/ retainসম্পত্তি প্রাপ্তির মতো । এটা তোলে এর কোড মূলত return [[self->value retain] autorelease];। কেবলমাত্র সেটারটি মানটি অনুলিপি করে এবং এটি মোটামুটি এইরকম দেখায় [self->value autorelease]; self->value = [newValue copy];, যেখানে একটি strong/ retain[self->value autorelease]; self->value = [newValue retain];
সেটটার এর

9

সর্বাধিক গুরুত্বপূর্ণ কারণটি তথ্য গোপনের ওওপি ধারণা : আপনি যদি সম্পত্তিগুলির মাধ্যমে সমস্ত কিছু প্রকাশ করেন এবং এভাবে বাহ্যিক বস্তুগুলিকে অন্য কোনও বস্তুর অভ্যন্তরে উঁকি দেওয়ার অনুমতি দেয় তবে আপনি এই অভ্যন্তরীণটি ব্যবহার করবেন এবং ফলস্বরূপ বাস্তবায়ন পরিবর্তনকে জটিল করে তুলবেন।

"সর্বনিম্ন পারফরম্যান্স" লাভ দ্রুত যোগ করতে পারে এবং তারপরে একটি সমস্যা হয়ে উঠতে পারে। আমি অভিজ্ঞতা থেকে জানি; আমি এমন একটি অ্যাপ্লিকেশন নিয়ে কাজ করি যা iDevices কে সত্যই তাদের সীমাতে নিয়ে যায় এবং সুতরাং আমাদের অপ্রয়োজনীয় পদ্ধতি কলগুলি এড়াতে হবে (অবশ্যই কেবল যেখানে যুক্তিসঙ্গত সম্ভব)। এই লক্ষ্যে সহায়তা করার জন্য, আমরা বিন্দু বাক্য গঠনটি এড়িয়ে যাচ্ছি কারণ এটি প্রথম দর্শনে পদ্ধতির কলগুলির সংখ্যাটি দেখা শক্ত করে তোলে: উদাহরণস্বরূপ, কয়টি পদ্ধতিতে কল অভিব্যক্তিটি self.image.size.widthট্রিগার করে? বিপরীতে, আপনি সঙ্গে সঙ্গে বলতে পারেন [[self image] size].width

এছাড়াও, সঠিক আইভার নামকরণের সাথে, কেভিও বৈশিষ্ট্য ছাড়াই সম্ভব (আইআইআরসি, আমি কেভিও বিশেষজ্ঞ নই)।


3
+1 "ন্যূনতম পারফরম্যান্স" লাভ সম্পর্কে ভাল প্রতিক্রিয়া যোগ করা এবং সমস্ত পদ্ধতি কলগুলি স্পষ্টভাবে দেখতে চাই। বৈশিষ্ট্যগুলির সাথে ডট বাক্য গঠনটি অবশ্যই কাস্টম গেটার্স / সেটারগুলিতে প্রচুর কাজকে মুখোশ দেয় (বিশেষত যদি সেই গেটর তার ডাকা প্রতিটি সময় কোনও কপি ফেরত দেয়)।
স্যাম

1
কেভিও সেটার ব্যবহার না করে আমার পক্ষে কাজ করে না। আইভার পরিবর্তন করা সরাসরি পর্যবেক্ষককে কল করে না যে মানটির পরিবর্তন হয়েছে!
বাইনারি

2
কেভিসি আইভারগুলি অ্যাক্সেস করতে পারে। কেভিও আইভারগুলিতে পরিবর্তনগুলি সনাক্ত করতে পারে না (এবং পরিবর্তে ডেকে পাঠানো অ্যাক্সেসরগুলির উপর নির্ভর করে)।
নিকোলাই রুহে

9

শব্দার্থবিদ্যা

  • @propertyআইভারগুলি কী করতে পারে তা প্রকাশ করতে পারে: nonatomicএবং copy
  • আইভারা কী প্রকাশ করতে পারে যা তা @propertyকরতে পারে না:

কর্মক্ষমতা

ছোট গল্প: আইভারগুলি দ্রুত, তবে বেশিরভাগ ব্যবহারে এটি গুরুত্বপূর্ণ নয় matter nonatomicবৈশিষ্ট্যগুলি লকগুলি ব্যবহার করে না, তবে সরাসরি আইভার দ্রুত হয় কারণ এটি অ্যাক্সেসরদের কল এড়িয়ে যায়। বিশদের জন্য তালিকা.এপ্লে.কম থেকে নিম্নলিখিত ইমেলটি পড়ুন।

Subject: Re: when do you use properties vs. ivars?
From: John McCall <email@hidden>
Date: Sun, 17 Mar 2013 15:10:46 -0700

বৈশিষ্ট্যগুলি বিভিন্নভাবে কার্য সম্পাদনকে প্রভাবিত করে:

  1. ইতিমধ্যে আলোচিত হিসাবে, লোড / স্টোর করতে কোনও বার্তা প্রেরণ কেবল লোড / স্টোরের ইনলাইন না করে ধীর

  2. লোড / স্টোর করার জন্য একটি বার্তা প্রেরণ করাও বেশ খানিকটা কোড যা আই-ক্যাশে রাখা দরকার: এমনকি যদি গেটার / সেটার কেবল লোড / স্টোরের বাইরে শূন্য অতিরিক্ত নির্দেশ যুক্ত করে, তবে একটি শক্ত অর্ধেক থাকত - বার্তা প্রেরণ এবং ফলাফল পরিচালনা করতে সেট করতে কলারে অতিরিক্ত নির্দেশাবলী প্রেরণ করুন।

  3. বার্তা প্রেরণে সেই নির্বাচককে পদ্ধতি ক্যাশে রাখতে বাধ্য করা হয় এবং সেই স্মৃতিটি সাধারণত ডি-ক্যাশে আটকে থাকে। এটি প্রবর্তনের সময় বাড়ায়, আপনার অ্যাপের স্থিতিশীল মেমরির ব্যবহার বাড়িয়ে তোলে এবং প্রসঙ্গে সুইচগুলি আরও বেদনাদায়ক করে তোলে। যেহেতু কোনও পদ্ধতিটির জন্য পদ্ধতি ক্যাশে গতিশীল শ্রেণীর জন্য সুনির্দিষ্ট, এই সমস্যাটি আপনি এতে কেভিও যত বেশি ব্যবহার করেন তা বাড়িয়ে তোলে।

  4. বার্তা প্রেরণ ফাংশনটির সমস্ত মানকে স্ট্যাকের কাছে ছড়িয়ে দিতে বাধ্য করে (বা কলি-সেভ রেজিস্টারগুলিতে রাখা হয়, যার অর্থ একটি ভিন্ন সময়ে স্পিলিং)।

  5. পাঠানো হচ্ছে একটি বার্তা নির্বিচারে পার্শ্ব প্রতিক্রিয়া হতে পারে এবং এর ফলে

    • সংকলকটিকে অ-স্থানীয় মেমরির সম্পর্কে তার সমস্ত অনুমানগুলি পুনরায় সেট করতে বাধ্য করে
    • উত্তোলন, ডুবানো, পুনরায় অর্ডার করা, একত্রিত করা বা মুছে ফেলা যায় না।

  6. এআরসি-তে, বার্তা প্রেরণের ফলাফলটি কলি বা কলার দ্বারা সর্বদা বজায় থাকবে , এমনকি +০ রিটার্নের জন্যও: পদ্ধতিটি যদি তার ফলাফলটি ধরে রাখে / স্বতঃসংশ্লিষ্ট না করে, কলার তা জানে না এবং আছে ফলাফলটি স্বতঃস্ফুর্ত হওয়া থেকে বিরত রাখতে পদক্ষেপ নেওয়ার চেষ্টা করা। এটি কখনই মুছে ফেলা যায় না কারণ বার্তা প্রেরণগুলি স্ট্যাটিকালি বিশ্লেষণযোগ্য নয়।

  7. এআরসি-তে, যেহেতু একটি সেস্টার পদ্ধতিটি সাধারণত তার যুক্তিটি +0 এ নেয়, তাই object বস্তুর (যা উপরে আলোচনা করা হয়েছে, এআরসি সাধারণত রয়েছে) আইভরের মধ্যে ধরে রাখার "স্থানান্তর" করার কোনও উপায় নেই, সুতরাং সাধারণত মানটি পেতে হয় ধরে রাখা / দুবার মুক্তি দেওয়া

এর কোনওটির অর্থ এই নয় যে এগুলি সবসময়ই খারাপ, অবশ্যই - সম্পত্তি ব্যবহারের জন্য অনেকগুলি ভাল কারণ রয়েছে। কেবল মনে রাখবেন যে অন্যান্য অনেক ভাষা বৈশিষ্ট্যের মতো এগুলিও নিখরচায় নয়।


জন।


6

বৈশিষ্ট্য বনাম, উদাহরণের ভেরিয়েবলগুলি একটি বাণিজ্য-বন্ধ, শেষ পর্যন্ত পছন্দটি অ্যাপ্লিকেশনটিতে নেমে আসে।

এনক্যাপসুলেশন / তথ্য গোপন করা এটি একটি ডিজাইনের দৃষ্টিকোণ, সংকীর্ণ ইন্টারফেস এবং ন্যূনতম সংযোগ থেকে একটি ভাল জিনিস (টিএম) যা সফ্টওয়্যারকে বজায় রাখতে সক্ষম এবং বোধগম্য করে তোলে। ওবজ-সি-তে কোনও কিছু আড়াল করা বেশ শক্ত, তবে বাস্তবায়নের মধ্যে ঘোষিত উদাহরণ পরিবর্তনগুলি আপনার কাছে যেমন আসবে তত কাছাকাছি এসে গেছে।

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

বৈশিষ্ট্যসম্পন্ন স্ট্যাটিক ভাষায়, যেমন সি #, সেটটার / গেটরসকে কলগুলি প্রায়শই সংকলক দ্বারা অপ্টিমাইজ করা যায়। তবে ওবজে-সি গতিশীল এবং এই জাতীয় কলগুলি অপসারণ করা আরও শক্ত।

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


5

বৈশিষ্ট্যগুলি আপনার ভেরিয়েবলগুলি অন্য শ্রেণিতে প্রকাশ করে। আপনার যদি কেবল এমন একটি ভেরিয়েবলের প্রয়োজন হয় যা আপনার তৈরি করা বর্গের সাথে সম্পর্কিত হয় তবে একটি উদাহরণ ভেরিয়েবল ব্যবহার করুন। এখানে একটি ছোট উদাহরণ রয়েছে: আরএসএসকে পার্স করার জন্য এক্সএমএল ক্লাস এবং এই জাতীয় চক্রটি একাধিক প্রতিনিধি পদ্ধতি এবং এর মাধ্যমে। পার্সের প্রতিটি পৃথক পাসের ফলাফল সংরক্ষণের জন্য এনএসউটেবল স্ট্রিংয়ের একটি উদাহরণ থাকা ব্যবহারিক। বাইরের কোনও শ্রেণীর যে স্ট্রিংটি অ্যাক্সেস করতে বা পরিচালনা করতে হবে তার কোনও কারণ নেই। সুতরাং, আপনি কেবল এটি হেডারে বা ব্যক্তিগতভাবে ঘোষণা করেছেন এবং এটি ক্লাস জুড়ে অ্যাক্সেস করুন। এর জন্য একটি সম্পত্তি সেট করা কেবল মেমরির সমস্যা না রয়েছে তা নিশ্চিত করার জন্য দরকারী হতে পারে, গেটর / সেটারগুলিকে অনুরোধ করার জন্য সেলফ-মিটেবল স্ট্রিং ব্যবহার করে।


5

পিছনে সামঞ্জস্যতা আমার জন্য একটি ফ্যাক্টর ছিল। আমি কোনও ওজেক্টিভ-সি 2.0 বৈশিষ্ট্য ব্যবহার করতে পারিনি কারণ আমি একটি সফ্টওয়্যার এবং প্রিন্টার ড্রাইভার বিকাশ করছিলাম যা প্রয়োজনের অংশ হিসাবে ম্যাক ওএস এক্স 10.3 এ কাজ করতে হয়েছিল। আমি জানি আপনার প্রশ্নটি আইওএসের চারপাশে লক্ষ্যবস্তু বলে মনে হয়েছিল, তবে আমি ভেবেছিলাম সম্পত্তিটি ব্যবহার না করার জন্য আমি এখনও আমার কারণগুলি ভাগ করব।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.