وقتی یه پروژه نوشته شده با سوییفت ۳ رو با ایکسکد نسخه ۹ باز میکنین، ایکسکد بهتون پیام میده که میتونین پروژه رو به نسخه ۴ سوییفت ارتقاء بدین. اگه این ارتقاء رو بپذیرین، ایکسکد یه پیام دیگه بهتون میده که «قوانین بررسی و استفاده متدها و مشخصههای @objc توی نسخه ۳ سوییفت، توی نسخه ۴ منقضی شدن». حالا این اخطارها چی هستن و چجوری رفع میشن؟!
نگران نباشین، من اینجام! ?
صادقانه بگم، تا قبل اینکه سوییفت ۴ قوانین مورد استفاده رو تغییر بده، این @objc چیزی نبود که بخوام زیاد روش وقت بذارم یا بهش توجه کنم. شما میتونین @objc رو به مشخصهها و متدهای سوییفت اضافه کنین، تا اونا بتونن توسط کدهای Objective-C قابل دسترس باشن. خود مترجم (Compiler) هم یه سری قوانین برای این کار داره و تا جایی که بتونه کمک شما میکنه.
مترجم سوییفت ۳ در مورد اضافه کردن @objc یکم زیادی ولخرجی میکنه! و حتی اون رو زمانی هم که بهش نیازی نیست، اضافه میکنه. این رویه باعث افزایش حجم خروجی برنامهها میشه، چون همه اون مشخصهها و متدهایی که بهشون @objc اضافه میشه، کدهایی دیگهای هم خواهند داشت تا بتونن توسط Objective-C مورد استفاده قرار بگیرن.
مترجم سوییفت ۴، یکم محافظهکار شده! و @objc رو توی موارد خاصی اضافه میکنه. بعنوان مثال، زمانیکه شما دارین متدهای دارای @objc رو اصطلاحا override میکنین، و یا یه پروتکل دارای @objc رو پیادهسازی میکنین. برای همین، نیازی نیست تمام متدهایی که برای UITableViewDataSource هست رو، وقتی استفاده میکنین، با @objc بنویسین. همچنین موقع استفاده از @IBOutlet، @IBAction و یا @IBInspectable هم نیازی به نوشتن @objc نیست.
این قوانین، یکم کار رو برای ارتقاء به سوییفت ۴ راحتتر میکنه، ولی آخرش یه سری کارا رو خودتون باید دستی انجام بدین.
وقتی یه پروژه نوشته شده با سوییفت ۳ رو با ایکسکد ۹ باز میکنین، بهتون یه پیام میده با این مظمون که «تبدیل به نسخه ۴ شدنیه!!» ?.
این که اصطلاحا Build Warning داشته باشیم، یکم رو اعصابه!! ولی شما مجبور به ارتقاء پروژه به نسخه ۴ سوییفت نیستین. ایکسکد ۹، در کنار نسخه ۴، از نسخه ۳.۲ هم پشتیبانی میکنه؛ و این کار از طریق تنظیمات پروژه انجام میشه.
اگه شما بخواین پروژه رو ارتقاء بدین، روی این اخطاری که ایکسکد بهتون داده کلیک میکنین، و ایکسکد ابزار ارتقاء پروژه رو بهتون نشون میده، و شما اول اون Target که میخواین ارتقاء بدین رو انتخاب میکنین، و بعدش دو تا گزینه جلوتون هست:
بازم میگم، اگه گزینه پیشنهادی رو انتخاب کنین، یه سری کار دیگه هم برای تکمیلش باید انجام بدین. این قدمهای بعدی، توسط خود ایکسکد، بصورت یه لینک (که فقط هم یبار نشونش میده!!) در دسترس شما قرار میگیره. البته میتونین از طریق قسمت Help خود ایکسکد، توی بخش Work In Xcode بهش دسترسی داشته باشین.
? نمیخواد دنبالش بگردین؛ لینکش اینه !!
بعد از قدمهای اولیه، ایکسکد که کاراش تموم شد، یه همچین پیامی به شما نشون میده:
اگه از قسمت تنظیمات پروژه، مشخصه مربوط به استنتاج @objc رو بررسی کنین، میبینین که این رویه هنوزم قوانین مربوط به سوییفت ۳ داره استفاده میشه:
این گزینه برای این خوبه، چون هر زمان که کدی مورد استفاده قرار بگیره که یه @objc جا انداخته، اخطارهایی در زمان Build و زمان اجرا بهتون نشون داده میشه، و شما میتونین برای رفع این اخطارها اقدام کنین.
زمانیکه شما میخواین یه پروژه تماما سوییفت رو ارتقاء بدین، معمولا ابزار بروزرسانی کدها که توسط خود ایکسکد مورد استفاده قرار میگیره، تا حد زیادی خوب عمل میکنه، و خودش هرجا نیاز ببینه، @objc رو اضافه میکنه. مثلا وقتی شما از #selector استفاده میکنین، خودش این @objc رو به اول تعریف متد مورد استفاده اضافه میکنه.
button.addTarget(self, action: #selector(doAction(sender:)), for: .touchUpInside)
از اونجایی که متد مورد نظر شما باید از طریق UIKit صدا زده بشه، در نتیجه باید توسط کدهای Objective-C قابلیت دسترسی داشته باشه. برای همین، ابزار بروزرسانی کد ایکسکد، میاد و به اول تعریف متد، @objc رو اضافه میکنه:
@objc func doAction(sender: UIButton) { // Do action here }
اگه شما روی یه پروژه ترکیبی Objective-C و Swift کار میکنین، یه سری کارا رو خودتون باید دستی انجام بدین. مثلا فرض کنین یه همچین کلاسی تعریف کردیم:
public class MyModel: NSObject { var someFlag = false func doSomething() { print("doing something") } }
بطور پیشفرض توی نسخه ۳ سوییفت، مشخصهها و متدها یه کلاس که زیر کلاس NSObject باشه، از طریق Objective-C قابلیت دسترسی داره (مگه اینکه بصورت private تعریف شده باشه).
self.model.someFlag = YES; [self.model doSomething];
توی نسخه ۴، این رویه دیگه برقرار نیست، و شما با خطاها و اخطارهای زیر روبرو میشین:
برای رفع این خطاها، میتونیم @objc رو به مشخصهها و متدها اضافه کنیم:
public class MyModel: NSObject { @objc var someFlag = false @objc func doSomething() { print("doing something") } }
اگه شما مطمئن باشین که میخواین کل مشخصهها و متدهای یه کلاس رو قابل دسترس برای Objective-C بکنین، میتونین از یه معرف دیگه به اسم @objcMembers استفاده کنین:
@objcMembers public class MyModel: NSObject { var someFlag = false // @objc func doSomething() { // @objc print("doing something") } }
استفاده از @objcMembers، بطور پیشفرض، دسترسی به متدهای یه کلاس رو، به متدهای تعریف شده داخل extension های کلاس هم اعمال میکنه. در نتیجه کد بالا، با کد پیش رو، معادل میشه:
public class MyModel: NSObject { @objc var someFlag = false } @objc extension MyModel { func doSomething() { print("doing something") } }
? توجه داشته باشین،چون extensionها نمیتونن دارای اصطلاحا Stored Propertieها باشن، نمیتونیم اون someFlag رو به extension منتقل کنیم.
همچنین برای جلوگیری از رفتار پیشفرض @objc (برای مثال روی Extensionها)، میتونیم از @nonobjc استفاده کنیم:
@objc extension MyModel { func doSomething() { print("doing something") } @nonobjc func doNothing() { // Not accessibile from objc // ... } }
زمانیکه مطمئن شدین همه خطاها و اخطارها رو رفع کردین، و پروژه دیگه مشکل خاصی با نسخه ۴ سوییفت نداره، باید از طریق تنظیمات مربوط به پروژه، برای همه Targetها، گزینه مربوط به استنتاج @objc رو تغییر بدین:
با تغییر این گزینه به Default، آخرین قدم رو برداشتین، و میتونین روند پیشبرد پروژه رو ادامه بدین.
? برای اطلاعات بیشتر، میتونین فیلم مربوط به نشست WWDC که درباره محدودسازی استنتاج @objc هست رو در این آدرس ببینین،
? و یا از مستندات خود اپل توی این آدرس استفاده کنین.
? منبع مطلب: این مطلب رو بر اساس این پست نوشتم.