Xcode script for generating/synthesizing properties

前端 未结 8 1857
南旧
南旧 2020-12-16 03:03

Does anybody have an Xcode script for generating @property and @synthsize directives for instance variables in a class?

相关标签:
8条回答
  • 2020-12-16 04:04

    This is the one I came up with based on one I found a long time ago, rewritten in Python and with the improvements that it can generate multiple properties at once, among other things.

    It will generate properties for all selected instance variable using (copy) as the attribute.

    There are still some edge cases with multiple @interfaces or @implementations in a file, as well as some with unusual identifiers or asterisk placement (as in *const), but it should cover most typical coding styles. Feel free to edit/post modifications if you fix any of these cases.

    #!/usr/bin/python
    
    # Takes a header file with one or more instance variables selected
    # and creates properties and synthesize directives for the selected properties.
    
    # Accepts google-style instance variables with a tailing underscore and
    # creates an appropriately named property without underscore.
    
    # Entire Document
    # Home Directory
    # Discard Output
    # Display in Alert
    
    import os
    import re
    import subprocess
    
    # AppleScripts for altering contents of files via Xcode
    setFileContentsScript = """\
    on run argv
      set fileAlias to POSIX file (item 1 of argv)
      set newDocText to (item 2 of argv)
        tell application "Xcode"
          set doc to open fileAlias
          set text of doc to newDocText
        end tell
    end run \
    """
    
    getFileContentsScript = """\
    on run argv
      set fileAlias to POSIX file (item 1 of argv)
        tell application "Xcode"
          set doc to open fileAlias
          set docText to text of doc
      end tell
      return docText
    end run \
    """
    
    # Get variables from Xcode
    headerFileText = """%%%{PBXAllText}%%%"""
    selectionStartIndex = %%%{PBXSelectionStart}%%%
    selectionEndIndex = %%%{PBXSelectionEnd}%%%
    selectedText = headerFileText[selectionStartIndex:selectionEndIndex]
    
    headerFilePath = """%%%{PBXFilePath}%%%"""
    
    # Look for an implementation file with .m or .mm extension
    implementationFilePath = headerFilePath[:-1] + "m"
    if not os.path.exists(implementationFilePath):
        implementationFilePath += "m"
    
    instanceVariablesRegex = re.compile(
      """^\s*((?:(?:\w+)\s+)*(?:(?:\w+)))""" + # Identifier(s)
      """([*]?)\\s*""" + # An optional asterisk
      """(\\w+?)(_?);""", # The variable name
      re.M)
    
    # Now for each instance variable in the selected section
    properties = ""
    synthesizes = ""
    
    for lineMatch in instanceVariablesRegex.findall(selectedText):
        types = " ".join(lineMatch[0].split()) # Clean up consequtive whitespace
        asterisk = lineMatch[1]
        variableName = lineMatch[2]
        trailingUnderscore = lineMatch[3]
    
        pointerPropertyAttributes = "(copy) " # Attributes if variable is pointer
        if not asterisk:
          pointerPropertyAttributes = ""
    
        newProperty = "@property %s%s %s%s;\n" % (pointerPropertyAttributes,
                                                 types,
                                                 asterisk,
                                                 variableName)
    
        # If there's a trailing underscore, we need to let the synthesize
        # know which backing variable it's using
        newSynthesize = "@synthesize %s%s;\n" % (variableName,
                                               trailingUnderscore and
                                               " = %s_" % variableName)
    
        properties += newProperty
        synthesizes += newSynthesize
    
    # Check to make sure at least 1 properties was found to generate
    if not properties:
      os.sys.stderr.writelines("No properties found to generate")
      exit(-1)
    
    # We want to insert the new properties either immediately after the last
    # existing property or at the end of the instance variable section
    findLastPropertyRegex = re.compile("^@interface.*?{.*?}.*?\\n" +
                                       "(?:.*^\\s*@property.*?\\n)?", re.M | re.S)
    headerInsertIndex = findLastPropertyRegex.search(headerFileText).end()
    
    # Add new lines on either side if this is the only property in the file
    addedNewLine = "\n"
    if re.search("^\s*@property", headerFileText, re.M):
      # Not the only property, don't add
      addedNewLine = ""
    
    newHeaderFileText = "%s%s%s%s" % (headerFileText[:headerInsertIndex],
                                    addedNewLine,
                                    properties,
                                    headerFileText[headerInsertIndex:])
    
    subprocess.call(["osascript",
                    "-e",
                    setFileContentsScript,
                    headerFilePath,
                    newHeaderFileText])
    
    
    if not os.path.exists(implementationFilePath):
      os.sys.stdout.writelines("No implementation file found")
      exit(0)
    
    implementationFileText = subprocess.Popen(
      ["osascript",
       "-e",
      getFileContentsScript,
       implementationFilePath],
      stdout=subprocess.PIPE).communicate()[0]
    
    # We want to insert the synthesizes either immediately after the last existing
    # @synthesize or after the @implementation directive
    lastSynthesizeRegex = re.compile("^\\s*@implementation.*?\\n" +
                                    "(?:.*^\\s*@synthesize.*?\\n)?", re.M | re.S)
    
    implementationInsertIndex = \
      lastSynthesizeRegex.search(implementationFileText).end()
    
    # Add new lines on either side if this is the only synthesize in the file
    addedNewLine = "\n"
    if re.search("^\s*@synthesize", implementationFileText, re.M):
      # Not the only synthesize, don't add
      addedNewLine = ""
    
    newImplementationFileText = "%s%s%s%s" % \
                      (implementationFileText[:implementationInsertIndex],
                       addedNewLine,
                       synthesizes,
                       implementationFileText[implementationInsertIndex:])
    
    subprocess.call(["osascript",
                     "-e",
                     setFileContentsScript,
                     implementationFilePath,
                     newImplementationFileText])
    
    # Switch Xcode back to header file
    subprocess.Popen(["osascript",
                      "-e",
                      getFileContentsScript,
                      headerFilePath],
                     stdout=subprocess.PIPE).communicate()
    
    0 讨论(0)
  • 2020-12-16 04:07

    Whoa, there's a whole lot of crazy scripting goin' on here.

    As of Xcode 4.4 (maybe before)... Your IVARs will be auto-synthesized.. For example..

    @property (assign) BOOL automatically;
    @property (strong) NSArray *believeDat;
    

    can be "accessored" via

    self.automatically = YES;
    

    and edit the instance variable directly via the auto-generated-with-leading-underscore like..

    _believeDat = @["thank you, jesus", @"mary poopins"];
    

    no @synthesize necessary.

    As for quick and easy entering of such @property... drag the following, one at a time, into the "Code Snippet" library.. and you can assign keyboard shortcuts to insert these jump-off points for entering the properties more quickly. I use rrr for objects and aaa for primitives.. but thats just me..

    @property (nonatomic, assign) <#type#> <#name#>;

    @property (nonatomic, retain) <#type#> *<#name#>;

    last but not least, and some may call me crazy.. but I throw the following macros into my .pch to further expedite, clarify, and bring welcome brevity to the process.. all common macro disclaimers apply...

    #define RONLY readonly
    #define RDWRT readwrite
    #define NATOM nonatomic
    #define STRNG strong
    #define ASS assign
    #define CP copy
    #define SET setter
    #define GET getter
    

    along with similarly structured #defines for Apple classes ( #define NSA NSArray \ #define NSS NSString), this makes things easier to read, and faster to enter (for me), looking like...

    @property (NATOM, STRNG) NSA* fonts;
    @property (NATOM, STRNG) NSS* cachedPath;
    
    0 讨论(0)
提交回复
热议问题